minc-tools-2.3.00+dfsg/0002755000175000000620000000000012574624760013624 5ustar stevestaffminc-tools-2.3.00+dfsg/CMakeLists.txt0000644000175000000620000000660312574624760016367 0ustar stevestaff# CMakeFiles.txt for the minc-tools # # Andrew Janke - a.janke@gmail.com # Vladimir S. FONOV - vladimir.fonov@gmail.com PROJECT(minc-tools) SET(MINC_TOOLS_PACKAGE_VERSION_MAJOR 2) SET(MINC_TOOLS_PACKAGE_VERSION_MINOR 3) SET(MINC_TOOLS_PACKAGE_VERSION_PATCH 00) SET(PACKAGE "minc-tools") SET(PACKAGE_BUGREPORT "a.janke@gmail.com") SET(PACKAGE_NAME "minc-tools") SET(PACKAGE_VERSION "${MINC_TOOLS_PACKAGE_VERSION_MAJOR}.${MINC_TOOLS_PACKAGE_VERSION_MINOR}.${MINC_TOOLS_PACKAGE_VERSION_PATCH}") SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") ENABLE_TESTING() INCLUDE(CTest) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) IF(COMMAND CMAKE_POLICY) IF(POLICY CMP0026) CMAKE_POLICY(SET CMP0026 OLD) # NEEDED in testing to read location of target ENDIF(POLICY CMP0026) ENDIF(COMMAND CMAKE_POLICY) IF(MINC_TOOLKIT_BUILD) SET(MINC_TOOLS_EXTERNALLY_CONFIGURED ON) ENDIF(MINC_TOOLKIT_BUILD) IF(NOT MINC_TOOLS_EXTERNALLY_CONFIGURED) SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules") SET(CPACK_GENERATOR TGZ) SET(CPACK_PACKAGE_VERSION_MAJOR ${MINC_TOOLS_PACKAGE_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${MINC_TOOLS_PACKAGE_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${MINC_TOOLS_PACKAGE_VERSION_PATCH}) INCLUDE(CPack) FIND_PACKAGE(LIBMINC REQUIRED) ENDIF(NOT MINC_TOOLS_EXTERNALLY_CONFIGURED) INCLUDE( ${LIBMINC_USE_FILE} ) INCLUDE( InstallManPages ) # 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) 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(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) # aliases SET(VERSION "${PACKAGE_VERSION}") # netcdf and HDF5 # config files for build CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) # set the master INCLUDE directories INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) ADD_SUBDIRECTORY( progs ) ADD_SUBDIRECTORY( conversion ) # testing IF(BUILD_TESTING) ADD_SUBDIRECTORY( Testing ) ENDIF(BUILD_TESTING) minc-tools-2.3.00+dfsg/cmake-modules/0002755000175000000620000000000012574624760016352 5ustar stevestaffminc-tools-2.3.00+dfsg/cmake-modules/InstallManPages.cmake0000644000175000000620000000077112574624760022401 0ustar stevestaff# install a list of files with names like XXXX.manY into install_prefi/manY/XXXX.Y macro(INSTALL_MAN_PAGES install_prefix ) set(argn "${ARGN}") foreach(i IN LISTS argn) get_filename_component(fname ${i} NAME_WE) get_filename_component(fext ${i} EXT) STRING(REGEX REPLACE ".man" "" SECTION ${fext}) #TODO: add gzip compression INSTALL(FILES ${i} DESTINATION ${install_prefix}/man${SECTION}/ RENAME ${fname}.${SECTION}) endforeach() endmacro(INSTALL_MAN_PAGES ) minc-tools-2.3.00+dfsg/cmake-modules/BuildHDF5.cmake0000644000175000000620000000124412574624760021021 0ustar stevestaffmacro(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)minc-tools-2.3.00+dfsg/cmake-modules/FindHDF5.cmake0000644000175000000620000000436512574624760020651 0ustar stevestaff# # 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 ) minc-tools-2.3.00+dfsg/cmake-modules/FindNETCDF.cmake0000644000175000000620000000127712574624760021125 0ustar stevestaff# 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) minc-tools-2.3.00+dfsg/cmake-modules/BuildNETCDF.cmake0000644000175000000620000000122412574624760021274 0ustar stevestaff 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)minc-tools-2.3.00+dfsg/config.h.cmake0000644000175000000620000000266512574624760016330 0ustar stevestaff/* various defines */ #define MINC2 @MINC2@ #define PACKAGE_NAME "@PACKAGE_NAME@" #define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" #define PACKAGE_VERSION "@PACKAGE_VERSION@" #define PACKAGE_STRING "@PACKAGE_STRING@" #define VERSION PACKAGE_VERSION #define H5Acreate_vers 2 #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 #define H5Acreate_vers 2 minc-tools-2.3.00+dfsg/.gitignore0000644000175000000620000000073312574624760015615 0ustar stevestaff*~ INSTALL *.in *.o *.lo *.la Makefile .deps .dirstamp .libs aclocal.m4 autom4te.cache/ configure config.h config.log config.status libtool progs/minccomplete/minccomplete progs/minccomplete/minccomplete.man1 progs/mincgen/ncgentab.c progs/mincgen/ncgentab.h progs/mincgen/ncgenyy.c progs/minchistory/minchistory progs/minchistory/minchistory.man1 progs/xfm/xfmflip stamp-h1 volume_io/Testing/example_modify volume_io/Testing/example_tags volume_io/Testing/example_volume_io minc-tools-2.3.00+dfsg/ChangeLog0000644000175000000620000011751012574624760015401 0ustar stevestaff2015-08-06 Robert D. Vincent * progs/mincstats/mincstats.c: Incorporate Claude's suggested changes to the otsu_threshold() function. * {various}: Corrected many warnings. 2015-08-06 Andrew Janke * progs/mincstats/mincstats.man1: Add documentation of new command line options. 2015-08-05 Robert D. Vincent * progs/mincstats/mincstats.c: Add -skewness and -kurtosis options. 2015-07-09 Robert D. Vincent * Testing/{mincstats-test.pl, mincinfo-test.pl}: Added test scripts for mincstats and mincinfo. 2015-06-26 Robert D. Vincent * progs/mincstats/mincstats.c: Fix potential memory error and removed some warnings. * conversion/dcm2mnc/*: Many changes to improve robustness of DICOM conversion. * conversion/nifti1/mnc2nii.c: Use new restructure_array() and restructure.h. * progs/mincmorph/*: Fix a number of memory issues and odd use of pointers. * progs/mincconvert/mincconvert.c: Allow conversion from MINC2 to MINC2. * mincextract.c, mincreshape.c, rawtominc.c: Use new ParseLong() function rather than strtol() to avoid interpreting numbers with leading zeros in octal. * Testing/: added tests for mincreshape, mincresample, minccalc, and mincaverage. * conversion/nifti1/: Moved nifti1_io.c, znzlib.c and headers to libminc. * progs/mincresample/mincresample.c: Warn before doing incorrect things with non-monotonic irregular dimensions. * progs/minccmp/minccmp.c: Add support for confusion matrix statistics (e.g. Dice statistics) with discrete volumes. * progs/mincdump/mincdump.c: Expand legal character set in text, fix usage message. * progs/mincresample/resample_volumes.c: Fix issue with -keep_real_range (see Github issue #21). * conversion/nifti1/mnc2nii.c: Fix a number of bugs. * progs/mincextract/mincextract.c, progs/minctoraw/minctoraw.c: Provide for creation of either big-endian or little-endian data. * progs/minccalc/{minccalc.c, sym.c}: Fix minor memory leak, incorrect modification of symbol values in ranges (see Github issue #18). * progs/minccmp/minccmp.c: Print results for all volumes, not just the first (see Github issue #9). * progs/mincaverage/mincaverage.c: Fix incorrect normalized average when mean is less than zero (bug reported by Alex Z.). -- 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. minc-tools-2.3.00+dfsg/progs/0002755000175000000620000000000012574624760014756 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmakescalar/0002755000175000000620000000000012574624760017730 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmakescalar/mincmakescalar.man10000644000175000000620000001164312574624760023463 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1997 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincmakescalar/mincmakescalar.man1,v 6.2 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCMAKESCALAR 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincmakescalar - convert vector minc files to scalar .SH SYNOPSIS .B mincmakescalar [] .mnc .mnc .SH DESCRIPTION .I Mincmakescalar converts vector minc files to scalar minc files. A vector minc file is one that contains the dimension \fIvector_dimension\fR as the fastest varying dimension of the image data and represents vector data at each voxel such as RGB images or gradient volumes. A scalar minc file does not contain the vector dimension and represents grayscale or intensity data. A variety of conversion schemes are possible. The simplest is an average of the components of the vector. The magnitude of the vector at each voxel can also be computed. RGB volumes can be converted to greyscale by a standard linear combination. Finally, the user can supply a list of coefficients for a linear combination of vector components. Some options will require a specific number of components on the input vectors (RGB data should have 3 components and the number of coefficients supplied for the linear combination should match the number of components on the input vectors), but the program will also accept input scalar data and will copy it through without modification. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-no_clobber\fR Synonym for \fB\-noclobber\fR. .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB-buffer_size\fR\ \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 10 MB. .TP \fB\-filetype\fR Create an output file with the same type as the first input file (default). .TP \fB\-byte\fR Store output voxels as 8-bit integers. .TP \fB\-short\fR Store output voxels as 16-bit integers. .TP \fB\-int\fR Store output voxels as 32-bit integers .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB\-float\fR Store output voxels as 32-bit floating point numbers. .TP \fB\-double\fR Store output voxels as 64-bit floating point numbers. .TP \fB\-signed\fR Create an output file with data stored in a signed type. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .TP \fB\-unsigned\fR Create an output file with data stored in an unsigned type. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .TP \fB\-valid_range\fR\ \fImin\ max\fR Create an output file with integer data stored in the specified restricted range. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .SH Conversion options .TP \fB\-magnitude\fR Compute the magnitude of each vector (default). .TP \fB\-average\fR Compute the average of the components of each vector. .TP \fB\-rgbtogrey\fR Convert RGB (3-component) files to greyscale using a linear combination with standard set of weighting coefficients. .TP \fB\-rgbtogray\fR Synonym for \fB\-rgbtogrey\fR. .TP \fB\-linear\fR\ \fIc1,c2,c3,...\fR Compute a linear combination of the components of each vector using the specified coefficients (these weights must be given as a comma or space-separated list of numbers in a single command-line argument). The number of coefficients must match the number of components on the input vectors. .SH Generic options for all commands: .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES To convert an RGB file to an grayscale file: mincmakescalar -rgbtogrey rgb.mnc gray.mnc To compute the gradient magnitude from a gradient volume mincmakescalar -magnitude gradient.mnc magnitude.mnc To convert an RGB file to a grayscale file using a different set of weighting factors for red, green and blue: mincmakescalar -linear '0.2,0.5,0.3' rgb.mnc gray.mnc .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1997 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincmakescalar/mincmakescalar.c0000644000175000000620000004303612574624760023052 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincmakescalar @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to make a scalar minc file from a vector minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1997 (Peter Neelin) @MODIFIED : * $Log: mincmakescalar.c,v $ * Revision 6.10 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.9 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.8 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.7 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.6 2005/01/28 19:34:09 bert * Warn user if vector_dimension is not the last dimension in the image * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2004/04/27 15:32:33 bert * Added -2 option * * Revision 6.3 2001/04/24 13:38:43 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.2 2001/04/17 18:40:20 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:25 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:20 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:19 neelin * Release of minc version 0.5 * * Revision 1.2 1997/08/07 16:23:19 neelin * Change -length to -magnitude. * * Revision 1.1 1997/08/07 16:09:07 neelin * Initial revision * ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define DEFAULT_RANGE DBL_MAX #define NCOPTS_DEFAULT NC_VERBOSE | NC_FATAL #define INVALID_DATA (-DBL_MAX) /* Types */ typedef enum {CONV_DEFAULT, CONV_AVERAGE, CONV_MAGNITUDE, CONV_GREY, CONV_LINEAR} Conversion_Type; /* Double_Array structure */ typedef struct { int numvalues; double *values; } Double_Array; /* Structure for program info */ typedef struct { Conversion_Type conversion_type; int num_coefficients; double *linear_coefficients; } Program_Data; /* Function prototypes */ static void do_makescalar(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static int get_double_list(char *dst, char *key, char *nextarg); static long get_vector_length(int mincid); /* Argument variables */ static int clobber = FALSE; #if MINC2 static int v2format = FALSE; #endif /* MINC2 */ static int verbose = TRUE; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = FALSE; static double valid_range[2] = {0.0, 0.0}; static int buffer_size = 10 * 1024; static Conversion_Type conversion_type = CONV_DEFAULT; static Double_Array linear_coefficients = {0, NULL}; /* Argument table */ static ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &v2format, "Produce a MINC 2.0 format output file."}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-buffer_size", ARGV_INT, (char *) 1, (char *) &buffer_size, "Set the internal buffer size (in kb)."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {"-magnitude", ARGV_CONSTANT, (char *) CONV_MAGNITUDE, (char *) &conversion_type, "Compute magnitude of vectors (default)."}, {"-average", ARGV_CONSTANT, (char *) CONV_AVERAGE, (char *) &conversion_type, "Average components of vectors."}, {"-rgbtogrey", ARGV_CONSTANT, (char *) CONV_GREY, (char *) &conversion_type, "Convert RGB to greyscale."}, {"-rgbtogray", ARGV_CONSTANT, (char *) CONV_GREY, (char *) &conversion_type, "Synonym for rgbtogrey."}, {"-linear", ARGV_FUNC, (char *) get_double_list, (char *) &linear_coefficients, "Specify comma-separated list of coefficients for linear combination."}, {NULL, ARGV_END, NULL, NULL, NULL} }; static const char str_wrong_dimension_order[] = { "Your input file contains an image with a vector dimension, but the\n" "vector dimension isn't the last (i.e. fastest-varying) dimension in\n" "the image. Please restructure the file using mincreshape before\n" "attempting to use mincmakescalar.\n" }; /* Main program */ int main(int argc, char *argv[]) { char *infile, *outfile; char *arg_string; int inmincid; int input_vector_length; int ivalue; Loop_Options *loop_options; Program_Data program_data; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { (void) fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* Check for conflicting options */ if ((conversion_type != CONV_DEFAULT) && (linear_coefficients.numvalues > 0)) { (void) fprintf(stderr, "Do not specify -linear with other conversion options\n"); exit(EXIT_FAILURE); } /* Set up conversion information */ if (conversion_type == CONV_DEFAULT) conversion_type = CONV_MAGNITUDE; program_data.conversion_type = conversion_type; program_data.num_coefficients = 0; program_data.linear_coefficients = NULL; /* Check for coefficients for linear combination */ if (linear_coefficients.numvalues > 0) { conversion_type = CONV_LINEAR; program_data.conversion_type = conversion_type; program_data.num_coefficients = linear_coefficients.numvalues; program_data.linear_coefficients = malloc(linear_coefficients.numvalues * sizeof(*program_data.linear_coefficients)); for (ivalue=0; ivalue < linear_coefficients.numvalues; ivalue++) { program_data.linear_coefficients[ivalue] = linear_coefficients.values[ivalue]; } } /* Open the input file and get the vector length */ inmincid = miopen(infile, NC_NOWRITE); input_vector_length = get_vector_length(inmincid); if (input_vector_length < 0) { fprintf(stderr, str_wrong_dimension_order); exit(EXIT_FAILURE); } if (input_vector_length < 1) input_vector_length = 1; /* Check that this length is okay */ if ((conversion_type == CONV_GREY) && (input_vector_length != 3) && (input_vector_length > 1)) { (void) fprintf(stderr, "Input file does not contain RGB data\n"); exit(EXIT_FAILURE); } if ((conversion_type == CONV_LINEAR) && (input_vector_length != program_data.num_coefficients) && (input_vector_length > 1)) { (void) fprintf(stderr, "Input vector length does not match number of linear coefficients\n"); exit(EXIT_FAILURE); } /* Set up looping options */ loop_options = create_loop_options(); set_loop_clobber(loop_options, clobber); set_loop_verbose(loop_options, verbose); #if MINC2 set_loop_v2format(loop_options, v2format); #endif /* MINC2 */ set_loop_datatype(loop_options, datatype, is_signed, valid_range[0], valid_range[1]); set_loop_output_vector_size(loop_options, 1); set_loop_buffer_size(loop_options, (long) buffer_size * 1024); set_loop_first_input_mincid(loop_options, inmincid); /* Do loop */ voxel_loop(1, &infile, 1, &outfile, arg_string, loop_options, do_makescalar, (void *) &program_data); /* Free stuff */ if (program_data.linear_coefficients != NULL) { free(program_data.linear_coefficients); } if (linear_coefficients.values != NULL) { free(linear_coefficients.values); } exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_makescalar @INPUT : caller_data - pointer to structure containing lookup info num_voxels - number of voxels to work on input_num_buffers - number of input buffers input_vector_length - length of input vector dimension input_data - vector of pointers to input buffer data output_num_buffers - number of output buffers output_vector_length - length of output vector dimension start - vector specifying start of hyperslab (not used) count - vector specifying count of hyperslab (not used) @OUTPUT : output_data - vector of pointers to output buffer data @RETURNS : (nothing) @DESCRIPTION: Routine to loop through an array of vector values and convert them to scalar. @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_makescalar(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Program_Data *program_data; long ivoxel; double *input_vector; double value, result; int ivalue; static double grey_coefficients[3] = {0.299, 0.587, 0.114}; /* Get pointer to lookup info */ program_data = (Program_Data *) caller_data; /* Check that values correspond */ if ((input_num_buffers != 1) || (output_num_buffers != 1) || (output_vector_length != 1)) { (void) fprintf(stderr, "Bad internal values.\n"); exit(EXIT_FAILURE); } if ((program_data->conversion_type == CONV_GREY) && (input_vector_length != 3)) { (void) fprintf(stderr, "Input must have three components for -grey conversion.\n"); exit(EXIT_FAILURE); } /* Loop through the voxels */ for (ivoxel=0; ivoxel < num_voxels; ivoxel++) { /* Handle the special case of scalar input */ if (input_vector_length <= 1) { output_data[0][ivoxel] = input_data[0][ivoxel]; continue; } /* Get location of input value */ input_vector = &input_data[0][ivoxel*input_vector_length]; /* Loop over components of vector */ result = 0.0; for (ivalue=0; ivalue < input_vector_length; ivalue++) { /* Get value and check for invalid values */ value = input_vector[ivalue]; if (value == INVALID_DATA) { result = INVALID_DATA; break; } /* Sum things up according to scheme */ switch (program_data->conversion_type) { case CONV_AVERAGE: result += value; break; case CONV_MAGNITUDE: result += value * value; break; case CONV_GREY: result += grey_coefficients[ivalue] * value; break; case CONV_LINEAR: result += program_data->linear_coefficients[ivalue] * value; break; default: break; } } /* End of loop over components */ /* Finish up the calculation */ if (result != INVALID_DATA) { switch (program_data->conversion_type) { case CONV_AVERAGE: result /= (double) input_vector_length; break; case CONV_MAGNITUDE: result = sqrt(result); break; default: break; } } /* Save the result */ output_data[0][ivoxel] = result; } return; } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static 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; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vector_length @INPUT : mincid - minc file id @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 : ---------------------------------------------------------------------------- */ static long get_vector_length(int mincid) { int imgid; int ndims; int dim[MAX_VAR_DIMS]; char dimname[MAX_NC_NAME]; long vector_length; int i; /* Get image variable id */ imgid = ncvarid(mincid, MIimage); /* Get the image dimension info */ (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; /* New deal - check for an actual vector_dimension anywhere in the * file. */ for (i = 0; i < ndims; i++) { ncdiminq(mincid, dim[i], dimname, NULL); if (!strcmp(dimname, MIvector_dimension)) { vector_length = -1; break; } } } return vector_length; } minc-tools-2.3.00+dfsg/progs/minc_modify_header/0002755000175000000620000000000012574624760020563 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minc_modify_header/minc_modify_header.c0000644000175000000620000004261212574624760024537 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_modify_header @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Modifies the header of a minc file, in place if possible. @METHOD : @GLOBALS : @CALLS : @CREATED : March 31, 1995 (Peter Neelin) @MODIFIED : * $Log: minc_modify_header.c,v $ * Revision 6.13 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.12 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.11 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.10 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.9 2004/06/11 15:19:34 bert * Fix attribute append operation * * Revision 6.8 2004/05/25 21:33:51 bert * Add -dappend and -sappend * * Revision 6.7 2004/02/02 18:27:06 bert * Include config.h * * Revision 6.6 2003/11/14 16:52:24 stever * More last-minute fixes. * * Revision 6.5 2001/04/17 18:40:16 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/12/13 16:19:37 neelin * Removed debugging statements * * Revision 6.3 2000/11/03 16:35:40 neelin * Modified -dinsert option to allow multiple double values to be * inserted in an attribute. * * Revision 6.2 2000/09/13 14:12:35 neelin * Added support for bzipped files (thanks to Steve Robbins). * * Revision 6.1 1999/10/19 14:45:17 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:07 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:06 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:35 neelin * Release of minc version 0.4 * * Revision 3.1 1995/11/16 13:16:19 neelin * Added include of math.h to get declaration of strtod under SunOs * * Revision 3.0 1995/05/15 19:32:21 neelin * Release of minc version 0.3 * * Revision 1.2 1995/04/04 19:10:56 neelin * Fixed handling of compressed files. * * Revision 1.1 1995/04/04 15:51:42 neelin * Initial revision * @COPYRIGHT : Copyright 1995 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 "config.h" #include #include #include #include #include #include #include /* Constants */ #define MINC_EXTENSION ".mnc" #define BZIP_EXTENSION ".bz" #define BZIP2_EXTENSION ".bz2" #define GZIP_EXTENSION ".gz" #define COMPRESS_EXTENSION ".Z" #define PACK_EXTENSION ".z" #define ZIP_EXTENSION ".zip" #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif /* Typedefs */ typedef enum { Insert_attribute, Delete_attribute, Append_attribute } Attribute_Action; /* Functions */ int get_attribute(char *dst, char *key, char *nextarg); /* Argument variables */ int attribute_list_size = 0; int attribute_list_alloc = 0; struct { Attribute_Action action; char *variable; char *attribute; char *value; int num_doubles; double *double_values; } *attribute_list = NULL; /* Argument table */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, NULL, NULL, "Options for specifying attribute values by name."}, {"-sinsert", ARGV_FUNC, (char *) get_attribute, NULL, "Insert string attribute (:=)."}, {"-dinsert", ARGV_FUNC, (char *) get_attribute, NULL, "Insert a double precision attribute (:=(,...))."}, {"-delete", ARGV_FUNC, (char *) get_attribute, NULL, "Delete an attribute (:)."}, {"-sappend", ARGV_FUNC, (char *) get_attribute, NULL, "Append string attribute (:=)."}, {"-dappend", ARGV_FUNC, (char *) get_attribute, NULL, "Append a double precision attribute (:=(,...))."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *pname; char *filename, *tempfile, *newfile; char string[1024]; char *variable_name, *attribute_name; int created_tempfile; int done_redef; int iatt; int mincid, varid; int variable_exists, attribute_exists; nc_type attribute_type, new_type; int attribute_length, new_length; void *new_value; int total_length, alloc_length, ival; char *zeros; int old_ncopts; /* Parse the command line */ pname=argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc != 2)) { (void) fprintf(stderr, "\nUsage: %s [] \n", pname); (void) fprintf(stderr, " %s [-help]\n\n", pname); exit(EXIT_FAILURE); } filename = argv[1]; /* Create temp file name. First try looking for minc extension, then a compression extension. Chop off the unwanted extension. */ (void) strncpy(string, filename, sizeof(string)-1); tempfile = strstr(string, MINC_EXTENSION); if (tempfile != NULL) { tempfile += strlen(MINC_EXTENSION); if (*tempfile == '\0') tempfile = NULL; } else { tempfile = strstr(string, GZIP_EXTENSION); if (tempfile == NULL) tempfile = strstr(string, BZIP_EXTENSION); if (tempfile == NULL) tempfile = strstr(string, BZIP2_EXTENSION); if (tempfile == NULL) tempfile = strstr(string, COMPRESS_EXTENSION); if (tempfile == NULL) tempfile = strstr(string, PACK_EXTENSION); if (tempfile == NULL) tempfile = strstr(string, ZIP_EXTENSION); } if (tempfile != NULL) { *tempfile = '\0'; tempfile = string; } /* If tempfile == NULL, then either we have a minc file or we don't know how to edit the file in place. Check that it is a minc file. */ if (tempfile == NULL) { newfile = miexpand_file(filename, tempfile, TRUE, &created_tempfile); if (created_tempfile) { if (newfile != NULL) { (void) remove(newfile); free(newfile); } (void) fprintf(stderr, "Cannot edit file \"%s\" in place.\n", filename); exit(EXIT_FAILURE); } } /* Expand the file. */ newfile = miexpand_file(filename, tempfile, FALSE, &created_tempfile); if (newfile == NULL) { (void) fprintf(stderr, "Error decompressing file \"%s\"\n", filename); exit(EXIT_FAILURE); } /* If a new file was created, get rid of the old one */ if (created_tempfile) { (void) remove(filename); } /* Open the file */ mincid = miopen(newfile, NC_WRITE); /* Loop through attribute list, modifying values */ done_redef = FALSE; ncopts = NC_VERBOSE; zeros = NULL; alloc_length = 0; for (iatt=0; iatt < attribute_list_size; iatt++) { /* Get variable and attribute name */ variable_name = attribute_list[iatt].variable; attribute_name = attribute_list[iatt].attribute; /* Check for attribute existence */ if (strlen(variable_name) == 0) { varid = NC_GLOBAL; variable_exists = TRUE; } else { old_ncopts = ncopts; ncopts = 0; varid = ncvarid(mincid, variable_name); ncopts = old_ncopts; variable_exists = (varid != MI_ERROR); } attribute_type = NC_CHAR; attribute_length = 0; if (variable_exists) { old_ncopts = ncopts; ncopts = 0; attribute_exists = (ncattinq(mincid, varid, attribute_name, &attribute_type, &attribute_length) != MI_ERROR); ncopts = old_ncopts; } else attribute_exists = FALSE; /* Are we inserting or deleting? */ switch (attribute_list[iatt].action) { case Insert_attribute: case Append_attribute: if (attribute_list[iatt].value != NULL) { new_type = NC_CHAR; new_length = strlen(attribute_list[iatt].value)+1; new_value = (void *) attribute_list[iatt].value; } else { new_type = NC_DOUBLE; new_length = attribute_list[iatt].num_doubles; new_value = (void *) attribute_list[iatt].double_values; } /* For append we have to copy the entire attribute, if it * already exists. */ if (attribute_list[iatt].action == Append_attribute && attribute_exists) { char *tmp_value; /* Verify that the existing type matches the newly * requested type. Don't allow a -dappend on a * string attribute, for example. */ if (new_type != attribute_type) { fprintf(stderr, "Can't append %s data to %s attribute %s:%s.\n", (new_type == NC_DOUBLE) ? "double" : "string", (attribute_type == NC_DOUBLE) ? "double" : "string", variable_name, attribute_name); exit(EXIT_FAILURE); } new_type = attribute_type; tmp_value = malloc((attribute_length + new_length) * nctypelen(new_type)); ncattget(mincid, varid, attribute_name, tmp_value); /* For string attributes, remove any trailing null * character before appending. */ if (new_type == NC_CHAR && tmp_value[attribute_length-1] == 0) { attribute_length--; } memcpy(tmp_value + attribute_length * nctypelen(new_type), new_value, new_length * nctypelen(new_type)); new_length += attribute_length; new_value = (void *) tmp_value; } total_length = attribute_length*nctypelen(attribute_type); if (!attribute_exists || (total_length < new_length*nctypelen(new_type))) { if (! done_redef) { done_redef = TRUE; (void) ncredef(mincid); } } else if (!done_redef && attribute_exists && (total_length > 0)) { if (total_length > alloc_length) { if (zeros != NULL) free(zeros); zeros = malloc(total_length); alloc_length = total_length; for (ival=0; ival < alloc_length; ival++) zeros[ival] = '\0'; } (void) ncattput(mincid, varid, attribute_name, NC_CHAR, total_length, zeros); (void) ncsync(mincid); } if (!variable_exists) { old_ncopts = ncopts; ncopts = 0; varid = micreate_group_variable(mincid, variable_name); ncopts = old_ncopts; if (varid == MI_ERROR) { varid = ncvardef(mincid, variable_name, NC_INT, 0, NULL); } variable_exists = (varid != MI_ERROR); } if (variable_exists) { (void) ncattput(mincid, varid, attribute_name, new_type, new_length, new_value); } break; case Delete_attribute: if (attribute_exists) { if (! done_redef) { done_redef = TRUE; (void) ncredef(mincid); } (void) ncattdel(mincid, varid, attribute_name); } break; default: (void) fprintf(stderr, "Program error: unknown action %d\n", (int) attribute_list[iatt].action); exit(EXIT_FAILURE); } } ncopts = NC_VERBOSE | NC_FATAL; /* Close the file */ (void) miclose(mincid); /* Free stuff */ free(newfile); if (zeros != NULL) free(zeros); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_attribute @INPUT : dst - client data passed by ParseArgv key - matching key in argv nextarg - argument following key in argv @OUTPUT : (none) @RETURNS : TRUE because nextarg is used. @DESCRIPTION: Gets attributes from command line. Syntax for argument is ":=". Numeric values are converted to double precision. @METHOD : @GLOBALS : @CALLS : @CREATED : May 3, 1994 (Peter Neelin) @MODIFIED : March 31, 1995 (P.N) - grabbed from rawtominc ---------------------------------------------------------------------------- */ int get_attribute(char *dst, char *key, char *nextarg) /* ARGSUSED */ { int need_double; Attribute_Action action; char *format; char *variable; char *attribute; char *value; char *end, *cur; double *dvalues; int num_doubles, ivalue; /* Check the key */ if (strcmp(key, "-sinsert") == 0) { action = Insert_attribute; need_double = FALSE; format = ":="; } else if (strcmp(key, "-dinsert") == 0) { action = Insert_attribute; need_double = TRUE; format = ":="; } else if (strcmp(key, "-delete") == 0) { action = Delete_attribute; need_double = FALSE; format = ":"; } else if (strcmp(key, "-sappend") == 0) { action = Append_attribute; need_double = FALSE; format = ":="; } else if (strcmp(key, "-dappend") == 0) { action = Append_attribute; need_double = TRUE; format = ":="; } /* Check for a following argument */ if (nextarg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Get the variable name */ variable = nextarg; attribute = strchr(variable, ':'); if (attribute == NULL) { (void) fprintf(stderr, "%s option requires argument %s\n", key, format); exit(EXIT_FAILURE); } *attribute = '\0'; attribute++; /* Get the value */ value = NULL; num_doubles = 0; dvalues = NULL; if (action == Insert_attribute || action == Append_attribute) { value = strchr(attribute, '='); if (value == NULL) { (void) fprintf(stderr, "%s option requires argument :=\n", key); exit(EXIT_FAILURE); } *value = '\0'; value++; /* Convert to double precision */ if (need_double) { /* Count the commas */ num_doubles = 1; for (cur=value; *cur != '\0'; cur++) { if (*cur == ',') num_doubles++; } /* Allocate a list */ dvalues = malloc(sizeof(*dvalues) * num_doubles); /* Loop over values */ cur = value; for (ivalue=0; ivalue < num_doubles; ivalue++) { /* Get the value */ dvalues[ivalue] = strtod(cur, &end); if (end == cur) { (void) fprintf(stderr, "\"%s\" option requires a numeric argument\n", key); exit(EXIT_FAILURE); } cur = end; /* Skip whitespace and the comma */ while (isspace((int) *cur)) {cur++;} if ((*cur != '\0') && (*cur != ',')) { (void) fprintf(stderr, "\"%s\" option requires a numeric argument\n", key); exit(EXIT_FAILURE); } else if (*cur == ',') { cur++; } } /* End of loop over double values */ /* Clear the value string */ value = NULL; } /* Endif need_double */ } /* Endif insert */ /* Save the information */ attribute_list_size++; if (attribute_list_size > attribute_list_alloc) { attribute_list_alloc += 10; if (attribute_list == NULL) { attribute_list = malloc(attribute_list_alloc * sizeof(*attribute_list)); } else { attribute_list = realloc(attribute_list, attribute_list_alloc * sizeof(*attribute_list)); } } attribute_list[attribute_list_size-1].action = action; attribute_list[attribute_list_size-1].variable = variable; attribute_list[attribute_list_size-1].attribute = attribute; attribute_list[attribute_list_size-1].value = value; attribute_list[attribute_list_size-1].num_doubles = num_doubles; attribute_list[attribute_list_size-1].double_values = dvalues; return TRUE; } minc-tools-2.3.00+dfsg/progs/minc_modify_header/minc_modify_header.man10000644000175000000620000000743112574624760025151 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1995 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. .\" .\" $Header: /private-cvsroot/minc/progs/minc_modify_header/minc_modify_header.man1,v 6.4 2004-05-25 21:33:11 bert Exp $ .\" .TH MINC_MODIFY_HEADER 1 "$Date: 2004-05-25 21:33:11 $" "" "MINC User's Guide" .SH NAME minc_modify_header - modify the attributes in the header of a minc file .SH SYNOPSIS .B minc_modify_header [] .mnc .SH DESCRIPTION \fIMinc_modify_header\fR allows the modification, insertion or deletion of attributes in a minc file. If possible, the file is modified in place, without copying the data. This will happen when inserting (modifying) an attribute that already exists and that ends up being the same length or shorter in the new file. If an attribute is deleted or lengthened, then a complete copy of the data is made, resulting in a completely new file that replaces the original. If the file is compressed, then it is first decompressed into a file whose name is either the same as that of the original file up to the ".mnc" extension or the same minus the compression extension (".bz", ".bz2", ".gz", ".Z", ".z" or ".zip"). The new file will not be re-compressed. Care is taken to completely overwrite any existing attribute when inserting a new attribute so that information is guaranteed to be removed from the file. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .TP \fB\-sinsert\fR \fIvar:attr=value\fR Insert a string attribute into the header. If the attribute does not exist or the new string is longer than the existing one, then all data in the file will be copied. .TP \fB\-sappend\fR \fIvar:attr=value\fR Similar to \fB\-sinsert\fR, but appends the string to the attribute's value. If the attribute already exists it must be of string type. .TP \fB\-dinsert\fR \fIvar:attr=value(,...)\fR Insert a double precision attribute into the header. If the attribute does not exist or the new attribute is longer than the existing one, then all data in the file will be copied. A comma-separated array of values can be specified. .TP \fB\-dappend\fR \fIvar:attr=value(,...)\fR Similar to \fB\-dinsert\fR, but appends the list of double precision values to the attribute's value. If the attribute already exists it must be of double precision type. .TP \fB-delete\fR \fIvar:attr\fR Delete an attribute from the header. USE OF THIS OPTION WILL FORCE A COMPLETE COPY OF ALL DATA TO BE MADE. Use \fB-sinsert\fR with an empty string to delete information without copying data (the attribute will continue to exist). .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES: .PP To replace the patient name with an identifier string: .IP minc_modify_header file.mnc -sinsert 'patient:full_name=C02-F0023' .PP To delete the patient name completely (forcing a copy of all data): .IP minc_modify_header file.mnc -delete 'patient:full_name' .PP To hide the patient name without copying data, assuming that we know that the attribute exists (the attribute will remain in the file, but it will be empty): .IP minc_modify_header file.mnc -sinsert 'patient:full_name=' .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1995 by Peter Neelin minc-tools-2.3.00+dfsg/progs/CMakeLists.txt0000644000175000000620000002014012574624760017511 0ustar stevestaff# CMakeFiles.txt for the MINC2 progs # # Andrew Janke - a.janke@gmail.com INCLUDE_DIRECTORIES(Proglib) LINK_LIBRARIES( ${LIBMINC_LIBRARIES} ) ADD_DEFINITIONS(-DHAVE_CONFIG_H) IF(NOT MINC_TOOLS_EXTERNALLY_CONFIGURED) FIND_PACKAGE(BISON) FIND_PACKAGE(FLEX) ENDIF(NOT MINC_TOOLS_EXTERNALLY_CONFIGURED) # all the progs ADD_EXECUTABLE(invert_raw_image mincview/invert_raw_image.c) ADD_EXECUTABLE(mincaverage mincaverage/mincaverage.c) TARGET_LINK_LIBRARIES(mincaverage m) IF(BISON_FOUND AND FLEX_FOUND) IF(NOT FL_LIBRARY) # A HACK to fix missing FLEX library SET(FLEX_LIBRARIES "") ENDIF(NOT FL_LIBRARY) include_directories(${CMAKE_CURRENT_BINARY_DIR} minccalc) BISON_TARGET(gram ${CMAKE_CURRENT_SOURCE_DIR}/minccalc/gram.y ${CMAKE_CURRENT_BINARY_DIR}/gram.c COMPILE_FLAGS "--debug") FLEX_TARGET(lex ${CMAKE_CURRENT_SOURCE_DIR}/minccalc/lex.l ${CMAKE_CURRENT_BINARY_DIR}/lex.c ) #ADD_FLEX_BISON_DEPENDENCY(gram lex) ADD_EXECUTABLE(minccalc minccalc/minccalc.c minccalc/eval.c minccalc/ident.c minccalc/node.c minccalc/optim.c minccalc/scalar.c minccalc/sym.c minccalc/vector.c ${FLEX_lex_OUTPUTS} ${BISON_gram_OUTPUTS} ) TARGET_LINK_LIBRARIES(minccalc ${FLEX_LIBRARIES} m) INSTALL( TARGETS minccalc DESTINATION bin) include_directories(${CMAKE_CURRENT_BINARY_DIR} mincgen) BISON_TARGET(ncgentab ${CMAKE_CURRENT_SOURCE_DIR}/mincgen/ncgentab.y ${CMAKE_CURRENT_BINARY_DIR}/ncgentab.c) FLEX_TARGET(ncgenyy ${CMAKE_CURRENT_SOURCE_DIR}/mincgen/ncgenyy.l ${CMAKE_CURRENT_BINARY_DIR}/ncgenyy.c) ADD_EXECUTABLE(mincgen mincgen/main.c ${BISON_ncgentab_OUTPUTS} ${FLEX_ncgenyy_OUTPUTS} mincgen/escapes.c mincgen/genlib.c mincgen/getfill.c mincgen/init.c mincgen/load.c) INSTALL( TARGETS mincgen DESTINATION bin) TARGET_LINK_LIBRARIES(mincgen ${FLEX_LIBRARIES}) ENDIF(BISON_FOUND AND FLEX_FOUND) ADD_EXECUTABLE(mincconcat mincconcat/mincconcat.c) ADD_EXECUTABLE(mincconvert mincconvert/mincconvert.c) ADD_EXECUTABLE(minccopy minccopy/minccopy.c) ADD_EXECUTABLE(mincdump mincdump/mincdump.c mincdump/vardata.c mincdump/dumplib.c) ADD_EXECUTABLE(mincexample1 mincexample/mincexample1.c) ADD_EXECUTABLE(mincexample2 mincexample/mincexample2.c) ADD_EXECUTABLE(mincexpand mincexpand/mincexpand.c) ADD_EXECUTABLE(mincextract mincextract/mincextract.c Proglib/minc_endian.c) ADD_EXECUTABLE(mincinfo mincinfo/mincinfo.c) ADD_EXECUTABLE(minclookup minclookup/minclookup.c) TARGET_LINK_LIBRARIES(minclookup m) ADD_EXECUTABLE(mincmakescalar mincmakescalar/mincmakescalar.c) TARGET_LINK_LIBRARIES(mincmakescalar m) ADD_EXECUTABLE(mincmakevector mincmakevector/mincmakevector.c) ADD_EXECUTABLE(mincmath mincmath/mincmath.c) TARGET_LINK_LIBRARIES(mincmath m) ADD_EXECUTABLE(minc_modify_header minc_modify_header/minc_modify_header.c) ADD_EXECUTABLE(mincresample mincresample/mincresample.c mincresample/resample_volumes.c Proglib/convert_origin_to_start.c) TARGET_LINK_LIBRARIES(mincresample ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(mincreshape mincreshape/mincreshape.c mincreshape/copy_data.c) ADD_EXECUTABLE(mincstats mincstats/mincstats.c) TARGET_LINK_LIBRARIES(mincstats m) ADD_EXECUTABLE(minctoraw minctoraw/minctoraw.c Proglib/minc_endian.c) ADD_EXECUTABLE(mincwindow mincwindow/mincwindow.c) ADD_EXECUTABLE(mincmorph mincmorph/mincmorph.c mincmorph/kernel_io.c mincmorph/kernel_ops.c mincmorph/kernel_io.h mincmorph/kernel_ops.h ) TARGET_LINK_LIBRARIES(mincmorph ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(mincsample mincsample/mincsample.c mincsample/mt19937ar.c mincsample/mt19937ar.h ) TARGET_LINK_LIBRARIES(mincsample ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(rawtominc rawtominc/rawtominc.c Proglib/convert_origin_to_start.c) TARGET_LINK_LIBRARIES(rawtominc m) ADD_EXECUTABLE(voxeltoworld coordinates/voxeltoworld.c) TARGET_LINK_LIBRARIES(voxeltoworld ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(worldtovoxel coordinates/worldtovoxel.c) TARGET_LINK_LIBRARIES(worldtovoxel ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(transformtags xfm/transformtags.c) TARGET_LINK_LIBRARIES(transformtags ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(xfmconcat xfm/xfmconcat.c) TARGET_LINK_LIBRARIES(xfmconcat ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(xfminvert xfm/xfminvert.c) TARGET_LINK_LIBRARIES(xfminvert ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(mincblob mincblob/mincblob.c) TARGET_LINK_LIBRARIES(mincblob ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(minccmp minccmp/minccmp.c) TARGET_LINK_LIBRARIES(minccmp ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) SET(MINC_TOOLS invert_raw_image mincaverage mincconcat mincconvert minccopy mincdump # mincexample1 # mincexample2 mincblob minccmp mincexpand mincextract mincinfo minclookup mincmakescalar mincmakevector mincmath minc_modify_header mincresample mincreshape mincstats minctoraw mincwindow mincmorph mincsample rawtominc voxeltoworld worldtovoxel transformtags xfmconcat xfminvert ) # install progs INSTALL(TARGETS ${MINC_TOOLS} DESTINATION bin) add_custom_target (minc_tools DEPENDS ${MINC_TOOLS} ) # perl and shell scripts INSTALL(PROGRAMS mincdiff/mincdiff mincedit/mincedit mincheader/mincheader mincview/mincview PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ DESTINATION bin ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/minchistory/minchistory.in ${CMAKE_CURRENT_BINARY_DIR}/minchistory.out @ONLY) add_custom_target(minchistory chmod +x ${CMAKE_CURRENT_BINARY_DIR}/minchistory.out DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/minchistory/minchistory.in) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/mincpik/mincpik.in ${CMAKE_CURRENT_BINARY_DIR}/mincpik.out @ONLY) add_custom_target(mincpik chmod +x ${CMAKE_CURRENT_BINARY_DIR}/mincpik.out DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/mincpik/mincpik.in) INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/minchistory.out PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ RENAME minchistory DESTINATION bin ) INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/mincpik.out PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ RENAME mincpik DESTINATION bin ) INSTALL_MAN_PAGES( ${CMAKE_INSTALL_PREFIX}/man coordinates/voxeltoworld.man1 mincaverage/mincaverage.man1 mincblob/mincblob.man1 minccalc/minccalc.man1 minccmp/minccmp.man1 mincconcat/mincconcat.man1 mincconvert/mincconvert.man1 minccopy/minccopy.man1 mincdiff/mincdiff.man1 mincdump/mincdump.man1 mincedit/mincedit.man1 mincexpand/mincexpand.man1 mincextract/mincextract.man1 mincgen/mincgen.man1 mincheader/mincheader.man1 mincinfo/mincinfo.man1 minclookup/minclookup.man1 mincmakescalar/mincmakescalar.man1 mincmakevector/mincmakevector.man1 mincmath/mincmath.man1 minc_modify_header/minc_modify_header.man1 mincresample/mincresample.man1 mincreshape/mincreshape.man1 mincsample/mincsample.man1 mincstats/mincstats.man1 minctoraw/minctoraw.man1 mincview/invert_raw_image.man1 mincview/mincview.man1 mincwindow/mincwindow.man1 rawtominc/rawtominc.man1 xfm/transformtags.man1 xfm/xfm2def.man1 xfm/xfmconcat.man1 xfm/xfmflip.man1 xfm/xfminvert.man1 ) minc-tools-2.3.00+dfsg/progs/mincheader/0002755000175000000620000000000012574624760017055 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincheader/mincheader0000755000175000000620000000606412574624760021106 0ustar stevestaff#! /bin/sh # # Script to view header info of MINC files. # # Usage: mincheader [-data] # Exit status: status of `mincdump' # # # Modifications: # $Log: mincheader,v $ # Revision 6.4 2008-01-04 06:01:23 rotor # * updated shell scripting style and added more verbose help # # Revision 6.3 2004/04/27 15:32:58 bert # Use mincdump instead of ncdump # # Revision 6.2 2000/09/12 15:20:07 neelin # Rewrite in Bourne shell by Steve Robbins. Returns exit status from mincdump. # # Revision 6.1 1999/10/19 14:45:23 neelin # Fixed Log subsitutions for CVS # # Revision 6.0 1997/09/12 13:23:37 neelin # Release of minc version 0.6 # # Revision 5.0 1997/08/21 13:24:37 neelin # Release of minc version 0.5 # # Revision 4.0 1997/05/07 20:00:42 neelin # Release of minc version 0.4 # # Revision 3.0 1995/05/15 19:31:32 neelin # Release of minc version 0.3 # # Revision 2.2 1995/01/25 08:04:41 neelin # Added check for failure to read minc file. # # Revision 2.1 95/01/24 10:19:09 neelin # Added support for compressed files. # # Revision 2.0 94/09/28 10:34:10 neelin # Release of minc version 0.2 # # Revision 1.6 94/09/28 10:34:06 neelin # Pre-release # # Revision 1.5 93/10/14 10:09:35 neelin # Fixed usage error message. # # Revision 1.4 93/08/25 11:24:20 neelin # Added checking for -h or -help options. # # Revision 1.3 93/08/11 15:20:55 neelin # Added RCS logging in source. # set -o errexit set -o nounset # simple little function to emulate perl's die(); die () { echo >&2 "$@" echo "" exit 1 } # create tmpdir tmpdir=${TMPDIR:-/tmp}/mincedit.$$ trap 'rm -rf "$tmpdir"' 0 trap ' exit ' 1 2 15 (umask 077 && mkdir $tmpdir) || { echo "$0: Could not create temporary directory! Exiting." 1>&2 echo "" exit 1 } me=`basename $0` usage="Usage: $me [-data] " # set defaults and then get arguments header_opts='-header_only' header_only=yes while [ $# -gt 0 ] do case "$1" in -h|-he|-hel|-help) echo $usage; echo "" exit 0 ;; -d|-da|-dat|-data) header_opts='-all_data'; header_only=no ;; *) break; esac shift done # only allow one input file and check for it [ $# -eq 1 ] || die $usage [ -f "$1" ] || die "$me: no such file '$1'" tmpfile="$tmpdir/mincheader-$$-tmp" # For a compressed file, dump header only unless directed otherwise dumpfile=`mincexpand "$1" $tmpfile -name_only $header_opts` if [ "$dumpfile" = $tmpfile -a $header_only = yes ] then mincdump -h "$dumpfile" else # Get variables names (excluding image): remove leading "image", remove word # "image", collapse multiple blanks to one, remove leading blanks, remove # trailing blanks, replace blanks with commas vars=`mincinfo -varnames "$dumpfile" | \ sed -e 's/^image //g' \ -e 's/ image / /g' \ -e 's/ */ /g' \ -e 's/^ //' \ -e 's/ $//' \ -e 's/ /,/g'` [ x$vars = x ] || mincdump -v "$vars" "$dumpfile" fi minc-tools-2.3.00+dfsg/progs/mincheader/mincheader.man10000644000175000000620000000246312574624760021735 0ustar stevestaff.\" 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincheader/mincheader.man1,v 6.1 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCHEADER 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincheader - prints out header information for a minc file .SH SYNOPSIS .B mincheader [-data] .SH DESCRIPTION \fIMincheader\fR prints out the header information for a minc file in cdl format (the netCDF text format). \fIMincheader\fR simply calls \fImincdump\fR, writing out all information except the image data. If the input file is compressed, then its header is decompressed and only the header is printed. If the \fB-data\fR option is given, then the whole file is decompressed and all data (except the image) is dumped. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincmakevector/0002755000175000000620000000000012574624760017765 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmakevector/mincmakevector.man10000644000175000000620000000657512574624760023565 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1997 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincmakevector/mincmakevector.man1,v 6.2 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCMAKEVECTOR 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincmakevector - convert a list of scalar minc files into one vector file .SH SYNOPSIS .B mincmakevector [] .mnc [...] .mnc .SH DESCRIPTION .I Mincmakevector converts a list of scalar minc files into one vector minc file. A vector minc file is one that contains the dimension \fIvector_dimension\fR as the fastest varying dimension of the image data and represents vector data at each voxel such as RGB images or gradient volumes. A scalar minc file does not contain the vector dimension and represents grayscale or intensity data. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-no_clobber\fR Synonym for \fB\-noclobber\fR. .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB-buffer_size\fR\ \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 10 MB. .TP \fB\-filetype\fR Create an output file with the same type as the first input file (default). .TP \fB\-byte\fR Store output voxels as 8-bit integers. .TP \fB\-short\fR Store output voxels as 16-bit integers. .TP \fB\-int\fR Store output voxels as 32-bit integers .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB\-float\fR Store output voxels as 32-bit floating point numbers. .TP \fB\-double\fR Store output voxels as 64-bit floating point numbers. .TP \fB\-signed\fR Create an output file with data stored in a signed type. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .TP \fB\-unsigned\fR Create an output file with data stored in an unsigned type. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .TP \fB\-valid_range\fR\ \fImin\ max\fR Create an output file with integer data stored in the specified restricted range. This only has an effect if the one of the \fB\-byte\fR, \fB\-short\fR or \fB\-int\fR options is specified. .SH Generic options for all commands: .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES To convert files containing red, green and blue colour components into an RGB file: mincmakevector red.mnc green.mnc blue.mnc rgb.mnc .SH AUTHOR Peter Neelin .SH COPYRIGHTS .ps 18 Copyright 1997 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincmakevector/mincmakevector.c0000644000175000000620000002347712574624760023153 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincmakevector @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to make a vector minc file from a series of scalar minc files. @METHOD : @GLOBALS : @CALLS : @CREATED : August 11, 1997 (Peter Neelin) @MODIFIED : * $Log: mincmakevector.c,v $ * Revision 6.10 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.9 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.8 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.7 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.6 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.5 2004/04/30 19:53:30 bert * Remove unused variable * * Revision 6.4 2004/04/27 15:32:15 bert * Added -2 option * * Revision 6.3 2001/04/24 13:38:44 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.2 2001/04/17 18:40:21 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:25 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:31 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:32 neelin * Release of minc version 0.5 * * Revision 1.1 1997/08/12 19:03:46 neelin * Initial revision * ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define DEFAULT_RANGE DBL_MAX #define NCOPTS_DEFAULT NC_VERBOSE | NC_FATAL #define INVALID_DATA (-DBL_MAX) /* Structure for program info */ typedef struct { int dummy; } Program_Data; /* Function prototypes */ static void do_makevector(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static long get_vector_length(int mincid); /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; #if MINC2 static int v2format = FALSE; #endif /* MINC2 */ static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = FALSE; static double valid_range[2] = {0.0, 0.0}; static int buffer_size = 10 * 1024; /* Argument table */ static ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &v2format, "Produce a MINC 2.0 format output file."}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-buffer_size", ARGV_INT, (char *) 1, (char *) &buffer_size, "Set the internal buffer size (in kb)."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char **input_files; char *output_file; char *arg_string; int num_input_files; int inmincid; Loop_Options *loop_options; Program_Data program_data; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc < 3)) { (void) fprintf(stderr, "\nUsage: %s [options] [...] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } input_files = &argv[1]; num_input_files = argc - 2; output_file = argv[argc-1]; /* Open the first input file and get the vector length */ inmincid = miopen(input_files[0], NC_NOWRITE); if (get_vector_length(inmincid) > 1) { (void) fprintf(stderr, "Input file %s is not a scalar file\n", input_files[0]); exit(EXIT_FAILURE); } /* Set up looping options */ loop_options = create_loop_options(); set_loop_clobber(loop_options, clobber); set_loop_verbose(loop_options, verbose); #if MINC2 set_loop_v2format(loop_options, v2format); #endif /* MINC2 */ set_loop_datatype(loop_options, datatype, is_signed, valid_range[0], valid_range[1]); set_loop_output_vector_size(loop_options, num_input_files); set_loop_buffer_size(loop_options, (long) buffer_size * 1024); set_loop_first_input_mincid(loop_options, inmincid); set_loop_accumulate(loop_options, TRUE, 0, NULL, NULL); /* Do loop */ voxel_loop(num_input_files, input_files, 1, &output_file, arg_string, loop_options, do_makevector, (void *) &program_data); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_makevector @INPUT : caller_data - pointer to structure containing lookup info num_voxels - number of voxels to work on input_num_buffers - number of input buffers input_vector_length - length of input vector dimension input_data - vector of pointers to input buffer data output_num_buffers - number of output buffers output_vector_length - length of output vector dimension start - vector specifying start of hyperslab (not used) count - vector specifying count of hyperslab (not used) @OUTPUT : output_data - vector of pointers to output buffer data @RETURNS : (nothing) @DESCRIPTION: Routine to copy values from scalar files into a vector. @METHOD : @GLOBALS : @CALLS : @CREATED : August 11, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_makevector(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { long ivoxel, ovoxel; int current_input_file; /* Check that values correspond */ if ((input_num_buffers != 1) || (output_num_buffers != 1) || (input_vector_length != 1)) { (void) fprintf(stderr, "Bad internal values.\n"); exit(EXIT_FAILURE); } /* Figure out which file we are looking at */ current_input_file = get_info_current_file(loop_info); /* Loop through the voxels */ for (ivoxel=0, ovoxel = current_input_file; ivoxel < num_voxels; ivoxel++, ovoxel += output_vector_length) { output_data[0][ovoxel] = input_data[0][ivoxel]; } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vector_length @INPUT : mincid - minc file id @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 : ---------------------------------------------------------------------------- */ static long get_vector_length(int mincid) { 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 */ (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; } minc-tools-2.3.00+dfsg/progs/minchistory/0002755000175000000620000000000012574624760017326 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minchistory/minchistory.in0000755000175000000620000000767412574624760022245 0ustar stevestaff#! /usr/bin/env perl # # Simple script to dump history information from a minc file # # Copyright Andrew Janke a.janke@gmail.com # # 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 makes no representations about the suitability of this software # for any purpose. It is provided "as is" without express or implied warranty. my($n_columns) = 80; use strict; use warnings "all"; use Getopt::Long; use Pod::Usage; use Text::Format; use File::Basename; my($me, %opt, $cmd_text, $text, $fname, $counter, @tmp, $c); $me = &basename($0); %opt = ( 'help' => 0, 'man' => 0, 'verbose' => 0, 'error_string' => undef, ); # Check arguments &GetOptions( 'help|?' => \$opt{'help'}, 'man' => \$opt{'man'}, 'v|verbose' => \$opt{'verbose'}, 'version' => sub { &print_version_info }, 'e|error_string=s' => \$opt{'error_string'}, ) or pod2usage(-verbose => 1) && exit; # handle -man, -help or missing args pod2usage(-verbose => 1) if $opt{'help'}; pod2usage(-exitstatus => 0, -verbose => 2) if $opt{'man'}; pod2usage(-verbose => 0) && exit if ($#ARGV < 0); #$Usage = "Usage: $me [ ... ]\n\n"; #die $Usage if ($#ARGV < 0); # set up text objects $text = new Text::Format( firstIndent => 0, bodyIndent => 5, columns => $n_columns ); $cmd_text = new Text::Format( firstIndent => 0, bodyIndent => 5, columns => ($n_columns - 2) ); # for each input file foreach $fname (@ARGV){ if(!-e $fname){ warn "$me: Couldn't find $fname\n"; next; } print STDOUT $text->format("--- History of $fname ---\n"); $counter = 1; foreach (`mincinfo -attvalue :history "$fname"`){ chomp; next if $_ eq ''; # add the command number to the front $_ = sprintf("[%02d] ", $counter ++) . $_; # add \'s to the end of each line except the last @tmp = split(/\n/, $cmd_text->format($_)); for($c=0; $c<$#tmp; $c++){ print STDOUT "$tmp[$c] \\\n"; } print STDOUT "$tmp[$#tmp]\n"; } print STDOUT "\n"; } # print version information sub print_version_info { my $PACKAGE = '@PACKAGE_NAME@'; my $VERSION = '@PACKAGE_VERSION@'; my $PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@'; print STDOUT "\n$PACKAGE version $VERSION\n". "Comments to $PACKAGE_BUGREPORT\n\n"; exit 0; } __END__ =head1 NAME B - prints history information for minc files =head1 SYNOPSIS B [options] [ ... ] minchistory will dump the internal history of one or more MINC files =head1 DESCRIPTION B is a B script that prints the history information in a collection of MINC files. Most MINC files contain an internal "audit trail" that lists the commands used to create the MINC file. This tool provides a convenient way to examine this information. For each file, B prints a title line followed by a series of lines which list the history information for that file. --- History of --- [01] Wed Oct 13 15:35:35 EST 2004>>>> dicom3_to_minc . [02] Wed Oct 13 15:43:34 2004>>> mincreshape +direction \ bob-jones_256x256x256_T1.mnc out.mnc -dimorder zspace,yspace,xspace \ -clobber B is essentially an alias for the following command with a bit of pretty printing added. mincinfo -attvalue :history B =head1 OPTIONS =over 4 =item B<-v>, B<--verbose> Be noisy when doing things =item B<--version> Print version number and exit =item B<-h>, B<--help> Dump some quick help output =item B<--man> Dump a man page =back =head1 SEE ALSO mincinfo(1) =head1 AUTHOR Andrew Janke - a.janke@gmail.com =head1 COPYRIGHTS Copyright 2000 by Andrew L Janke =cuts minc-tools-2.3.00+dfsg/progs/minccopy/0002755000175000000620000000000012574624760016577 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minccopy/minccopy.c0000644000175000000620000001601212574624760020562 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minccopy @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to copy minc image values from one minc file to another (both files must exist). @METHOD : @GLOBALS : @CALLS : @CREATED : May 13, 1993 (Peter Neelin) @MODIFIED : * $Log: minccopy.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:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.4 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.3 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.2 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.1 1999/10/19 14:45:20 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:34 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:34 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:35 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:08 neelin * Release of minc version 0.3 * * Revision 2.1 1995/01/23 12:42:00 neelin * Change ncopen, ncclose to miopen, miclose. * * Revision 2.0 94/09/28 10:33:54 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:33:51 neelin * Pre-release * * Revision 1.6 93/08/11 15:18:00 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Variables used for argument parsing */ static int copy_pixel_values = FALSE; /* Argument table */ ArgvInfo argTable[] = { {"-pixel_values", ARGV_CONSTANT, (char *) TRUE, (char *) ©_pixel_values, "Copy pixel values as is."}, {"-real_values", ARGV_CONSTANT, (char *) FALSE, (char *) ©_pixel_values, "Copy real pixel intensities (default)."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *infilename, *outfilename; int inminc, outminc, inimg, outimg, inicv, outicv; int ndims, outndims, indims[MAX_VAR_DIMS], outdims[MAX_VAR_DIMS]; nc_type indatatype, outdatatype; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS], end[MAX_VAR_DIMS]; long length, size; int idim; void *data; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { (void) fprintf(stderr, "\nUsage: %s [] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infilename = argv[1]; outfilename = argv[2]; /* Open the files */ inminc = miopen(infilename, NC_NOWRITE); outminc = miopen(outfilename, NC_WRITE); /* Inquire about the image variables */ inimg = ncvarid(inminc, MIimage); outimg = ncvarid(outminc, MIimage); (void) ncvarinq(inminc, inimg, NULL, &indatatype, &ndims, indims, NULL); (void) ncvarinq(outminc, outimg, NULL, &outdatatype, &outndims, outdims, NULL); /* If copying pixel values as is, then check that types are the same */ if (copy_pixel_values && (indatatype != outdatatype)) { (void) fprintf(stderr, "%s: Input and output datatypes must be the same for exact pixel copies.\n", argv[0]); exit(EXIT_FAILURE); } /* Check number of dimensions */ if (ndims != outndims) { (void) fprintf(stderr, "%s: Input and output files have different number of image dimensions.\n", argv[0]); exit(EXIT_FAILURE); } /* Set input file start, count and end vectors for reading a slice at a time. Check dimension sizes */ for (idim=0; idim < ndims; idim++) { (void) ncdiminq(inminc, indims[idim], NULL, &end[idim]); (void) ncdiminq(outminc, outdims[idim], NULL, &length); if (end[idim] != length) { (void) fprintf(stderr, "%s: Input and output files have different image dimensions\n", argv[0]); exit(EXIT_FAILURE); } } (void) miset_coords(ndims, (long) 0, start); (void) miset_coords(ndims, (long) 1, count); if (copy_pixel_values) size = nctypelen(indatatype); else size = nctypelen(NC_DOUBLE); for (idim=ndims-2; idim < ndims; idim++) { count[idim] = end[idim]; size *= count[idim]; } /* Allocate space */ data = malloc(size); /* Set up image conversion */ if (!copy_pixel_values) { inicv = miicv_create(); (void) miicv_setint(inicv, MI_ICV_TYPE, NC_DOUBLE); (void) miicv_setint(inicv, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(inicv, MI_ICV_USER_NORM, TRUE); (void) miicv_attach(inicv, inminc, inimg); outicv = miicv_create(); (void) miicv_setint(outicv, MI_ICV_TYPE, NC_DOUBLE); (void) miicv_setint(outicv, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(outicv, MI_ICV_USER_NORM, TRUE); (void) miicv_attach(outicv, outminc, outimg); } /* Loop over input slices */ while (start[0] < end[0]) { /* Read and write slice */ if (copy_pixel_values) { (void) ncvarget(inminc, inimg, start, count, data); (void) ncvarput(outminc, outimg, start, count, data); } else { (void) miicv_get(inicv, start, count, data); (void) miicv_put(outicv, start, count, data); } /* Increment start counter */ idim = ndims-1; start[idim] += count[idim]; while ( (idim>0) && (start[idim] >= end[idim])) { start[idim] = 0; idim--; start[idim] += count[idim]; } } /* End loop over slices */ /* Clean up */ (void) miclose(outminc); (void) miclose(inminc); if (!copy_pixel_values) { (void) miicv_free(outicv); (void) miicv_free(inicv); } free(data); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/minccopy/minccopy.man10000644000175000000620000000225012574624760021173 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCCOPY 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME minccopy \- copy minc image values from one minc file to another .SH SYNOPSIS .B minccopy .BI [-pixel_values] .BI [-real_values] .BI infile .BI outfile .SH DESCRIPTION \fIminccopy\fR copies image values from one minc file to another. Both the input and output files must exist, and the images in both files must have an equal number dimensions and equal dimension lengths. By default the \fIreal\fR value of each pixel is copied. Use the \fB\-pixel_values\fR to copy the pixel values instead. \fBNOTE\fR: This program is intended primarily for use with scripts such as \fBmincedit\fR. It does not follow the typical design rules of most MINC command-line tools and therefore should be used only with caution. .SH OPTIONS .TP \fB\-pixel_values\fR Copy voxel values in raw mode. .TP \fB\-real_values\fR Copy voxel intensities (default). .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print version number of program and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincexpand/0002755000175000000620000000000012574624760017104 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincexpand/mincexpand.man10000644000175000000620000000347712574624760022021 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1995 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincexpand/mincexpand.man1,v 6.1 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCEXPAND 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincexpand - expands a compressed minc file, if necessary. .SH SYNOPSIS .B mincexpand [] [] .SH DESCRIPTION \fIMincexpand\fR expands a compressed, packed, gzipped or zipped minc file into a temporary file using gunzip (or zcat or pcat) and prints out the name of the new file. If the input file is not compressed, then nothing is done and the original file name is printed. A second line is printed, indicating whether the name is that of a new temporary file ("Temporary") or that of the original file ("Original"). If no output file name is given, then the program generates its own. .SH OPTIONS .TP \fB\-header_only\fR Expand only enough of the file to be able to read the header. .TP \fB\-all_data\fR Expand the whole file (default). .TP \fB\-name_only\fR Print out only the file name, not the status (Temporary or Original). .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1995 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincexpand/mincexpand.c0000644000175000000620000001022012574624760021367 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincexpand @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to expand a compressed minc image volume, if necessary. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : * $Log: mincexpand.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:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.4 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.3 2004/12/14 23:51:24 bert * Get rid of compilation warning with c99 * * Revision 6.2 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.1 1999/10/19 14:45:22 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:16 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:15 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:02:02 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:32:41 neelin * Release of minc version 0.3 * * Revision 1.3 1995/01/24 09:06:19 neelin * Added -name_only option. * * Revision 1.2 95/01/24 08:48:57 neelin * Added optional output file argument. * * Revision 1.1 95/01/23 08:33:31 neelin * Initial revision * * Revision 1.1 95/01/20 15:51:32 neelin * Initial revision * @COPYRIGHT : Copyright 1995 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #define _GNU_SOURCE 1 #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Variables used for argument parsing */ int header_only = FALSE; int name_only = FALSE; /* Argument table */ ArgvInfo argTable[] = { {"-header_only", ARGV_CONSTANT, (char *) TRUE, (char *) &header_only, "Expand only enough file to get the header."}, {"-all_data", ARGV_CONSTANT, (char *) FALSE, (char *) &header_only, "Expand the whole file (default)."}, {"-name_only", ARGV_CONSTANT, (char *) TRUE, (char *) &name_only, "Print out only the file name."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *filename, *tempfile, *newfile; int created_tempfile; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2) || (argc > 3)) { (void) fprintf(stderr, "\nUsage: %s [] []\n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } filename = argv[1]; if (argc == 3) tempfile = argv[2]; else tempfile = NULL; /* Expand the file */ ncopts = 0; newfile = miexpand_file(filename, tempfile, header_only, &created_tempfile); if (newfile == NULL) newfile = strdup(filename); /* Print out file name and message about temporary file */ (void) printf("%s\n", newfile); if (!name_only) { if (created_tempfile) { (void) printf("Temporary\n"); } else { (void) printf("Original\n"); } } /* Free the temporary file name string */ free(newfile); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/mincstats/0002755000175000000620000000000012574624760016763 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincstats/mincstats.c0000644000175000000620000017610512574624760021144 0ustar stevestaff/* mincstats.c * * Andrew Janke - a.janke@gmail.com * Centre for Magnetic Resonance * University of Queensland, Australia * * $Log: mincstats.c,v $ * Revision 1.24 2007-12-11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 1.23 2007/09/27 01:06:31 rotor * * bugfix to histogram stats with a zero volume, discovered by Simon, fixed by * Claude * * Revision 1.22 2005/07/29 16:46:21 bert * Add warning message for mincstats -mask w/o -mask_range, -mask_binvalue, etc. * * Revision 1.21 2005/07/25 19:56:52 bert * Fix pct_T calculation by taking into account a possibly non-zero histogram floor * * Revision 1.20 2005/05/20 15:39:45 bert * Remove and/or conditionalize test code for memory-mapped files (see HDF5_MMAP_TEST) * * Revision 1.19 2004/12/14 23:39:36 bert * New bimodal threshold algorithms * * Revision 1.18 2004/12/06 15:28:50 rotor * * Hopefully the final bug-fix for the BiModalT calculation * * Revision 1.17 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 1.16 2004/10/18 08:20:35 rotor * * Changes to mincstats * - Fixed bug in calculation of BiModalT * - changed default # of histogram bins to 65536 (from 10000) * * Revision 1.15 2004/04/27 15:29:22 bert * Added milog_init() call during initialization * * Revision 1.14 2003/09/05 18:29:40 bert * Avoid passing NULL to fprintf when no mask file is specified, to avoid seg. faults reported by Richard Boyes. * * Revision 1.13 2003/08/20 05:52:55 rotor * * INDENTATION changes only (merging my and peter neelins code!) * * Revision 1.12 2003/08/20 05:45:10 rotor * * Fixed broken calculation of Median value from histogram. * * Revision 1.11 2002/09/05 00:41:57 rotor * ---------------------------------------------------------------------- * Fixed clash of C/L arguments in mincstats (-max and -max_bins) * -max_bins has now been changed to -int_max_bins * * Committing in . * * Modified Files: * mincstats.c * ---------------------------------------------------------------------- * * Revision 1.10 2002/04/08 21:46:34 jgsled * fixed problem where mincstats segmentation fault when trying to close a NULL file pointer * * Revision 1.9 2002/01/09 13:23:16 neelin * Removed extraneous newline for histogram output with -quiet turned on. * * Revision 1.8 2001/12/11 14:36:00 neelin * Added -discrete_histogram and -integer_histogram, as well as * -world_only options. * * Revision 1.7 2001/12/10 14:11:45 neelin * Obtained speed improvement by only doing CoM summing when needed. * * Revision 1.6 2001/12/06 21:54:25 neelin * Check for -quiet when printing volume and mask ranges. * * Revision 1.5 2001/12/06 21:48:16 neelin * Significant modifications to get mincstats working. Also added support * for multiple ranges in the volume and the mask. Added -binvalue and * -maskbinvalue options. * * Revision 1.4 2001/12/05 17:20:13 neelin * Lots of fixes to get it working. Also fixed up centre of mass calculation * and display. * * Revision 1.2 2001/11/28 21:59:39 neelin * Significant modifications. Removed dependencies on volume_io. * Added support for centre-of-mass calculation. * Compiles but crashes under linux. * * Revision 1.1 2001/11/28 21:54:08 neelin * *** empty log message *** * * * Thu Feb 1 17:16:21 EST 2001 - completed filename checking and other * mundane stuff - first release 1.0 * Wed Jan 31 14:33:30 EST 2001 - finished -entropy, -median and -histogram * Fri Jan 19 15:25:44 EST 2001 - created first version from minccount as a * mirror of Alex Zijdenbos + John Sleds * volume_stats proggy with less memory * overhead * Original version - 1999 sometime.. * * A few notes on the stats in here. * Median - This is a "histogram median" based upon calculating * the volume of histogram above and below the median * Thus the more bins the more accurate the approximation * Majority - This is the centre of the largest bin in the histogram * BiModalT - The Bi-Modal Threshold calculated using the method described in * Otsu N, "A Threshold Selection Method from Grey-level Histograms" * IEEE Trans on Systems, Man and Cybernetics. 1979, 9:1;62-66. * Entropy - This is what is called "Shannon Entropy" of a histogram * H(x) = - Sum(P(i) * log2(P(i)) * Where P(i) is the bin probability * PctT - The threshold needed for a particular "Critical percentage" of * of a histogram. */ #include "config.h" #include #include #if HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define SQR(x) ((x)*(x)) #define CUBE(x) ((x)*(x)*(x)) #define QUAD(x) ((x)*(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]; double *histogram; double hvoxels; double vvoxels; double volume; double vol_per; double hist_per; double min; double max; double sum; double sum2; double mu; /* current estimate of mean. */ double M2; /* for calculation of second moment */ double M3; /* for calculation of third moment */ double M4; /* for calculation of fourth moment */ 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(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info); void do_stats(double value, long index[], Stats_Info * stats); void print_result(char *title, double result); long get_minc_nvoxels(int mincid); double get_minc_voxel_volume(int mincid); void get_minc_attribute(int mincid, char *varname, char *attname, int maxvals, double vals[]); int get_minc_ndims(int mincid); void find_minc_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]); void get_minc_voxel_to_world(int mincid, double voxel_to_world[WORLD_NDIMS][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 Skewness = FALSE; static int Kurtosis = 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; /* Alternative methods of calculating the bimodal threshold */ #define BMT_OTSU 1 /* Otsu algorithm (default) */ #define BMT_KITTLER 2 /* Kittler-Illingworth algorithm */ #define BMT_KAPUR 3 /* Kapur et al. algorithm */ #define BMT_SIMPLE 4 /* Simple mean-of-means, citation unknown */ static int BMTMethod = BMT_OTSU; 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 = 65536; /* 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[MAX_VAR_DIMS]; int file_ndims = 0; /* Argument table */ static 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."}, {"-skewness", ARGV_CONSTANT, (char *)TRUE, (char *)&Skewness, "sample skewness (3rd moment)"}, {"-kurtosis", ARGV_CONSTANT, (char *)TRUE, (char *)&Kurtosis, "sample kurtosis (4th moment)"}, {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."}, {"-otsu", ARGV_CONSTANT, (char *)BMT_OTSU, (char *)&BMTMethod, "Use Otsu '97 algorithm for bimodal threshold (default)"}, {"-kittler", ARGV_CONSTANT, (char *)BMT_KITTLER, (char *)&BMTMethod, "Use Kittler&Illingworth '86 algorithm for bimodal threshold"}, {"-kapur", ARGV_CONSTANT, (char *)BMT_KAPUR, (char *)&BMTMethod, "Use Kapur et al. '85 algorithm for bimodal threshold"}, {"-simple", ARGV_CONSTANT, (char *)BMT_SIMPLE, (char *)&BMTMethod, "Use simple mean-of-means algorithm for bimodal threshold"}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Alternative thresholding algorithm. This is more computationally * expensive than some of the alternatives, and doesn't seem to do a * great job. On the other hand it doesn't seem to fail like the * current algorithm. */ static double simple_threshold(double *histogram, double *hist_centre, int hist_bins) { double sum1, sum2; double mean1, mean2; double testthreshold; double newthreshold; int newthreshold_bin; double count1, count2; int c; /* Start with a guess of the bimodal threshold. */ newthreshold = ceil(hist_centre[hist_bins / 2]); newthreshold_bin = hist_bins / 2; for (;;) { sum1 = 0.0; sum2 = 0.0; count1 = 0.0; count2 = 0.0; /* Calculate the mean of the bins on each side of the * proposed threshold. */ for (c = 0; c < newthreshold_bin; c++) { sum1 += (hist_centre[c] * histogram[c]); count1 += histogram[c]; } for (c = newthreshold_bin; c < hist_bins; c++) { sum2 += (hist_centre[c] * histogram[c]); count2 += histogram[c]; } /* Avoid divide by zero */ if (count1 == 0.0 || count2 == 0.0) { continue; } mean1 = sum1 / count1; mean2 = sum2 / count2; /* The new threshold is the mean of the means. */ testthreshold = ceil((mean1 + mean2) / 2); /* If the threshold is unchanged, that is our final * guess. */ if (newthreshold == testthreshold) { break; /* Return result */ } else { /* Adopt the new guess and try again until we converge. */ newthreshold = testthreshold; for (c = 0; c < hist_bins; c++) { if (newthreshold == ceil(hist_centre[c])) { newthreshold_bin = c; break; } } } } return (newthreshold); } /** This copyright applies to the following functions: * * otsu_threshold() * kittler_threshold() * kapur_threshold() * * These functions were extracted from the "xite" package from this * source. The functions were extensively modified, however, by me * to generalize the functions for our purposes. Any bugs are * therefore my responsibility (bert 2004-12-14). * * Copyright 1990, Blab, UiO * Image processing lab, Department of Informatics * University of Oslo * E-mail: blab@ifi.uio.no *________________________________________________________________ * * Permission to use, copy, modify and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that this copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation and that the name of B-lab, * Department of Informatics or University of Oslo not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * B-LAB DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL B-LAB BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Otsu, N. "A threshold selection method from gray-level histograms", * IEEE Transactions on Systems, Man, and Cybernetics, vol T-SMC 9, * No 1, pp 62-66, 1979. */ static double otsu_threshold(const double histo[], const double hist_centre[], int hist_bins) { double threshold; double criterion; double expr_1; /* Temporary for common subexpression */ int i, k; /* Generic loop counters */ long omega_k; double sigma_b_k; double mu_T; double mu_k; long sum; int k_low, k_high; /* Ignore outlying zero bins */ for (k_low = 0; (histo[k_low] <= 0.0) && (k_low < hist_bins-1); k_low++) ; for (k_high = hist_bins-1; (histo[k_high] <= 0) && (k_high > 0); k_high--) ; sum = 0L; mu_T = 0.0; for (i = k_low; i <= k_high; i++) { sum += histo[i]; mu_T += hist_centre[i] * histo[i]; } mu_T /= (double)sum; criterion = 0.0; threshold = hist_centre[(k_high - k_low + 1 ) / 2]; omega_k = 0L; mu_k = 0.0; for (k = k_low; k <= k_high ; k++) { omega_k += (long)histo[k]; if( omega_k == 0L || omega_k >= sum ) continue; mu_k += hist_centre[k] * histo[k]; expr_1 = mu_T * omega_k - mu_k; sigma_b_k = expr_1 * expr_1 / ( (double) omega_k * ( sum - omega_k ) ); if (criterion < sigma_b_k) { criterion = sigma_b_k; threshold = hist_centre[k]; } } return threshold; } /* Kittler, J. & Illingworth J., "Minimum error thresholding", Pattern * Recognition, vol 19, pp 41-47, 1986. */ static double kittler_threshold (double hist_bin[], double hist_centre[], int hist_size) { double threshold; double criterion; int g; double n; int T_low, T_high; double P_1_T, P_2_T, P_tot; double mu_1_T, mu_2_T; double sum_gh_1, sum_gh_2, sum_gh_tot; double sum_ggh_1, sum_ggh_2, sum_ggh_tot; double sigma_1_T, sigma_2_T; double J_T; criterion = 1e10; threshold = hist_centre[hist_size / 2 + 1]; J_T = criterion; T_low = 0; while ((hist_bin[T_low] == 0) && (T_low < hist_size - 1)) { T_low++; } T_high = hist_size - 1; while ((hist_bin[T_high] == 0) && (T_high > 0)) { T_high--; } n = 0; for (g = T_low; g <= T_high; g++) { n += hist_bin[g]; } P_1_T = hist_bin[T_low]; P_tot = 0; for (g = T_low; g <= T_high; g++) { P_tot += hist_bin[g]; } sum_gh_1 = hist_centre[T_low] * hist_bin[T_low]; sum_gh_tot = 0.0; for (g = T_low; g <= T_high; g++) { sum_gh_tot += hist_centre[g] * hist_bin[g]; } sum_ggh_1 = hist_centre[T_low] * hist_centre[T_low] * hist_bin[T_low]; sum_ggh_tot = 0.0; for (g = T_low; g <= T_high; g++) { sum_ggh_tot += hist_centre[g] * hist_centre[g] * hist_bin[g]; } for (g = T_low + 1; g < T_high - 1; g++) { P_1_T += hist_bin[g]; P_2_T = P_tot - P_1_T; sum_gh_1 += hist_centre[g] * hist_bin[g]; sum_gh_2 = sum_gh_tot - sum_gh_1; mu_1_T = sum_gh_1 / P_1_T; mu_2_T = sum_gh_2 / P_2_T; sum_ggh_1 += hist_centre[g] * hist_centre[g] * hist_bin[g]; sum_ggh_2 = sum_ggh_tot - sum_ggh_1; sigma_1_T = sum_ggh_1 / P_1_T - mu_1_T * mu_1_T; sigma_2_T = sum_ggh_2 / P_2_T - mu_2_T * mu_2_T; /* Equation (15) in the article */ if ((sigma_1_T != 0.0) && (P_1_T != 0) && (sigma_2_T != 0.0) && (P_2_T != 0)) { J_T = 1 + 2 * (P_1_T * log(sigma_1_T) + P_2_T * log(sigma_2_T)) - 2 * (P_1_T * log(P_1_T) + P_2_T * log(P_2_T) ); } if (criterion > J_T) { criterion = J_T; threshold = hist_centre[g]; } } return threshold; } /* Kapur, Sahoo & Wong "A new method for Gray-level picture thresholding using the entropy of the histogram", Computer Vision, Graphics, and Image Processing, vol 29, pp 273-285, 1985. */ #define BIN_TINY 1e-6 static double kapur_threshold(double histo[], double hist_centre[], int hist_bins) { double threshold; double Phi, Phi_max; int i, k; double *p = malloc(sizeof(double) * hist_bins); double *P = malloc(sizeof(double) * hist_bins); double *H = malloc(sizeof(double) * hist_bins); double sum; sum = 0; for (i = 0; i < hist_bins; i++) { sum += histo[i]; } for (i = 0; i < hist_bins; i++) { p[i] = histo[i] * 1.0 / sum; } P[0] = p[0]; for (i = 1; i < hist_bins; i++) { P[i] = P[i - 1] + p[i]; } if (histo[0] == 0) { H[0] = 0; } else { H[0] = -p[0] * log(p[0]); } for (i = 1; i < hist_bins; i++) { if (histo[i] == 0) { H[i] = H[i - 1]; } else { H[i] = H[i - 1] - p[i] * log(p[i]); } } Phi_max = -10e10; threshold = hist_centre[hist_bins / 2]; for (k = 0; k <= hist_bins - 1; k++) { if ((P[k] > BIN_TINY) && (1 - P[k] > BIN_TINY)) { Phi = log(P[k] * (1 - P[k])) + H[k] / P[k] + (H[hist_bins - 1] - H[k]) / (1.0 - P[k]); if (Phi_max < Phi) { Phi_max = Phi; threshold = hist_centre[k]; } } } free(p); free(P); free(H); return threshold; } int main(int argc, char *argv[]) { char **infiles; int nfiles; Loop_Options *loop_options; int mincid, imgid; int idim; int irange, imask; double real_range[2], valid_range[2]; nc_type datatype; int is_signed; double voxel_to_world[WORLD_NDIMS][WORLD_NDIMS + 1]; Stats_Info *stats; FILE *FP; double scale, voxmin, voxmax; milog_init(argv[0]); /* 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; if (mask_file != NULL && num_masks == 1 && *mask_min.values == -DBL_MAX && *mask_max.values == DBL_MAX) { fprintf(stderr, "%s: Warning: Mask specified without a range. Mask will be ignored.\n", argv[0]); } /* 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 && !Skewness && !Kurtosis) { 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], 0) != 0) { (void)fprintf(stderr, "%s: Couldn't find %s\n", argv[0], infiles[0]); exit(EXIT_FAILURE); } if(infiles[1] != NULL && access(infiles[1], 0) != 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, 0) != -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 */ mincid = miopen(infiles[0], NC_NOWRITE); imgid = ncvarid(mincid, MIimage); nvoxels = get_minc_nvoxels(mincid); voxel_volume = get_minc_voxel_volume(mincid); (void)miget_datatype(mincid, imgid, &datatype, &is_signed); (void)miget_image_range(mincid, real_range); (void)miget_valid_range(mincid, imgid, valid_range); file_ndims = get_minc_ndims(mincid); find_minc_spatial_dims(mincid, space_to_dim, dim_to_space); get_minc_voxel_to_world(mincid, 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(datatype == NC_FLOAT || datatype == NC_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 -int_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]; } } /* Do math */ loop_options = create_loop_options(); set_loop_first_input_mincid(loop_options, mincid); set_loop_verbose(loop_options, verbose); set_loop_buffer_size(loop_options, (long)1024 * max_buffer_size_in_kb); voxel_loop(nfiles, infiles, 0, NULL, NULL, loop_options, do_math, NULL); free_loop_options(loop_options); /* 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; double *hist_centre; double *pdf; /* probability density Function */ double *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 = calloc(hist_bins, sizeof(double)); pdf = calloc(hist_bins, sizeof(double)); cdf = calloc(hist_bins, sizeof(double)); 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 */ zero_moment += pdf[c]; first_moment += hist_centre[c] * pdf[c]; if(c > 0 && zero_moment > 0.0 && zero_moment < 1.0) { 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; } stats->pct_T += hist_centre[0]; /* Add histogram minimum */ switch (BMTMethod) { case BMT_KITTLER: stats->biModalT = kittler_threshold(stats->histogram, hist_centre, hist_bins); break; case BMT_KAPUR: stats->biModalT = kapur_threshold(stats->histogram, hist_centre, hist_bins); break; case BMT_SIMPLE: stats->biModalT = simple_threshold(stats->histogram, hist_centre, hist_bins); break; default: stats->biModalT = otsu_threshold(stats->histogram, hist_centre, hist_bins); break; } /* 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 %ld\n", hist_centre[c], (long)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 (Skewness || Kurtosis) { /* Use the online estimate of the standard deviation for * consistency. This gives results nearly identical to * Octave 4.0.0 and Matlab R2014a. */ double sd = sqrt(stats->M2 / stats->vvoxels); if (Skewness) { print_result("Skewness: ", stats->M3 / (stats->vvoxels * CUBE(sd))); } if (Kurtosis) { print_result("Kurtosis: ", stats->M4 / (stats->vvoxels * QUAD(sd))); } } 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(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info) /* ARGSUSED */ { long ivox; long index[MAX_VAR_DIMS]; 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)) { get_info_voxel_index(loop_info, ivox, file_ndims, index); 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++) { get_info_voxel_index(loop_info, ivox, file_ndims, index); 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; } /** * Calculate second, third, and fourth moments. * Relations derived from: Technical report: SAND2008-6212, * "Formulas for Robust, One-Pass Parallel Computation of * Covariances and Arbitrary-Order Statistical Moments", * Philippe Pebay, Sandia National Laboratories, pppebay@sandia.gov * http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf */ void update_moments(Stats_Info *stats, double value) { double n = stats->vvoxels; double n1 = stats->vvoxels - 1; double delta = value - stats->mu; double mu = stats->mu + delta / n; double M2 = stats->M2 + delta * (value - mu); double M3 = stats->M3 + (n1 * (n1 - 1.0) * CUBE(delta) / SQR(n) - 3.0 * stats->M2 * delta / n); double M4 = stats->M4 + (n1 * (SQR(n1) - n1 + 1.0) * QUAD(delta) / CUBE(n) + 6.0 * stats->M2 * SQR(delta) / SQR(n) - 4.0 * stats->M3 * delta / n); stats->mu = mu; stats->M2 = M2; stats->M3 = M3; stats->M4 = M4; } 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 (Kurtosis || Skewness) { update_moments(stats, 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]) && (hist_sep > 0.0) ) { /*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(int mincid) { int imgid, dim[MAX_VAR_DIMS]; int idim, ndims; long nvoxels, length; /* Get the dimension ids for the image variable */ imgid = ncvarid(mincid, MIimage); (void)ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); /* Loop over them to get the total number of voxels */ nvoxels = 1; for(idim = 0; idim < ndims; idim++) { (void)ncdiminq(mincid, dim[idim], NULL, &length); nvoxels *= length; } return nvoxels; } /* Get the volume of a spatial voxel */ double get_minc_voxel_volume(int mincid) { int imgid, dim[MAX_VAR_DIMS]; int idim, ndims; double volume, step; char dimname[MAX_NC_NAME]; /* Get the dimension ids for the image variable */ imgid = ncvarid(mincid, MIimage); (void)ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); /* Loop over them to get the total spatial volume */ volume = 1.0; for(idim = 0; idim < ndims; idim++) { /* Get the name and check that this is a spatial dimension */ (void)ncdiminq(mincid, dim[idim], dimname, NULL); if((dimname[0] == '\0') || (strcmp(&dimname[1], "space") != 0) || !(dimname[0] == 'x' || dimname[0] == 'y' || dimname[0] == 'z')) { continue; } /* Get the step attribute of the coordinate variable */ step = 1.0; get_minc_attribute(mincid, dimname, MIstep, 1, &step); /* Make sure that it is positive and calculate the volume */ if(step < 0.0) step = -step; volume *= step; } return volume; } /* Get a double attribute from a minc file */ void get_minc_attribute(int mincid, char *varname, char *attname, int maxvals, double vals[]) { int varid; int old_ncopts; int att_length; if(!mivar_exists(mincid, varname)) return; varid = ncvarid(mincid, varname); old_ncopts = ncopts; ncopts = 0; (void)miattget(mincid, varid, attname, NC_DOUBLE, maxvals, vals, &att_length); ncopts = old_ncopts; } /* Get the total number of image dimensions in a minc file */ int get_minc_ndims(int mincid) { int imgid; int ndims; imgid = ncvarid(mincid, MIimage); (void)ncvarinq(mincid, imgid, NULL, NULL, &ndims, NULL, NULL); return ndims; } /* Get the mapping from spatial dimension - x, y, z - to file dimensions and vice-versa. */ void find_minc_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]) { int imgid, dim[MAX_VAR_DIMS]; int idim, ndims, world_index; char dimname[MAX_NC_NAME]; /* Set default values */ for(idim = 0; idim < 3; idim++) space_to_dim[idim] = -1; for(idim = 0; idim < MAX_VAR_DIMS; idim++) dim_to_space[idim] = -1; /* Get the dimension ids for the image variable */ imgid = ncvarid(mincid, MIimage); (void)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 */ (void)ncdiminq(mincid, dim[idim], dimname, NULL); if((dimname[0] == '\0') || (strcmp(&dimname[1], "space") != 0)) { 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; } } /* Get the voxel to world transform (for column vectors) */ void get_minc_voxel_to_world(int mincid, double voxel_to_world[WORLD_NDIMS][WORLD_NDIMS + 1]) { int idim, jdim; double dircos[WORLD_NDIMS]; double step, start; char *dimensions[] = { MIxspace, MIyspace, MIzspace }; /* Zero the matrix */ for(idim = 0; idim < WORLD_NDIMS; idim++) { for(jdim = 0; jdim < WORLD_NDIMS + 1; jdim++) voxel_to_world[idim][jdim] = 0.0; } for(jdim = 0; jdim < WORLD_NDIMS; jdim++) { /* Set default values */ step = 1.0; start = 0.0; for(idim = 0; idim < WORLD_NDIMS; idim++) dircos[idim] = 0.0; dircos[jdim] = 1.0; /* Get the attributes */ get_minc_attribute(mincid, dimensions[jdim], MIstart, 1, &start); get_minc_attribute(mincid, dimensions[jdim], MIstep, 1, &step); get_minc_attribute(mincid, dimensions[jdim], MIdirection_cosines, WORLD_NDIMS, dircos); normalize_vector(dircos); /* Put them in the matrix */ for(idim = 0; idim < WORLD_NDIMS; idim++) { voxel_to_world[idim][jdim] = step * dircos[idim]; voxel_to_world[idim][WORLD_NDIMS] += start * dircos[idim]; } } } 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 < 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 < 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 = calloc(hist_bins, sizeof(double)); 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->mu = 0.0; stats->M2 = 0.0; stats->M3 = 0.0; stats->M4 = 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); } minc-tools-2.3.00+dfsg/progs/mincstats/mincstats.man10000644000175000000620000002630212574624760021547 0ustar stevestaff.\" Copyright 2001 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. .\" .TH MINCSTATS 1 "$Date: 2004-05-20 21:52:09 $" "" "MINC User's Guide" .SH NAME mincstats - calculate simple statistics across voxels of a minc file .SH SYNOPSIS .B mincstats [] .mnc .SH DESCRIPTION \fIMincstats\fR will calculate simple statistical measures across all voxels of a minc file. Note that these are global statistical measures and not voxel-by-voxel measures (see \fImincaverage\fR for that). By default all statistics are calculated. If any statistics are requested via a command-line option, then only the requested statistics are printed. A very useful feature of this program is the ability to restrict the set of voxels included in the statistic calculation, either by restricting the range of included values, or by using a mask file with a restricted range. Multiple ranges for the input file or mask file can be specified. For each range of included volume values, and for each range of mask values, the relevant statistics are printed out (n*m values, where n is the number of volume ranges and m the number of mask ranges). These calculations are done in a single pass through the data, so specifying multiple ranges is much faster than running the program repeatedly. This is quite helpful when calculating many regional averages with a VOI mask volume. Special mention should be given to histograms and related statistical measures. The default range of the histogram is from the smallest value in the file to the largest. In the not uncommon, but special, case when the number of histogram bins exactly matches the number of possible values in the file (e.g. 256 bins for full-range byte data), the histogram can end up with some odd features when using the default histogram range. This arises from the discretization of the data that are then rebinned into a slightly mismatched histgram. For the example of byte data, the values that should be used are 256 bins and a histogram range that extends half a bin below the smallest value and half a bin above the largest. Use option \fB-discrete_histogram\fR to work this out automatically, or use \fB-integer_histogram\fR to have bins of unit width if the input data are inherently integer (e.g. label data). In general, one should be careful about the rebinning of discretized data to a histogram with a bin size that is close to the level of discretization. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. The order in which the statistics are printed will be always the same irrespective or the order in which they are requested on the command line .SH General options .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-verbose\fR Print out extra information (more than the default). .TP \fB\-quiet\fR Print out only the requested numbers .TP \fB\-max_buffer_size_in_kb\fR\ \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4 MB. .SH Invalid value options .TP \fB\-ignore_nan\fR Exclude invalid values (outside valid range) from statistic calculations. This is the default. .TP \fB\-include_nan\fR Treat invalid values as zeros and include them in statistic calculations. .TP \fB\-replace_nan\fR\ \fIvalue\fR Replace invalid values with the specified value and include the new value in statistic calculations. .SH Volume range options .TP \fB\-floor\fR\ \fImin1\fR,\fImin2\fR,... Comma-separated list of lower bounds for ranges of data to include in statistic calculation. .TP \fB\-ceil\fR\ \fImax1\fR,\fImax2\fR,... Comma-separated list of upper bounds for ranges of data to include in statistic calculation. .TP \fB\-range\fR\ \fImin1\fR,\fImax1\fR,\fImin2\fR,\fImax2\fR,... Comma-separated list of lower and upper bounds for ranges of data to include in statistic calculation. .TP \fB\-binvalue\fR \fIval1\fR,\fIval2\fR,... Comma-separated list of integer values to include in statistic calculation. A range of +/- 0.5 is defined around each specified value. .TP \fB\-mask\fR\ \fIfilename.mnc\fR Name of file to be used for masking data included in statistic calculation. For this to have any effect, you must specify a mask range with one of the following options. .TP \fB\-mask_floor\fR\ \fImin1\fR,\fImin2\fR,...: Like \fB\-floor\fR, but applied to the mask file. .TP \fB\-mask_ceil\fR\ \fImax1\fR,\fImax2\fR,... Like \fB\-ceil\fR, but applied to the mask file. .TP \fB\-mask_range\fR\ \fImin1\fR,\fImax1\fR,\fImin2\fR,\fImax2\fR,... Like \fB\-range\fR, but applied to the mask file. .TP \fB\-mask_binvalue\fR\ \fIval1\fR,\fIval2\fR,... Like \fB\-binvalue\fR, but applied to the mask file. .SH Histogram options .TP \fB\-histogram\fR\ \fIfilename\fR Specify the name of a file into which the histogram is written. If multiple ranges or mask ranges are specified, then all histograms are written in this file, separated by blank lines. Information describing each histogram is written before it in lines starting with the hash (pound) character. These files can be loaded into gnuplot. .TP \fB\-hist_bins\fR \fInumber-of-bins\fR Specify the number of bins in the histogram. .TP \fB-bins\fR \fInumber-of-bins\fR Synonym for \fB\-hist_bins\fR. .TP \fB\-hist_floor\fR\ \fImin\fR Specify lower bound for histogram. .TP \fB\-hist_ceil\fR\ \fImax\fR Specify upper bound for histogram. .TP \fB\-hist_range\fR\ \fImin\fR\ \fImax\fR Specify a range for the histogram .TP \fB\-integer_histogram\fR Create bins of unit width, centred around integer values. This is useful for integer data such as labels. The histogram range is rounded to the nearest integer, then the min is lowered and the max is raised by 0.5. The number of bins is taken as the difference of these two values. Note that 0.01 is added to the minimum and subtracted from the maximum prior to the rounding in order to ensure that a correctly specified range (e.g. [0.5,255.5]) is preserved. If you want to have integer bins that are wider than one, you will have to work out the histogram range and number of bins yourself and not use this option. .TP \fB\-discrete_histogram\fR Attempt to match the histogram to the discretization of the input data. This is appropriate for continuous data that are stored in an integer representation and when a bin width close to the discretization is desired. This is similar to \fB\-integer_histogram\fR, except that the the histogram range is first converted to voxel values which are rounded and extended by half a bin on either side. This new voxel range is then converted back to real values. The number of bins is taken as the difference in the voxel value range. Note that this does not account for variations in slice-to-slice scaling, so odd histogram effects may still occur. This option is intended to give behaviour similar to that of \fIvolume_stats\fR. .TP \fB\-int_max_bins\fR \fInumber-of-bins\fR Specify the largest histogram that can be automatically sized with the above options. The limit prevents accidental creation of huge histograms. This option replaced the old \fB-max_bins\fR option in MINC 1.1. .SH Basic statistics .TP \fB\-all\fR Compute all statistical measures. This is the default. .TP \fB\-none\fR Synonym for \fB\-count\fR (for similarity to volume_stats). Note that although this was necessary for \fIvolume_stats\fR, it is not needed here, since specifying any of these options automatically turns off \fB\-all\fR .TP \fB\-count\fR Count the number of voxels that are within the range and mask. .TP \fB\-percent\fR Print the percentage of voxels within the range and mask .TP \fB\-volume\fR Print the volume of the voxels within the range and mask (in mm-cubed). .TP \fB\-min\fR Print the minimum value. .TP \fB\-max\fR Print the maximum value. .TP \fB\-sum\fR Print the sum of all values. .TP \fB\-sum2\fR Print the sum of the squares of all values. .TP \fB\-mean\fR Print the mean. .TP \fB\-variance\fR Print the variance. .TP \fB\-stddev\fR Print the standard deviation. .TP \fB\-skewness\fR Print the sample skewness (3rd moment) . .TP \fB\-kurtosis\fR Print the sample kurtosis (4th moment) . .TP \fB\-CoM\fR Print the centre of mass. Both the voxel coordinate and the world coordinates are printed. The voxel coordinates are printed in file order, whilst the world coordinates are printed in x,y,z order. .TP \fB\-com\fR Synonym for \fB\-CoM\fR. .TP \fB\-world_only\fR Print the centre of mass in world coordinates only. .SH Histogram statistics .P Note that histogram statistics are derived solely from the histogram counts and bin centres, so results such as the median will not be exactly the same as the true value for all included voxels. For example, the error on the median can be as large as a half bin width. Furthermore, if the histogram range is less than that of included voxels, then the result applies only to voxels included in the histogram. .TP \fB\-hist_count\fR Print number of voxels in histogram. This may be different from the number of included and masked voxels if the histogram range is less than the range of the included data. .TP \fB\-hist_percent\fR Print percentage of voxels included in histogram. .TP \fB\-median\fR Print the histogram median. .TP \fB\-majority\fR Print the bin centre (intensity value) for the bin with the most counts. .TP \fB\-biModalT\fR Print the bi-modal threshold separating the volume into two classes The default is to use the otsu method (see options below) .TP \fB\-otsu\fR Use the method described in Otsu N, "A Threshold Selection Method from Grey-level Histograms", IEEE Trans on Systems, Man and Cybernetics. 1979, 9:1; 62-66 to calculate the threshold .TP \fB\-kittler\fR Use the Kittler&Illingworth '86 algorithm to calculate the for bimodal threshold. Kittler, J. & Illingworth J., "Minimum error thresholding", Pattern Recognition, vol 19, pp 41-47, 1986. .TP \fB\-kapur\fR Use the Kapur et al. '85 algorithm to calculate the bimodal threshold. Kapur, Sahoo & Wong "A new method for Gray-level picture thresholding using the entropy of the histogram", Computer Vision, Graphics, and Image Processing, vol 29, pp 273-285, 1985. .TP \fB\-simple\fR Use simple mean-of-means algorithm to calculate the bimodal threshold This is more computationally expensive than some of the alternatives, and doesn't seem to do a great job. But it does seem more robust than some of the other methods. .TP \fB\-pctT\fR Print the threshold needed for a particular critical percentage of the histogram. .TP \fB\-entropy\fR Print the Shannon entropy. H(x) = - Sum(P(i) * log2(P(i)) where P(i) is the bin probability .SH Generic options for all commands: .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Andrew Janke .SH COPYRIGHTS .P Program: Copyright \(co 2000 by Andrew Janke .P Man page: Copyright \(co 2001 by Peter Neelin minc-tools-2.3.00+dfsg/progs/minclookup/0002755000175000000620000000000012574624760017136 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minclookup/minclookup.c0000644000175000000620000010111712574624760021461 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minclookup @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to perform lookup table operations on a scalar minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : * $Log: minclookup.c,v $ * Revision 6.10 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.9 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.8 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.7 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.6 2004/12/03 21:57:08 bert * Include config.h * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2004/05/20 21:52:08 bert * Revised man pages * * Revision 6.3 2001/04/24 13:38:43 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.2 2001/04/17 18:40:20 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:24 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:13 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:12 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:47 neelin * Release of minc version 0.4 * * Revision 3.2 1996/07/10 16:58:37 neelin * Added -lut_string option and added special handling of duplicated first * or last entries. * * Revision 3.1 1996/07/10 14:38:03 neelin * Added options to set output file type, sign and range. * * Revision 3.0 1995/05/15 19:32:39 neelin * Release of minc version 0.3 * * Revision 1.6 1995/03/21 14:26:42 neelin * changed usage message. * * Revision 1.5 1995/03/21 14:00:10 neelin * Changed calls to voxel_loop routines. * * Revision 1.4 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 1.3 1994/12/14 09:22:30 neelin * Removed debugging code. * * Revision 1.2 94/12/13 16:34:02 neelin * Added sorting of lookup file. * * Revision 1.1 94/12/09 15:31:00 neelin * Initial revision * ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define DEFAULT_RANGE DBL_MAX #define NCOPTS_DEFAULT NC_VERBOSE | NC_FATAL /* Types */ typedef enum {LU_TABLE, LU_GRAY, LU_HOTMETAL, LU_SPECTRAL} Lookup_Type; /* Lookup table structure */ typedef struct { int nentries; int vector_length; double *table; int free_data; } Lookup_Table; /* Structure for lookup information */ typedef struct { Lookup_Table *lookup_table; double *null_value; int invert; int discrete; double range[2]; } Lookup_Data; /* Structure for sorting the lookup table */ typedef struct { double key; int index; } Sort_Key; /* Function prototypes */ static Lookup_Table *read_lookup_table(char *lookup_file, char *lookup_string); static double *get_values_from_string(char *string, int array_size, double *array, int *nread); static double *get_null_value(int vector_length, char *null_value_string); static void get_full_range(int mincid, double lookup_range[2]); static void do_lookup(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void lookup_in_table(double index, Lookup_Table *lookup_table, int discrete_values, double null_value[], double output_value[]); static char *get_next_line(char *line, int linelen, FILE *fp, char **string); static int sorting_function(const void *value1, const void *value2); /* Lookup tables */ static double gray_lookup_values[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 }; static Lookup_Table gray_lookup = { sizeof(gray_lookup_values)/sizeof(gray_lookup_values[0])/4, 3, gray_lookup_values, FALSE }; static double spectral_lookup_values[] = { 0.00, 0.0000,0.0000,0.0000, 0.05, 0.4667,0.0000,0.5333, 0.10, 0.5333,0.0000,0.6000, 0.15, 0.0000,0.0000,0.6667, 0.20, 0.0000,0.0000,0.8667, 0.25, 0.0000,0.4667,0.8667, 0.30, 0.0000,0.6000,0.8667, 0.35, 0.0000,0.6667,0.6667, 0.40, 0.0000,0.6667,0.5333, 0.45, 0.0000,0.6000,0.0000, 0.50, 0.0000,0.7333,0.0000, 0.55, 0.0000,0.8667,0.0000, 0.60, 0.0000,1.0000,0.0000, 0.65, 0.7333,1.0000,0.0000, 0.70, 0.9333,0.9333,0.0000, 0.75, 1.0000,0.8000,0.0000, 0.80, 1.0000,0.6000,0.0000, 0.85, 1.0000,0.0000,0.0000, 0.90, 0.8667,0.0000,0.0000, 0.95, 0.8000,0.0000,0.0000, 1.00, 0.8000,0.8000,0.8000 }; static Lookup_Table spectral_lookup = { sizeof(spectral_lookup_values)/sizeof(spectral_lookup_values[0])/4, 3, spectral_lookup_values, FALSE }; static double hotmetal_lookup_values[] = { 0.00, 0.0, 0.0, 0.0, 0.25, 0.5, 0.0, 0.0, 0.50, 1.0, 0.5, 0.0, 0.75, 1.0, 1.0, 0.5, 1.00, 1.0, 1.0, 1.0 }; static Lookup_Table hotmetal_lookup = { sizeof(hotmetal_lookup_values)/sizeof(hotmetal_lookup_values[0])/4, 3, hotmetal_lookup_values, FALSE }; /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = FALSE; static double valid_range[2] = {0.0, 0.0}; static int buffer_size = 10 * 1024; static char *lookup_file = NULL; static char *lookup_string = NULL; static Lookup_Type lookup_type = LU_GRAY; static int invert_table = FALSE; static double lookup_range[2] = {DEFAULT_RANGE, DEFAULT_RANGE}; static double lookup_min = DEFAULT_RANGE; static double lookup_max = DEFAULT_RANGE; static int discrete_lookup = FALSE; static char *null_value_string = NULL; #if MINC2 static int minc2_format = FALSE; #endif /* MINC2 */ /* Argument table */ ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format, "Produce a MINC 2.0 format output file"}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-buffer_size", ARGV_INT, (char *) 1, (char *) &buffer_size, "Set the internal buffer size (in kb)."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {"-gray", ARGV_CONSTANT, (char *) LU_GRAY, (char *) &lookup_type, "Use a grayscale lookup table (default)."}, {"-grey", ARGV_CONSTANT, (char *) LU_GRAY, (char *) &lookup_type, "Use a grayscale lookup table."}, {"-hotmetal", ARGV_CONSTANT, (char *) LU_HOTMETAL, (char *) &lookup_type, "Use a hot metal lookup table."}, {"-spectral", ARGV_CONSTANT, (char *) LU_SPECTRAL, (char *) &lookup_type, "Use a spectral lookup table."}, {"-invert", ARGV_CONSTANT, (char *) TRUE, (char *) &invert_table, "Invert the lookup table (only applies to -continuous)."}, {"-noinvert", ARGV_CONSTANT, (char *) FALSE, (char *) &invert_table, "Don't invert the lookup table."}, {"-range", ARGV_FLOAT, (char *) 2, (char *) lookup_range, "Min and max for lookup table (default from file)."}, {"-minimum", ARGV_FLOAT, (char *) 1, (char *) &lookup_min, "Minimum for continuous lookup table."}, {"-maximum", ARGV_FLOAT, (char *) 1, (char *) &lookup_max, "Maximum for continuous lookup table."}, {"-lookup_table", ARGV_STRING, (char *) 1, (char *) &lookup_file, "File containing the lookup table (use - for stdin)."}, {"-lut_string", ARGV_STRING, (char *) 1, (char *) &lookup_string, "String containing the lookup table, with \";\" to separate lines."}, {"-discrete", ARGV_CONSTANT, (char *) TRUE, (char *) &discrete_lookup, "Lookup table has discrete (integer) entries - range is ignored."}, {"-continuous", ARGV_CONSTANT, (char *) FALSE, (char *) &discrete_lookup, "Lookup table is continuous from 0 to 1 (default)."}, {"-null_value", ARGV_STRING, (char *) 1, (char *) &null_value_string, "Specify a vector value for entries missing from a discrete lookup."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *infile, *outfile; char *arg_string; Loop_Options *loop_options; int inmincid; Lookup_Data lookup_data; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { (void) fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* Get the appropriate lookup table */ if ((lookup_file != NULL) || (lookup_string != NULL)) { lookup_data.lookup_table = read_lookup_table(lookup_file, lookup_string); } else { switch (lookup_type) { case LU_GRAY: lookup_data.lookup_table = &gray_lookup; break; case LU_HOTMETAL: lookup_data.lookup_table = &hotmetal_lookup; break; case LU_SPECTRAL: lookup_data.lookup_table = &spectral_lookup; break; case LU_TABLE: break; default: fprintf(stderr, "Unknown lookup type: %d\n", lookup_type); break; } } /* Get the null value */ lookup_data.null_value = get_null_value(lookup_data.lookup_table->vector_length, null_value_string); /* Open the input file and get the range */ inmincid = miopen(infile, NC_NOWRITE); if (!discrete_lookup && (lookup_range[0] == DEFAULT_RANGE)) { get_full_range(inmincid, lookup_range); } if (lookup_min != DEFAULT_RANGE) lookup_range[0] = lookup_min; if (lookup_max != DEFAULT_RANGE) lookup_range[1] = lookup_max; /* Set up lookup information */ lookup_data.invert = invert_table; lookup_data.range[0] = lookup_range[0]; lookup_data.range[1] = lookup_range[1]; lookup_data.discrete = discrete_lookup; /* Set up looping options */ loop_options = create_loop_options(); set_loop_clobber(loop_options, clobber); set_loop_verbose(loop_options, verbose); #if MINC2 set_loop_v2format(loop_options, minc2_format); #endif /* MINC2 */ set_loop_datatype(loop_options, datatype, is_signed, valid_range[0], valid_range[1]); set_loop_convert_input_to_scalar(loop_options, TRUE); set_loop_output_vector_size(loop_options, lookup_data.lookup_table->vector_length); set_loop_buffer_size(loop_options, (long) buffer_size * 1024); set_loop_first_input_mincid(loop_options, inmincid); /* Do loop */ voxel_loop(1, &infile, 1, &outfile, arg_string, loop_options, do_lookup, (void *) &lookup_data); /* Free stuff */ if (lookup_data.null_value != NULL) free(lookup_data.null_value); if (lookup_data.lookup_table->free_data) { free(lookup_data.lookup_table->table); free(lookup_data.lookup_table); } exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_lookup_table @INPUT : lookup_filename - name of file from which to read lookup table ("-" means use stdin) lookup_string - string from which to read lookup table if lookup_filename is NULL @OUTPUT : (nothing) @RETURNS : Pointer to lookup table @DESCRIPTION: Routine to read in a lookup table from a file or stdin. If the filename is NULL, then data is read from the lookup_string. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Lookup_Table *read_lookup_table(char *lookup_filename, char *lookup_string) { Lookup_Table *lookup_table; FILE *fp; double *table, *row, *new_table; int nentries, table_nvalues, nvalues, ivalue, ientry; char line[1000]; Sort_Key *sort_table; int need_sort; int old_offset, new_offset; char *lut_string; /* Check for null file name */ if ((lookup_filename == NULL) && (lookup_string == NULL)) return NULL; /* Open the file */ if (lookup_filename == NULL) { fp = NULL; } else if (strcmp(lookup_filename, "-") == 0) { fp = stdin; } else { fp = fopen(lookup_filename, "r"); if (fp == NULL) { (void) fprintf(stderr, "Unable to open lookup file \"%s\".\n", lookup_filename); exit(EXIT_FAILURE); } } /* Read in the table */ /* Read the first line and get the vector length from that*/ nentries = 0; lut_string = lookup_string; if (get_next_line(line, sizeof(line), fp, &lut_string) == NULL) { if (fp != NULL) (void) fprintf(stderr, "Lookup file %s is empty.\n", lookup_filename); else (void) fprintf(stderr, "Lookup string is empty.\n"); exit(EXIT_FAILURE); } row = get_values_from_string(line, 0, NULL, &table_nvalues); if (table_nvalues < 2) { (void) fprintf(stderr, "First line has fewer than 2 values.\n"); if (row != NULL) free(row); exit(EXIT_FAILURE); } table = malloc(sizeof(*table) * table_nvalues); for (ivalue=0; ivalue < table_nvalues; ivalue++) table[ivalue] = row[ivalue]; nentries++; need_sort = FALSE; while (get_next_line(line, sizeof(line), fp, &lut_string) != NULL) { (void) get_values_from_string(line, table_nvalues, row, &nvalues); if (nvalues != table_nvalues) { (void) fprintf(stderr, "Wrong number of values on line %d.\n", nentries+1); free(row); free(table); exit(EXIT_FAILURE); } table = realloc(table, sizeof(*table) * table_nvalues * (nentries+1)); for (ivalue=0; ivalue < table_nvalues; ivalue++) { table[ivalue + nentries*table_nvalues] = row[ivalue]; } nentries++; /* Check for need to sort */ if (table[(nentries-2)*table_nvalues] > table[(nentries-1)*table_nvalues]) { need_sort = TRUE; } } /* Close the input file */ if (fp != NULL) { (void) fclose(fp); } /* Do sorting if needed */ if (need_sort) { /* Set up sorting table */ sort_table = malloc(sizeof(*sort_table) * nentries); for (ientry=0; ientry < nentries; ientry++) { sort_table[ientry].key = table[ientry*table_nvalues]; sort_table[ientry].index = ientry; } /* Sort the sorting table */ qsort((void *) sort_table, nentries, sizeof(*sort_table), sorting_function); /* Copy the table */ new_table = malloc(sizeof(*table) * table_nvalues * nentries); for (ientry=0; ientry < nentries; ientry++) { new_offset = ientry * table_nvalues; old_offset = sort_table[ientry].index * table_nvalues; for (ivalue=0; ivalue < table_nvalues; ivalue++) { new_table[new_offset + ivalue] = table[old_offset + ivalue]; } } free(table); table = new_table; free(sort_table); } /* Allocate space for the lookup table and set initial values */ lookup_table = malloc(sizeof(*lookup_table)); lookup_table->free_data = TRUE; lookup_table->nentries = nentries; lookup_table->vector_length = table_nvalues - 1; lookup_table->table = table; /* Return the lookup table */ return lookup_table; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_next_line @INPUT : linelen - length of line to read in fp - file pointer or NULL if data should be read from a string string - pointer string containing data @OUTPUT : line - line that has been read in string - pointer is advanced to character following ";" @RETURNS : pointer to line or NULL if end of data occurs @DESCRIPTION: Routine to get the next line from a file or from a string that uses ";" as a line separator. @METHOD : @GLOBALS : @CALLS : @CREATED : July 10, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #define LOOKUP_LINE_SEPARATOR ';' static char *get_next_line(char *line, int linelen, FILE *fp, char **string) { int count; /* Read from the file if appropriate */ if (fp != NULL) { return fgets(line, linelen, fp); } /* Otherwise search the string for the ";", copying characters */ else { /* Check for an empty string */ if (**string == '\0') return NULL; /* Loop over characters */ count = 0; while ((**string != '\0') && (**string != LOOKUP_LINE_SEPARATOR)) { if (count < linelen-1) { line[count] = **string; count++; } (*string)++; } /* Terminate the line and move past the ";" */ line[count] = '\0'; if (**string != '\0') (*string)++; /* Return the line */ return line; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : sorting_function @INPUT : value1 - pointer to first value value2 - pointer to second value @OUTPUT : (nothing) @RETURNS : -1, 0 or 1 for value1 <, ==, > value2 @DESCRIPTION: Routine to compare two values for sorting. The value is a pointer to a structure that has a double precision primary key. If that is equal then an integer secondary key is used. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int sorting_function(const void *value1, const void *value2) { const Sort_Key *key1, *key2; key1 = (const Sort_Key *) value1; key2 = (const Sort_Key *) value2; if (key1->key == key2->key) { if (key1->index == key2->index) return 0; else if (key1->index < key2->index) return -1; else return 1; } else if (key1->key < key2->key) return -1; else return 1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_values_from_string @INPUT : string - string containing values array_size - maximum number of values (<=0 means allocate a new array ) @OUTPUT : array - array into which values are written (can be NULL if array_size <= 0) nread - number of values read @RETURNS : Pointer to array of values @DESCRIPTION: Routine to convert a string to an array of values (floating point). If an existing array is passed in (array_size > 0), then up to array_size values are copied into it. Otherwise, a new array is created. Normally, the number of values read is returned - if an error occurs (non-numeric string for example), then zero is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static double *get_values_from_string(char *string, int array_size, double *array, int *nread) { #define VECTOR_SEPARATOR ',' char *cur, *prev, *end; int num_read, num_alloc; double dvalue; /* Set up variables */ num_read = 0; if (array_size <= 0) { num_alloc = 0; array = NULL; } else { num_alloc = array_size; } /* Skip leading white space */ end = string + strlen(string); cur = string; while (isspace(*cur)) cur++; /* Loop through string looking for doubles */ while (cur!=end) { /* Get double */ prev = cur; dvalue = strtod(prev, &cur); if (cur == prev) { *nread = 0; if (array_size <= 0 && array != NULL) { free(array); } return NULL; } /* Add the value to the list */ num_read++; if (num_read > num_alloc) { if (array_size <= 0) { num_alloc += 1; if (array == NULL) { array = malloc(num_alloc * sizeof(*array)); } else { array = realloc(array, num_alloc * sizeof(*array)); } } else { *nread = num_read; return array; } } array[num_read-1] = dvalue; /* Skip any spaces */ while (isspace(*cur)) cur++; /* Skip an optional comma */ if (*cur == VECTOR_SEPARATOR) cur++; } *nread = num_read; return array; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_null_value @INPUT : vector_length - desired vector length string - string from which we should get null values @OUTPUT : (nothing) @RETURNS : Pointer to array of values @DESCRIPTION: Routine to convert a string to an array of values to be used as a null value for the lookup table. Returns NULL if string is NULL @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static double *get_null_value(int vector_length, char *string) { int num_read; double *values; /* Check for NULL string */ if (string == NULL) return NULL; /* Read values */ values = get_values_from_string(string, 0, NULL, &num_read); /* Check the number of values read */ if (num_read != vector_length) { if (values != NULL) free(values); (void) fprintf(stderr, "Null value does not match lookup table (%d values).\n", num_read); exit(EXIT_FAILURE); } return values; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_full_range @INPUT : mincid - id of the input minc file @OUTPUT : range - 2-value array giving real range of input values @RETURNS : (nothing) @DESCRIPTION: Routine to get the full real range of an input file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_full_range(int mincid, double range[2]) { char *string; int varid; int ivar, idim, ndims; int dim[MAX_VAR_DIMS]; double sign, *extreme, *values; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS], num_values; long ivalue, length; /* Loop over image-min and image-max variables */ range[0] = 0.0; range[1] = 1.0; for (ivar=0; ivar < 2; ivar++) { if (ivar==0) { string = MIimagemin; sign = -1.0; extreme = &range[0]; } else { string = MIimagemax; sign = +1.0; extreme = &range[1]; } ncopts = 0; varid = ncvarid(mincid, string); ncopts = NCOPTS_DEFAULT; if (varid != MI_ERROR) { (void) ncvarinq(mincid, varid, NULL, NULL, &ndims, dim, NULL); num_values = 1; for (idim=0; idim < ndims; idim++) { (void) ncdiminq(mincid, dim[idim], NULL, &length); start[idim] = 0; count[idim] = length; num_values *= length; } if (num_values > 0) { values = malloc(num_values * sizeof(*values)); (void) mivarget(mincid, varid, start, count, NC_DOUBLE, NULL, values); *extreme = values[0]; for (ivalue=0; ivalue < num_values; ivalue++) { if ((values[ivalue] * sign) > (*extreme * sign)) *extreme = values[ivalue]; } free(values); } } /* If variable is found */ } /* Loop over image-min/max */ return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_lookup @INPUT : caller_data - pointer to structure containing lookup info num_voxels - number of voxels to work on input_num_buffers - number of input buffers input_vector_length - length of input vector dimension input_data - vector of pointers to input buffer data output_num_buffers - number of output buffers output_vector_length - length of output vector dimension start - vector specifying start of hyperslab (not used) count - vector specifying count of hyperslab (not used) @OUTPUT : output_data - vector of pointers to output buffer data @RETURNS : (nothing) @DESCRIPTION: Routine to loop through an array of voxels and do a lookup table conversion on them. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_lookup(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Lookup_Data *lookup_data; long ivoxel; double lookup_value, scale, offset, denom; /* Get pointer to lookup info */ lookup_data = (Lookup_Data *) caller_data; /* Check that values correspond */ if ((input_num_buffers != 1) || (output_num_buffers != 1) || (input_vector_length != 1) || (output_vector_length != lookup_data->lookup_table->vector_length)) { (void) fprintf(stderr, "Bad internal values.\n"); exit(EXIT_FAILURE); } /* Calculate a scale and offset for input values */ if (lookup_data->discrete) { scale = 1.0; offset = 0.0; } else { denom = (lookup_data->range[1] - lookup_data->range[0]); if (denom == 0.0) scale = 0.0; else scale = 1.0 / denom; if (!lookup_data->invert) { offset = -lookup_data->range[0] * scale; } else { scale = -scale; offset = -lookup_data->range[1] * scale; } } /* Loop through the voxels */ for (ivoxel=0; ivoxel < num_voxels; ivoxel++) { /* Convert input to a lookup value */ lookup_value = input_data[0][ivoxel] * scale + offset; /* Look it up */ lookup_in_table(lookup_value, lookup_data->lookup_table, lookup_data->discrete, lookup_data->null_value, &output_data[0][ivoxel*output_vector_length]); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : lookup_in_table @INPUT : index - value to look up in table lookup_table - the lookup table (big surprise!) discrete_values - flag indicating whether the table should be considered continuous in the range 0 to 1 (FALSE) or discrete, with integer values that should be rounded (TRUE). If the table is continuous, then interpolation between table entries is done (the first and last values propagate beyond the bounds). If the table is discrete, then the null_value is used for indices that are not found. null_value - array specifying the null values to use if the index is not found in the table. Only applies to discrete valued tables (may be NULL otherwise). @OUTPUT : output_value - vector of output values. @RETURNS : (nothing) @DESCRIPTION: Routine to look up a value in the table. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void lookup_in_table(double index, Lookup_Table *lookup_table, int discrete_values, double null_value[], double output_value[]) { int vector_length, nentries; int start, length, mid; int offset, offset1, offset2, ivalue; double value1, value2, *result, frac, rfrac, denom; /* Check for bad lookup table */ nentries = lookup_table->nentries; vector_length = lookup_table->vector_length; if ((nentries < 1) || (vector_length < 1)) { (void) fprintf(stderr, "Bad table size %d x %d\n", nentries, vector_length); exit(EXIT_FAILURE); } /* Round values if needed */ if (discrete_values) index = rint(index); /* Search the table for the value */ start = 0; length = nentries; while (length > 1) { mid = start + length / 2; offset = mid*(vector_length+1); if (index < lookup_table->table[offset]) { length = mid - start; } else { length = start + length - mid; start = mid; } } /* Add a special check for the end of the table */ if (nentries > 1) { offset1 = vector_length+1; offset2 = (nentries-2) * (vector_length+1); if ((start == 0) && (index == lookup_table->table[offset1])) start = 1; else if ((start == nentries-1) && (index == lookup_table->table[offset2])) start = nentries-2; } /* Save the value */ offset = start*(vector_length+1); if (discrete_values) { if (index == lookup_table->table[offset]) result = &lookup_table->table[offset+1]; else result = null_value; for (ivalue=0; ivalue < vector_length; ivalue++) { if (result != NULL) output_value[ivalue] = result[ivalue]; else output_value[ivalue] = 0.0; } } else { offset1 = offset; if (start < nentries - 1) offset2 = offset + vector_length + 1; else offset2 = offset; value1 = lookup_table->table[offset1]; value2 = lookup_table->table[offset2]; denom = value2 - value1; if (denom != 0.0) frac = (index - value1) / denom; else frac = 0.0; if (frac < 0.0) frac = 0.0; if (frac > 1.0) frac = 1.0; rfrac = 1.0 - frac; for (ivalue=0; ivalue < vector_length; ivalue++) { output_value[ivalue] = rfrac * lookup_table->table[offset1 + 1 + ivalue] + frac * lookup_table->table[offset2 + 1 + ivalue]; } } } minc-tools-2.3.00+dfsg/progs/minclookup/minclookup.man10000644000175000000620000002127712574624760022103 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1996 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. .\" .\" $Header: /private-cvsroot/minc/progs/minclookup/minclookup.man1,v 6.2 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCLOOKUP 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME minclookup - perform lookup table conversions on minc files .SH SYNOPSIS .B minclookup [] .mnc .mnc .SH DESCRIPTION \fIMinclookup\fR will perform a lookup table operation on each voxel of a minc file. A lookup table consists of a list of input values with matching output values. Each voxel of the input file is found in the lookup table and the corresponding output value is written out. These output values can be either scalar or vector values so, for example, a colour lookup table would have four columns: one column for input values and one column for each of red, green and blue output values. Lookup tables can take one of two forms: \fIcontinuous\fR or \fIdiscrete\fR. A continuous lookup table is for treating voxel values as continuous (real) values and converting values by doing interpolation between the values given in the lookup table. A discrete lookup table treats input values as integers and deals with them as completely independent entries, doing no interpolation. The most common use of continuous lookup tables is for converting intensity values into RGB colours. To make the lookup tables simpler, the input values are all rescaled into the range zero to one. By default, the smallest value in the file maps to zero and the largest maps to one. This value is then found in the lookup table, usually between two entries in the table (the table is always sorted in ascending order of input values). Linear interpolation is then done on each output column and the resultant value (or values) is written to the output file. If there is more than one output value per input value, then the dimension vector_dimension is added to the output file with length equal to the number of output columns in the lookup table. For input values outside the range zero to one, the nearest table value is used. Discrete lookup tables are usually used for remapping label values. Each input value is treated as an integer (it is not rescaled) and if it is found in the lookup table, then the corresponding value (or values) is written to the output file. If it is not found, then a null value is written out (zero by default). No interpolation is done with discrete lookup tables - to get a non-null output value, there must be an entry in the table. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-no_clobber\fR Synonym for \fB-noclobber\fR. .TP \fB-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-buffer_size\fR\ \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 10 MB. .TP \fB\-filetype\fR Create an output file with the same type as the first input file (default). .TP \fB\-byte\fR Store each voxel as an 8-bit integer. .TP \fB\-short\fR Store each voxel as a 16-bit integer. .TP \fB\-int\fR Store each voxel as a 32-bit integer. .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB\-float\fR Store each voxel in 32-bit floating point format. .TP \fB-double\fR Store each voxel in 64-bit floating point format. .TP \fB\-signed\fR Create an output file with data stored in a signed type. This option is meaningless when used with floating point data formats, which are always signed. .TP \fB\-unsigned\fR Create an output file with data stored in an unsigned type. This option is meaningless when used with floating point data formats. .TP \fB\-valid_range\fR\ \fImin\ max\fR Scale integer voxel values to fall between the values \fImin\fR and \fImax\fR. By default integer voxel values will be scaled to use the entire range of the base type. This option is meaningless when used with floating point data formats. .SH Lookup table options .TP \fB\-gray\fR Use a gray lookup table to write out RGB values (default). .TP \fB\-grey\fR Synonym for \fB-gray\fR. .TP \fB\-hotmetal\fR Use a hot-metal lookup table to write out RGB values. .TP \fB\-spectral\fR Use a spectral (rainbow) lookup table to write out RGB values. .TP \fB\-invert\fR Invert the lookup table so that the maximum value maps to zero and the minimum value maps to one. Applies only to continuous lookup tables. .TP \fB\-noinvert\fR Do not invert the lookup table - the minimum maps to zero and the maximum maps to one (default). .TP \fB\-range\fR\ \fImin\ max\fR Specify the range of values that should map to the range of the lookup table (default is the full range of the input file). .TP \fB\-minimum\fR\ \fImin\fR Specify the input value that maps to the minimum value in the lookup table. .TP \fB\-maximum\fR\ \fImax\fR Specify the input value that maps to the maximum value in the lookup table. .TP \fB\-lookup_table\fR [\fIfile\fR | \fB-\fR] Specify the name of a file containing the lookup table. If \fB-\fR is given, the lookup table is read from the standard input. The file must have at least two columns: The first column gives the input values; the other columns give the corresponding output values. For a continuous lookup table, the first column should contain a value between zero and one inclusive Explicit entries for both zero and one should usually be given. For a discrete lookup table, the first column should contain integer values. If more than one output column is given, then the output file will have the dimension \fBvector_dimension\fR with a length equal to the number of output columns. The lines of the table will be sorted if necessary so that the first column is in ascending order. .TP \fB\-lut_string\fR\ \fIlookup-table-string\fR Specify the complete lookup table as a single string. The semicolon character ";" is used to separate lines. .TP \fB\-continuous\fR The lookup table is continuous (see description above): Input values are treated as continuous (real) values and are rescaled to the range zero to one before being looked up; interpolation is done between values in the table. This is the default behaviour. .TP \fB\-discrete\fR The lookup table is discrete (see description above): Input values are treated as integers and no interpolation is done between input values. .TP \fB\-null_value\fR\ \fInull-value-string\fR Specify a null value to be used with discrete lookup tables when a value is not found in the lookup table. This value must be specified as a comma-separated list of values, with the same number of values as output columns in the lookup table. .SH Generic options for all commands: .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES To get hot-metal RGB images from an MRI file: minclookup -hotmetal input.mnc output.mnc To convert the labels in a minc label file, use -discrete: minclookup -discrete -lookup_table lookupfile \\ in_labels.mnc out_labels.mnc where lookupfile is a file containing entries to map label 2 to 4 and label 3 to 5: 2 4 3 5 You could also specify this lookup table on the command line: minclookup -discrete -lut_string '2 4;3 5' \\ in_labels.mnc out_labels.mnc To get a grey RGB file, with red for values less than the minimum and green for values greater than the minimum, you can give two zero entries and two one entries. The first zero is used for negative values, the second zero is used for interpolation to the next entry. There is no ambiguity about how to handle a value of exactly zero because the first and last values of the table are handled in a special way to make sure that they are treated as within range if this sort of two-entry situation occurs. minclookup -lookup_table - input.mnc output.mnc <] .mnc .SH DESCRIPTION \fImincblob\fR will calculate simple statistical metrics of a minc deformation grid file related to local volume change. The input deformation grid files are typically produced by minctracc. There are currently 4 deformation grid metrics to choose from: trace, determinant, translation and magnitude. The first two relate to different estimates of local volume change the third is a measure of how consistent movement is in a direction but without a local change in volume. The last calcuates the magnitude of the local deformation vector. These metrics are all calculated with respect to a vectors immediate neighbours. No smoothing of the field is performed as part of this calculation so if a smooth results is desired input grid files should be first smoothed or blurred. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-verbose\fR Print out extra information (more than the default). \fB\-trace\fR Compute the areas within the deformation field that equate to volume increase or decrease (+ve or -ve dilation) Dilation is defined as the trace of the deformation field Thus it should range between -1..1 with -1 being compression and 1 being dilation. This measure while fast is essentially a sum of partial derivatives in all three directions. .TP \fB\-determinant\fR This computes the same metric as -trace but with greater accuracy as the local Jacobian matrix is first estimated. The first order determinant of the Jacobian is then returned. .TP \fB\-translation\fR Compute the areas within the deformation field that equate to translation Translation is defined as: trans = arccos( A.B / |A|.|B| ) * e^- (|A|-|B|) Thus it should range between 0..1 with 1 being "translation". .TP \fB\-magnitude\fR Compute the magnitude of the local deformation vector. . .SH Generic options for all commands: .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Andrew Janke .SH COPYRIGHTS .P Program: Copyright \(co 2000 by Andrew Janke .P Man page: Copyright \(co 2001 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincblob/mincblob.c0000644000175000000620000004115012574624760020473 0ustar stevestaff/* mincblob.c */ /* */ /* Andrew Janke - a.janke@gmail.com */ /* Center for Magnetic Resonance */ /* University of Queensland */ /* */ /* Copyright Andrew Janke, The University of Queensland. */ /* 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 the University of Queensland make no representations about the */ /* suitability of this software for any purpose. It is provided "as is" */ /* without express or implied warranty. */ /* */ /* The bloberiser */ /* */ /* Mon Aug 21 16:18:36 EST 2000 - Original version */ /* Wed Nov 1 17:47:35 EST 2000 - rewrote translation option (new equation) */ /* Thu Feb 7 23:42:40 EST 2002 - complete rewrite to use volume_io */ /* Mon May 6 21:07:18 EDT 2002 - added -determinant option (jacobian) */ /* TRACE */ /* Compute the areas within the deformation field that equate to volume */ /* increase or decrease (+ve or -ve dilation) */ /* Dilation is defined as the trace of the deformation field */ /* Thus it should range between -1..1 with -1 being compression and */ /* 1 being dilation. */ /* TRANSLATION */ /* Compute the areas within the deformation field that equate to translation */ /* Translation is defined as: trans = arccos( A.B / |A|.|B| ) * e^- (|A|-|B|) */ /* Thus it should range between 0..1 with 1 being "translation". */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include typedef enum { NO_OP, TRACE, DETERMINANT, TRANSLATION, MAGNITUDE } op; /* function prototypes */ double fdiv(double num, double denom); double farccos(double a0, double b0, double c0, double a1, double b1, double c1); double cindex(double a0, double b0, double c0, double a1, double b1, double c1); double feuc(double a, double b, double c); void clear_borders(VIO_Volume v, int sizes[3]); void fill_slice0(VIO_Volume v, VIO_Real value, int v0, int v1_size, int v2_size); void fill_slice1(VIO_Volume v, VIO_Real value, int v0_size, int v1, int v2_size); void fill_slice2(VIO_Volume v, VIO_Real value, int v0_size, int v1_size, int v2); void print_version_info(void); /* argument variables */ static int verbose = FALSE; static int clobber = FALSE; static op operation = NO_OP; /* argument table */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "General options:"}, {"-version", ARGV_FUNC, (char *)print_version_info, (char *)NULL, "print version info and exit"}, {"-verbose", ARGV_CONSTANT, (char *)TRUE, (char *)&verbose, "print out extra information"}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "clobber existing files"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nOperations:"}, {"-trace", ARGV_CONSTANT, (char *)TRACE, (char *)&operation, "compute the trace (approximate growth and shrinkage) -- FAST"}, {"-determinant", ARGV_CONSTANT, (char *)DETERMINANT, (char *)&operation, "compute the determinant (exact growth and shrinkage) -- SLOW"}, {"-translation", ARGV_CONSTANT, (char *)TRANSLATION, (char *)&operation, "compute translation (structure displacement)"}, {"-magnitude", ARGV_CONSTANT, (char *)MAGNITUDE, (char *)&operation, "compute the magnitude of the displacement vector"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char **argv) { char *arg_string; char *infile; char *outfile; VIO_Volume in_vol; VIO_Volume out_vol; nc_type datatype; VIO_BOOL signed_flag; VIO_Real steps[MAX_VAR_DIMS]; VIO_Real starts[MAX_VAR_DIMS]; int sizes[MAX_VAR_DIMS]; double out_real_max, out_real_min; char *in_axis_order[4] = { MIvector_dimension, MIzspace, MIyspace, MIxspace }; char *out_axis_order[3] = { MIzspace, MIyspace, MIxspace }; int x, y, z; double value; VIO_progress_struct progress; /* Jacobian matrix */ VIO_Real J[3][3]; /* Save list of arguments as strings */ arg_string = time_stamp(argc, argv); /* Check arguments */ if(ParseArgv(&argc, argv, argTable, 0) || (argc != 3)){ fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* check for an operation */ if(operation == NO_OP){ fprintf(stderr, "%s: You need to specify an operation!\n\n", argv[0]); exit(EXIT_FAILURE); } /* check for the infile and outfile */ if(access(infile, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find %s\n\n", argv[0], infile); exit(EXIT_FAILURE); } if(access(outfile, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists! (use -clobber to overwrite)\n\n", argv[0], outfile); exit(EXIT_FAILURE); } /* read in the input volume and a few other things.... */ if(input_volume(infile, 4, in_axis_order, NC_UNSPECIFIED, TRUE, 0.0, 0.0, TRUE, &in_vol, NULL) != 0){ fprintf(stderr, "%s: Error reading input volume (%s)\n\n", argv[0], infile); exit(EXIT_FAILURE); } datatype = get_volume_nc_data_type(in_vol, &signed_flag); get_volume_sizes(in_vol, sizes); get_volume_starts(in_vol, starts); get_volume_separations(in_vol, steps); switch (operation){ default: case NO_OP: fprintf(stderr, "%s: GNARKLE! this shouldn't happen!\n\n", argv[0]); exit(EXIT_FAILURE); break; case TRACE: case DETERMINANT: out_real_min = -1.0; out_real_max = 1.0; break; case MAGNITUDE: case TRANSLATION: out_real_min = 0.0; out_real_max = 1.0; break; } /* set up the output volume */ out_vol = create_volume(3, out_axis_order, NC_DOUBLE, TRUE, 0.0, 0.0); set_volume_sizes(out_vol, &sizes[1]); set_volume_starts(out_vol, &starts[1]); set_volume_separations(out_vol, &steps[1]); alloc_volume_data(out_vol); /* set the surrounding voxels to 0 */ clear_borders(out_vol, &sizes[1]); /* start to do some stuff */ initialize_progress_report(&progress, FALSE, sizes[2] - 2, "Blobberising"); for(z = 1; z < sizes[1] - 1; z++){ for(y = 1; y < sizes[2] - 1; y++){ for(x = 1; x < sizes[3] - 1; x++){ switch (operation){ default: case NO_OP: fprintf(stderr, "%s: GNARKLE! this shouldn't happen!\n\n", argv[0]); exit(EXIT_FAILURE); break; case TRACE: value = ((get_volume_real_value(in_vol, 0, z, y, x + 1, 0) - get_volume_real_value(in_vol, 0, z, y, x - 1, 0)) / (steps[3] * 2)) + ((get_volume_real_value(in_vol, 1, z, y + 1, x, 0) - get_volume_real_value(in_vol, 1, z, y - 1, x, 0)) / (steps[2] * 2)) + ((get_volume_real_value(in_vol, 2, z + 1, y, x, 0) - get_volume_real_value(in_vol, 2, z - 1, y, x, 0)) / (steps[1] * 2)); break; case DETERMINANT: /* compute the Jacobian matrix */ J[0][0] = 1 + ((get_volume_real_value(in_vol, 0, z, y, x + 1, 0) - get_volume_real_value(in_vol, 0, z, y, x - 1, 0)) / (steps[3] * 2)); J[0][1] = (get_volume_real_value(in_vol, 0, z, y + 1, x, 0) - get_volume_real_value(in_vol, 0, z, y - 1, x, 0)) / (steps[2] * 2); J[0][2] = (get_volume_real_value(in_vol, 0, z + 1, y, x, 0) - get_volume_real_value(in_vol, 0, z - 1, y, x, 0)) / (steps[1] * 2); J[1][0] = (get_volume_real_value(in_vol, 1, z, y, x + 1, 0) - get_volume_real_value(in_vol, 1, z, y, x - 1, 0)) / (steps[3] * 2); J[1][1] = 1 + ((get_volume_real_value(in_vol, 1, z, y + 1, x, 0) - get_volume_real_value(in_vol, 1, z, y - 1, x, 0)) / (steps[2] * 2)); J[1][2] = (get_volume_real_value(in_vol, 1, z + 1, y, x, 0) - get_volume_real_value(in_vol, 1, z - 1, y, x, 0)) / (steps[1] * 2); J[2][0] = (get_volume_real_value(in_vol, 2, z, y, x + 1, 0) - get_volume_real_value(in_vol, 2, z, y, x - 1, 0)) / (steps[3] * 2); J[2][1] = (get_volume_real_value(in_vol, 2, z, y + 1, x, 0) - get_volume_real_value(in_vol, 2, z, y - 1, x, 0)) / (steps[2] * 2); J[2][2] = 1 + ((get_volume_real_value(in_vol, 2, z + 1, y, x, 0) - get_volume_real_value(in_vol, 2, z - 1, y, x, 0)) / (steps[1] * 2)); value = (J[0][0] * ((J[1][1] * J[2][2]) - (J[1][2] * J[2][1])) - J[0][1] * ((J[1][0] * J[2][2]) - (J[1][2] * J[2][0])) + J[0][2] * ((J[1][0] * J[2][1]) - (J[1][1] * J[2][0])) ) - 1; break; case TRANSLATION: value = ( /* x direction */ cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z, y, x - 1, 0), get_volume_real_value(in_vol, 1, z, y, x - 1, 0), get_volume_real_value(in_vol, 2, z, y, x - 1, 0)) + cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z, y, x + 1, 0), get_volume_real_value(in_vol, 1, z, y, x + 1, 0), get_volume_real_value(in_vol, 2, z, y, x + 1, 0)) + /* y direction */ cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z, y - 1, x, 0), get_volume_real_value(in_vol, 1, z, y - 1, x, 0), get_volume_real_value(in_vol, 2, z, y - 1, x, 0)) + cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z, y + 1, x, 0), get_volume_real_value(in_vol, 1, z, y + 1, x, 0), get_volume_real_value(in_vol, 2, z, y + 1, x, 0)) + /* z direction */ cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z - 1, y, x, 0), get_volume_real_value(in_vol, 1, z - 1, y, x, 0), get_volume_real_value(in_vol, 2, z - 1, y, x, 0)) + cindex(get_volume_real_value(in_vol, 0, z, y, x, 0), get_volume_real_value(in_vol, 1, z, y, x, 0), get_volume_real_value(in_vol, 2, z, y, x, 0), get_volume_real_value(in_vol, 0, z + 1, y, x, 0), get_volume_real_value(in_vol, 1, z + 1, y, x, 0), get_volume_real_value(in_vol, 2, z + 1, y, x, 0)) ) / 6; break; case MAGNITUDE: value = sqrt((get_volume_real_value(in_vol, 0, z, y, x, 0) * get_volume_real_value(in_vol, 0, z, y, x, 0)) + (get_volume_real_value(in_vol, 1, z, y, x, 0) * get_volume_real_value(in_vol, 1, z, y, x, 0)) + (get_volume_real_value(in_vol, 2, z, y, x, 0) * get_volume_real_value(in_vol, 2, z, y, x, 0))); break; } set_volume_real_value(out_vol, z, y, x, 0, 0, value); /* check the min and max */ if(value < out_real_min){ out_real_min = value; } else if(value > out_real_max){ out_real_max = value; } } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); if(verbose){ fprintf(stdout, "%s: Found output range of [%g:%g]\n", argv[0], out_real_min, out_real_max); } set_volume_real_range(out_vol, out_real_min, out_real_max); output_volume(outfile, datatype, signed_flag, 0.0, 0.0, out_vol, arg_string, NULL); return (EXIT_SUCCESS); } double fdiv(double num, double denom) { if(fabs(denom) < 0.0005){ return 0.0; } else { return num / denom; } } double feuc(double a, double b, double c) { return sqrt((a * a) + (b * b) + (c * c)); } double farccos(double a0, double b0, double c0, double a1, double b1, double c1) { return acos(fdiv((a0 * a1) + (b0 * b1) + (c0 * c1), feuc(a0, b0, c0) * feuc(a1, b1, c1))); } double cindex(double a0, double b0, double c0, double a1, double b1, double c1) { return (1.0 - farccos(a0, b0, c0, a1, b1, c1)) * exp(-1.0 * (fabs(feuc(a0, b0, c0) - feuc(a1, b1, c1)))); } /* steve's super-dooper padding function(s) */ void clear_borders(VIO_Volume v, int sizes[3]) { fill_slice0(v, 0, 0, sizes[1], sizes[2]); fill_slice0(v, 0, sizes[0] - 1, sizes[1], sizes[2]); fill_slice1(v, 0, sizes[0], 0, sizes[2]); fill_slice1(v, 0, sizes[0], sizes[1] - 1, sizes[2]); fill_slice2(v, 0, sizes[0], sizes[1], 0); fill_slice2(v, 0, sizes[0], sizes[1], sizes[2] - 1); } void fill_slice0(VIO_Volume v, VIO_Real value, int v0, int v1_size, int v2_size) { int v1, v2; for(v1 = 0; v1 < v1_size; ++v1){ for(v2 = 0; v2 < v2_size; ++v2){ set_volume_real_value(v, v0, v1, v2, 0, 0, value); } } } void fill_slice1(VIO_Volume v, VIO_Real value, int v0_size, int v1, int v2_size) { int v0, v2; for(v0 = 0; v0 < v0_size; ++v0){ for(v2 = 0; v2 < v2_size; ++v2){ set_volume_real_value(v, v0, v1, v2, 0, 0, value); } } } void fill_slice2(VIO_Volume v, VIO_Real value, int v0_size, int v1_size, int v2) { int v0, v1; for(v0 = 0; v0 < v0_size; ++v0){ for(v1 = 0; v1 < v1_size; ++v1){ set_volume_real_value(v, v0, v1, v2, 0, 0, value); } } } void print_version_info(void) { fprintf(stdout, "%s version %s\n", PACKAGE_STRING, PACKAGE_VERSION); fprintf(stdout, "Comments to %s\n", PACKAGE_BUGREPORT); fprintf(stdout, "\n"); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/mincinfo/0002755000175000000620000000000012574624760016560 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincinfo/mincinfo.man10000644000175000000620000001030412574624760021134 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincinfo/mincinfo.man1,v 6.3 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCINFO 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincinfo \- print out specified information about a minc file .SH SYNOPSIS .B mincinfo [] [ ...] .SH DESCRIPTION \fImincinfo\fR will print out either a general description of a minc file (type, sign and range of data, plus a brief description of dimensions and their order), or specific information about dimensions, variables or attributes in the file. This program can be very useful for building shell scripts that access minc files. All information given by \fImincinfo\fR is presented as read from the file with no transformation. This means that start and step values, for example, are not in the world coordinate system. To display the start values for a file in world coordinates, use \fIvoxeltoworld\fR. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. Dimensions, variables and attributes are all specified by name. Attributes are specified by \fIvariable:attribute\fR where \fIvariable\fR can be omitted to specify global attributes. More than one option can be specified, in which case the return value from each option is printed on a separate line (\fB-image_info\fR prints on many lines) in the order of the options on the command line. .TP \fB\-image_info\fR Print out the default general information about the file. This information includes the type, sign and range of the pixel data, the order of the dimensions, and a list of dimensions giving name, length, start and step for each one. .TP \fB\-dimnames\fR Print out a space-separated list of the dimensions in the file. .TP \fB\-varnames\fR Print out a space-separated list of the variables in the file. .TP \fB\-dimlength\fR \fIdimension\fR Print the length of the specified dimension. .TP \fB\-vartype\fR \fIvariable\fR Print the type of the variable. .TP \fB\-vardims\fR \fIvariable\fR Print a space-separated list of the dimensions that subscript the variable (in C order). .TP \fB\-varatts\fR \fIvariable\fR Print a space-separated list of the attribute names for the specified variable. .TP \fB\-varvalues\fR \fIvariable\fR Print a newline-separated list of the values of the specified variable. .TP \fB\-atttype\fR \fIvariable:attribute\fR Print out the type of the specified attribute. .TP \fB\-attvalue\fR \fIvariable:attribute\fR Print out a space-separated list of the values of the specified attribute. .TP \fB\-error_string\fR \fIstring\fR Specifies a string to print out if an error occurs. This will cause the program to exit with normal status. The default is to print an appropriate error message and exit with an error status. .TP \fB\-help\fR Print summary of command-line options and abort. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES Print out standard information about a minc file. .IP mincinfo file.mnc .PP Print out contents of global history attribute. .IP mincinfo file.mnc -attvalue :history .PP Print out step value for x dimension, setting the default value to 1. .IP mincinfo file.mnc -attvalue xspace:step -error 1 .PP Print out the step values for x, y and z, setting the default value to 1. .IP mincinfo file.mnc -error 1 \\ -attvalue xspace:step \\ -attvalue yspace:step \\ -attvalue zspace:step .PP Print out the names of the dimensions subscripting the image variable. .IP mincinfo file.mnc -vardims image .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin .SH "SEE ALSO" .LP .BR voxeltoworld (1) minc-tools-2.3.00+dfsg/progs/mincinfo/mincinfo.c0000644000175000000620000005242012574624760020527 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincinfo @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to dump minc header information to standard output. @METHOD : @GLOBALS : @CALLS : @CREATED : May 19, 1993 (Peter Neelin) @MODIFIED : * $Log: mincinfo.c,v $ * Revision 6.13 2009-04-29 13:58:46 rotor * * fixed a stack smash in mincinfo.c * * 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 04:30:28 stever * Add braces around static initializers. * * Revision 6.10 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.9 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.8 2006/07/28 18:20:53 baghdadi * *** empty log message *** * * Revision 6.7 2006/07/28 17:51:01 baghdadi * Added option to print version of file * must use -minc_version -image_info * * Revision 6.6 2005/09/14 04:31:17 bert * include config.h * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2001/10/31 19:40:21 neelin * Fixed bug in printing of sign for default output - this was introduced * in the change to miget_datatype. * * Revision 6.3 2001/08/16 16:41:35 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.2 2000/04/25 18:12:05 neelin * Added modified version of patch from Steve Robbins to allow use on * multiple input files. * * Revision 6.1 1999/10/19 14:45:24 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:35 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:36 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:38 neelin * Release of minc version 0.4 * * Revision 3.1 1995/10/04 19:05:25 neelin * Fixed default_min for signed long. * * Revision 3.0 1995/05/15 19:31:31 neelin * Release of minc version 0.3 * * Revision 2.3 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.2 1995/02/01 15:29:31 neelin * Fixed call of miexpand_file. * * Revision 2.1 95/01/23 13:47:46 neelin * Changed ncopen, ncclose to miopen, miclose. Added miexpand_file to get * header only when appropriate. * * Revision 2.0 94/09/28 10:34:04 neelin * Release of minc version 0.2 * * Revision 1.10 94/09/28 10:34:00 neelin * Pre-release * * Revision 1.9 93/08/11 15:45:53 neelin * Functions called by ParseArgv must check that nextArg is not NULL. * * Revision 1.8 93/08/11 15:22: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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define MAX_NUM_OPTIONS 100 /* Name of program, for error reporting */ static char* exec_name; static char *type_names[] = { NULL, "byte", "char", "short", "long", "float", "double" }; /* Types */ typedef enum { ENDLIST, IMAGE_INFO, DIMNAMES, VARNAMES, DIMLENGTH, VARTYPE, VARDIMS, VARATTS, VARVALUES, ATTTYPE, ATTVALUE, MINC_VERSION } Option_code; typedef struct { Option_code code; char *value; } Option_type; /* Macros */ #define REPORT_ERROR \ {int s;if ((s=report_error())!=EXIT_SUCCESS) return s; goto error_label;} #define CHK_ERR(code) if ((code) == MI_ERROR) {REPORT_ERROR} #define RTN_ERR(code) if ((code) == MI_ERROR) {return MI_ERROR;} /* Function prototypes */ static int process_file( char* filename, int header_only ); static int get_option(char *dst, char *key, char *nextarg); static int report_error(void); static int get_attname(int mincid, char *string, int *varid, char *name); static int print_image_info(char *filename, int mincid); /* Variables used for argument parsing */ static char *error_string = NULL; static Option_type option_list[MAX_NUM_OPTIONS] = { { ENDLIST, "" } }; static int length_option_list = 0; /* Argument table */ ArgvInfo argTable[] = { {"-image_info", ARGV_FUNC, (char *) get_option, (char *) IMAGE_INFO, "Print out the default information about the images."}, {"-dimnames", ARGV_FUNC, (char *) get_option, (char *) DIMNAMES, "Print the names of the dimensions in the file."}, {"-varnames", ARGV_FUNC, (char *) get_option, (char *) VARNAMES, "Print the names of the variables in the file."}, {"-dimlength", ARGV_FUNC, (char *) get_option, (char *) DIMLENGTH, "Print the length of the specified dimension."}, {"-vartype", ARGV_FUNC, (char *) get_option, (char *) VARTYPE, "Print the type of the specified variable."}, {"-vardims", ARGV_FUNC, (char *) get_option, (char *) VARDIMS, "Print the dimension names for the specified variable."}, {"-varatts", ARGV_FUNC, (char *) get_option, (char *) VARATTS, "Print the attribute names for the specified variable."}, {"-varvalues", ARGV_FUNC, (char *) get_option, (char *) VARVALUES, "Print the values for the specified variable."}, {"-atttype", ARGV_FUNC, (char *) get_option, (char *) ATTTYPE, "Print the type of the specified attribute (variable:attribute)."}, {"-attvalue", ARGV_FUNC, (char *) get_option, (char *) ATTVALUE, "Print the value(s) of the specified attribute (variable:attribute)."}, {"-error_string", ARGV_STRING, (char *) 0, (char *) &error_string, "Error to print on stdout (default = exit with error status)."}, {"-minc_version", ARGV_FUNC, (char *) get_option, (char *) MINC_VERSION, "Print the minc file version, netcdf or hdf5."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { int ioption, ifile, header_only; int ret_value = EXIT_SUCCESS; exec_name = argv[0]; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { (void) fprintf(stderr, "\nUsage: %s [] [ ...]\n", exec_name); (void) fprintf(stderr, " %s -help\n\n", exec_name); exit(EXIT_FAILURE); } /* Check for no print options */ if (option_list[0].code == ENDLIST) { option_list[0].code = IMAGE_INFO; option_list[0].value = NULL; length_option_list = 1; } /* Set error handling */ if (error_string == NULL) ncopts = NC_VERBOSE | NC_FATAL; else ncopts = 0; /* Loop through print options, checking whether we need variable data */ header_only = TRUE; for (ioption=0; ioption < length_option_list; ioption++) { if (option_list[ioption].code == VARVALUES) header_only = FALSE; } for (ifile=1; ifile < argc; ++ifile) { if (process_file( argv[ifile], header_only ) != EXIT_SUCCESS) ret_value = EXIT_FAILURE; if (argc > 2) printf("\n\n"); } return ret_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : print_image_version @INPUT : (boolean) @OUTPUT : (none) @RETURNS : Exit status. @DESCRIPTION: Prints out image netcdf or hdf5 @METHOD : @GLOBALS : @CALLS : @CREATED : Apr 6, 2005 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ static int print_image_version(int Is_MINC2_File) { if (Is_MINC2_File) { (void) printf("Version: 2 (HDF5)\n"); } else { (void) printf("Version: 1 (netCDF)\n"); } return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : process_file @INPUT : filename header_only - TRUE if only header needs to be expanded @OUTPUT : (none) @RETURNS : Exit value for program @DESCRIPTION: Runs mincinfo on one file @METHOD : (Adapted from old main of mincinfo.) @GLOBALS : @CALLS : @CREATED : April 25, 2000 (Steve Robbins) @MODIFIED : ---------------------------------------------------------------------------- */ static int process_file( char* filename, int header_only ) { int mincid, varid, dimid; int ndims, dims[MAX_VAR_DIMS]; nc_type datatype; long length, var_length, row_length; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int att_length; int idim, iatt, natts, option; int nvars, ivar, ival; char *string; char name[MAX_NC_NAME]; char *cdata; double *ddata; int created_tempfile; char *tempfile; int Is_MINC2_File=0; /* Expand file */ tempfile = miexpand_file(filename, NULL, header_only, &created_tempfile); if (tempfile == NULL) { (void) fprintf(stderr, "%s: Error expanding file \"%s\"\n", exec_name, filename); return EXIT_FAILURE; } /* Open the file */ mincid = miopen(tempfile, NC_NOWRITE); if (created_tempfile) { (void) remove(tempfile); } if (mincid == MI_ERROR) { (void) fprintf(stderr, "%s: Error opening file \"%s\"\n", exec_name, tempfile); return EXIT_FAILURE; } /* check whether the file is Version 2 */ #ifdef MINC2 if (MI2_ISH5OBJ(mincid)) { Is_MINC2_File = 1; } #endif /* Loop through print options */ for (option=0; option < length_option_list; option++) { string = option_list[option].value; switch (option_list[option].code) { case IMAGE_INFO: CHK_ERR(print_image_info(filename, mincid)); break; case DIMNAMES: CHK_ERR(ncinquire(mincid, &ndims, NULL, NULL, NULL)); for (idim=0; idim= MAX_NUM_OPTIONS-1) { (void) fprintf(stderr, "Too many options - maximum is %d.\n", MAX_NUM_OPTIONS - 1); exit(EXIT_FAILURE); } /* Save option */ code = (Option_code) dst; option_list[length_option_list].code = code; if ((code == IMAGE_INFO) | (code == MINC_VERSION) || (code == DIMNAMES) || (code == VARNAMES)){ option_list[length_option_list].value = NULL; return_value = FALSE; } else { /* Check for following argument */ if (nextarg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); return FALSE; } option_list[length_option_list].value = nextarg; return_value = TRUE; } length_option_list++; option_list[length_option_list].code = ENDLIST; return return_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : report_error @INPUT : (none) @OUTPUT : (none) @RETURNS : Exit status. @DESCRIPTION: Prints out the error message @METHOD : @GLOBALS : @CALLS : @CREATED : May 19, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int report_error(void) { if (error_string == NULL) { (void) fprintf(stderr, "Error reading file.\n"); return EXIT_FAILURE; } else { (void) fprintf(stdout, "%s\n", error_string); return EXIT_SUCCESS; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_attname @INPUT : mincid - id of minc file string - string giving varname:attname @OUTPUT : varid - pointer to variale id name - name of attribute @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets variable id and attribute name from a string of the form "varname:attname" @METHOD : @GLOBALS : @CALLS : @CREATED : May 19, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_attname(int mincid, char *string, int *varid, char *name) { #define ATT_SEP_CHAR ':' char *attname, varname[MAX_NC_NAME]; int i; /* Get the variable name */ for (i=0; (i < MAX_NC_NAME) && (string[i] != ATT_SEP_CHAR) && (string[i] != '\0'); i++) { varname[i] = string[i]; } if (string[i] != ATT_SEP_CHAR) { if (error_string == NULL) { (void) fprintf(stderr, "Invalid attribute name '%s'\n", string); } return MI_ERROR; } varname[i] = '\0'; attname = &string[i+1]; /* Get varid and name */ if (varname[0] == '\0') { *varid = NC_GLOBAL; } else if ((*varid = ncvarid(mincid, varname)) == MI_ERROR) { return MI_ERROR; } (void) strncpy(name, attname, MAX_NC_NAME-1); name[MAX_NC_NAME-1] = '\0'; return MI_NOERROR; } /* ----------------------------- MNI Header ----------------------------------- @NAME : print_image_info @INPUT : mincid - id of minc file @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Prints information about image data in file. @METHOD : @GLOBALS : @CALLS : @CREATED : May 19, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int print_image_info(char *filename, int mincid) { int imgid, ndims, dim[MAX_VAR_DIMS], varid; nc_type datatype; double valid_range[2]; char *sign_type[] = {MI_UNSIGNED, MI_SIGNED}; int sign_index; int is_signed; long length; int idim; char name[MAX_NC_NAME]; int oldncopts; double dim_start, dim_step; /* Get information about variable */ RTN_ERR(imgid = ncvarid(mincid, MIimage)); RTN_ERR(ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL)); RTN_ERR(miget_datatype(mincid, imgid, &datatype, &is_signed)); RTN_ERR(miget_valid_range(mincid, imgid, valid_range)); /* Get sign index */ sign_index = (is_signed ? 1 : 0); /* Write out image info line */ (void) printf("file: %s\n", filename); (void) printf("image: %s %s %.20g to %.20g\n", sign_type[sign_index], type_names[datatype], valid_range[0], valid_range[1]); /* Write out dimension names */ (void) printf("image dimensions:"); for (idim=0; idim #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Argument variables */ int clobber = FALSE; int verbose = FALSE; /* Argument table */ ArgvInfo argTable[] = { {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out extra information."}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]){ VIO_General_transform transform, inverse; char *arg_string; char *pname; char *infile; char *outfile; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || argc != 3) { (void) fprintf(stderr, "\nUsage: %s [options] \n", pname); (void) fprintf(stderr, " %s -help\n\n", pname); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* check for the infile */ if(access(infile, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find %s\n\n", pname, infile); exit(EXIT_FAILURE); } /* check for the outfile */ if(access(outfile, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists! (use -clobber to overwrite)\n\n", pname, outfile); exit(EXIT_FAILURE); } /* Read in file to invert */ if (input_transform_file(infile, &transform) != VIO_OK) { (void) fprintf(stderr, "%s: Error reading transform file %s\n", pname, infile); exit(EXIT_FAILURE); } /* Invert the transform */ create_inverse_general_transform(&transform, &inverse); if(verbose){ (void) fprintf(stdout, "[%s]: Inverted %s\n", pname, infile); } /* Write out the transform */ if (output_transform_file(outfile, arg_string, &inverse) != VIO_OK) { (void) fprintf(stderr, "%s: Error writing transform file %s\n", pname, outfile); exit(EXIT_FAILURE); } if(verbose){ (void) fprintf(stdout, "[%s]: Wrote out %s\n", pname, outfile); } exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/xfm/xfmflip.in0000755000175000000620000000703612574624760017554 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - a.janke@gmail.com # # Copyright Andrew Janke, The University of Queensland. # 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 the University make no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Flips an xfm in the given direction use strict; use warnings "all"; use Getopt::Tabular; use File::Basename; use File::Temp qw/ tempdir /; my($Help, $Usage, $me, @opt_table, $tmpdir, $history, %opt); my(@args, $inxfm, $outxfm); # Argument variables and table $me = &basename($0); $tmpdir = "/tmp/$me-$$"; %opt = ( 'verbose' => 0, 'clobber' => 0, 'fake' => 0, 'todo' => 0, ); $Help = < 1, CLEANUP => 1 ); my @flips = ( [-1, 1, 1], [ 1, -1, 1], [ 1, 1, -1] ); # generate a temporary flip xfm &do_cmd('param2xfm', '-clobber', '-center', 0, 0, 0, '-scales', @{$flips[$opt{todo}]}, "$tmpdir/flip.xfm"); # create the flip xfm &do_cmd('xfmconcat', "$tmpdir/flip.xfm", $inxfm, "$tmpdir/flip.xfm", $outxfm); # do the dogy and add a history string (this may well bite me later on) my @buf = split("\n", `cat $outxfm`); open(FH, ">$outxfm"); print FH shift(@buf) . "\n" . "%\n" . "% Created by $me\n" . "%\n" . "% $history \n" . "\n" . join("\n", @buf) . "\n"; close(FH); sub do_cmd { print STDOUT "@_\n" if $opt{verbose}; if(!$opt{fake}){ system(@_) == 0 or die; } } sub print_version_info { my($package, $version, $package_bugreport); $package = '@PACKAGE@'; $version = '@VERSION@'; $package_bugreport = '@PACKAGE_BUGREPORT@'; print STDOUT "\n$package version $version\n". "Comments to $package_bugreport\n\n"; exit; } minc-tools-2.3.00+dfsg/progs/xfm/transformtags.man10000644000175000000620000000217612574624760021224 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH TRANSFORMTAGS 1 "$Date: 2004-05-20 21:52:09 $" "" "MINC User's Guide" .SH NAME transformtags \- apply MNI transform to a tag file .SH SYNOPSIS .B transformtags .BI [ -vol1 ] .BI [ -vol2 ] .BI [ "-transformation transform.xfm" ] .BI infile.tag .BI outfile.tag .SH DESCRIPTION \fBtransformtags\fR applies the specified transformation to one of the two sets of tags in the tag file. The default is to apply the transformation to the tags of the second volume. Use \fB\-vol1\fR to transform the first volume's tag. Only one volume at a time may be transformed. The transformation must be specified using the \fB-transformation\fR option. .SH OPTIONS .TP \fB\-vol1\fR Transform tags for volume 1. .TP \fB\-vol2\fR Transform tags for volume 2 (default). .TP \fB\-transformation\fR\ \fIfilename.xfm\fR Name of transformation file (default is internal identity transform). .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 Peter Neelin minc-tools-2.3.00+dfsg/progs/xfm/xfminvert.man10000644000175000000620000000127212574624760020350 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH XFMINVERT 1 "$Date: 2008-09-04 03:20:16 $" "" "MINC User's Guide" .SH NAME xfminvert \- invert an MNI transform file .SH SYNOPSIS \fBxfminvert\fR\ \fIinput.xfm\fR\ \fIresult.xfm\fR .SH DESCRIPTION \fIxfminvert\fR creates the file \fIresult.xfm\fR containing the inverse transformation of \fIinput.xfm\fR. .SH OPTIONS .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-verbose\fR Print out progress information. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 Peter Neelin minc-tools-2.3.00+dfsg/progs/xfm/xfmconcat.man10000644000175000000620000000146112574624760020310 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH XFMCONCAT 1 "$Date: 2008-09-04 03:20:16 $" "" "MINC User's Guide" .SH NAME xfmconcat \- concatenate MNI transform files .SH SYNOPSIS \fBxfmconcat\fR\ \fIinput1.xfm\fR [ \fIinput2.xfm\fR... ] \fIresult.xfm\fR .SH DESCRIPTION \fIxfmconcat\fR concatenates a number of transforms together. The resulting transformation will have the effect of applying transformation \fIinput1.xfm\fR then \fIinput2.xfm\fR, etc., in that order. .SH OPTIONS .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-verbose\fR Print out progress information. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 Peter Neelin minc-tools-2.3.00+dfsg/progs/xfm/xfmconcat.c0000644000175000000620000001313212574624760017674 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : xfmconcat @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to concatenate MNI transform files @METHOD : @GLOBALS : @CALLS : @CREATED : August 13, 1993 (Peter Neelin) @MODIFIED : * $Log: xfmconcat.c,v $ * Revision 6.7 2008-09-04 03:20:16 rotor * * added -clobber option to xfmconcat and readied for 2.0.16 release * * Revision 6.6 2008/01/23 22:54:35 rotor * * added Claude to AUTHORS * * added a patch for history to xfmconcat from Mishkin Derakhshan * * Revision 6.5 2008/01/17 02:33:06 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.4 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.3 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.2 2004/02/02 18:24:11 bert * Call ParseArgv() so that version information will be available * * Revision 6.1 1999/10/19 14:45:32 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:28 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:29 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:08 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:03 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:33:25 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:33:20 neelin * Pre-release * * Revision 1.4 93/10/12 12:52:35 neelin * Replaced def_mni.h with volume_io.h * * Revision 1.3 93/09/16 09:40:21 neelin * Use dave's open_file_with_default_suffix and input_transform_file and * output_transform_file to add suffixes to file names. * * Revision 1.2 93/09/01 15:58:49 neelin * Cast return of fclose to (void). * * Revision 1.1 93/08/13 15:27:18 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 #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Argument variables */ int clobber = FALSE; int verbose = FALSE; /* Argument table */ ArgvInfo argTable[] = { {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out extra information."}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]){ VIO_General_transform trans1, trans2, trans3; VIO_General_transform *new_result, *old_result, *input, *temp_result; int iarg, first_arg, last_arg; char *outfile; char *arg_string; char *pname; /* collect the command line for history */ arg_string = time_stamp(argc, argv); /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || argc < 3) { (void) fprintf(stderr, "Usage: %s [ ...] \n", pname); (void) fprintf(stderr, " %s -help\n\n", pname); exit(EXIT_FAILURE); } /* Set up pointers and indices of first and last files to concat */ new_result = &trans1; old_result = &trans2; input = &trans3; first_arg = 1; last_arg = argc-2; outfile = argv[argc-1]; /* check for the outfile */ if(access(outfile, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists! (use -clobber to overwrite)\n\n", pname, outfile); exit(EXIT_FAILURE); } /* Loop through arguments */ for (iarg=first_arg; iarg <= last_arg; iarg++) { /* Read in file to concatenate */ if (input_transform_file(argv[iarg], input) != VIO_OK) { (void) fprintf(stderr, "%s: Error reading transform file %s\n", pname, argv[iarg]); exit(EXIT_FAILURE); } /* Concatenate the transform */ temp_result = new_result; new_result = old_result; old_result = temp_result; if (iarg == first_arg) { copy_general_transform(input, new_result); } else { concat_general_transforms(old_result, input, new_result); delete_general_transform(old_result); } delete_general_transform(input); } /* End of loop through arguments */ /* Write out the transform */ if (output_transform_file(outfile, arg_string, new_result) != VIO_OK) { (void) fprintf(stderr, "%s: Error writing transform file %s\n", pname, outfile); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/xfm/xfm2def.c0000644000175000000620000001547112574624760017255 0ustar stevestaff/* * xfm2def.c * * Approximates a deformation grid from an input transformation * * Copyright Andrew Janke - a.janke@gmail.com * 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 makes 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 #include #include #include #define WORLD_NDIMS 3 #define DEF_BOOL -1 static char *std_dimorder_v[] = { MIxspace, MIyspace, MIzspace, MIvector_dimension }; /* argument variables and table */ static int verbose = FALSE; static int clobber = FALSE; static nc_type dtype = NC_SHORT; static int is_signed = FALSE; static int nelem[WORLD_NDIMS + 1] = { 100, 100, 100, 3 }; static double start[WORLD_NDIMS] = { -50.0, -50.0, -50.0 }; static double step[WORLD_NDIMS] = { 1.0, 1.0, 1.0 }; static double dircos[WORLD_NDIMS][WORLD_NDIMS] = { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0} }; static ArgvInfo argTable[] = { {"-verbose", ARGV_CONSTANT, (char *)TRUE, (char *)&verbose, "Print out extra information."}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "Overwrite existing files."}, {NULL, ARGV_HELP, NULL, NULL, "\nOuput grid Options"}, {"-byte", ARGV_CONSTANT, (char *)NC_BYTE, (char *)&dtype, "Create a byte data."}, {"-short", ARGV_CONSTANT, (char *)NC_SHORT, (char *)&dtype, "Create a short integer data.(Default)"}, {"-int", ARGV_CONSTANT, (char *)NC_INT, (char *)&dtype, "Create a 32-bit integer"}, {"-float", ARGV_CONSTANT, (char *)NC_FLOAT, (char *)&dtype, "Create a single-precision data."}, {"-double", ARGV_CONSTANT, (char *)NC_DOUBLE, (char *)&dtype, "Create a double-precision data."}, {"-signed", ARGV_CONSTANT, (char *)TRUE, (char *)&is_signed, "Create a signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *)FALSE, (char *)&is_signed, "Create a unsigned integer data. (Default)"}, {"-xnelements", ARGV_INT, (char *)1, (char *)&nelem[0], "Number of samples in x dimension."}, {"-ynelements", ARGV_INT, (char *)1, (char *)&nelem[1], "Number of samples in y dimension."}, {"-znelements", ARGV_INT, (char *)1, (char *)&nelem[2], "Number of samples in z dimension."}, {"-xstart", ARGV_FLOAT, (char *)1, (char *)&start[0], "Starting coordinate for x dimension."}, {"-ystart", ARGV_FLOAT, (char *)1, (char *)&start[1], "Starting coordinate for y dimension."}, {"-zstart", ARGV_FLOAT, (char *)1, (char *)&start[2], "Starting coordinate for z dimension."}, {"-xstep", ARGV_FLOAT, (char *)1, (char *)&step[0], "Step size for x dimension."}, {"-ystep", ARGV_FLOAT, (char *)1, (char *)&step[1], "Step size for y dimension."}, {"-zstep", ARGV_FLOAT, (char *)1, (char *)&step[2], "Step size for z dimension."}, {"-xdircos", ARGV_FLOAT, (char *)3, (char *)dircos[0], "Direction cosines along the x dimension"}, {"-ydircos", ARGV_FLOAT, (char *)3, (char *)dircos[1], "Direction cosines along the y dimension"}, {"-zdircos", ARGV_FLOAT, (char *)3, (char *)dircos[2], "Direction cosines along the z dimension"}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { char *xfm_fn; char *out_fn; char *history; progress_struct progress; Volume def_grid; VIO_General_transform xfm; int x, y, z, v; double vcoord[4], wcoord[3], wcoord_t[3]; double min, max; double value; int i; /* get the history string */ history = time_stamp(argc, argv); /* get args and file names */ if(ParseArgv(&argc, argv, argTable, 0) || (argc != 3)){ fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); fprintf(stderr, " %s [-help] %d\n\n", argv[0], argc); exit(EXIT_FAILURE); } xfm_fn = argv[1]; out_fn = argv[2]; /* check for infile and outfile */ if(access(xfm_fn, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find input xfm %s.\n\n", argv[0], xfm_fn); exit(EXIT_FAILURE); } if(access(out_fn, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists, -clobber to overwrite.\n\n", argv[0], out_fn); exit(EXIT_FAILURE); } /* read in the input transformation */ if(input_transform_file(xfm_fn, &xfm) != OK){ fprintf(stderr, "%s: Error reading in xfm %s\n\n", argv[0], xfm_fn); } /* create the def_grid volume */ def_grid = create_volume(4, std_dimorder_v, dtype, is_signed, 0.0, 0.0); set_volume_sizes(def_grid, nelem); set_volume_starts(def_grid, start); set_volume_separations(def_grid, step); for(i = 0; i < WORLD_NDIMS; i++){ set_volume_direction_cosine(def_grid, i, dircos[i]); } alloc_volume_data(def_grid); /* generate the grid itself */ min = DBL_MAX; max = -DBL_MAX; vcoord[3] = 0; initialize_progress_report(&progress, FALSE, nelem[0], "Creating grid"); for(x = nelem[0]; x--;){ for(y = nelem[1]; y--;){ for(z = nelem[2]; z--;){ /* figure out where we are in world space */ vcoord[0] = x; vcoord[1] = y; vcoord[2] = z; convert_voxel_to_world(def_grid, vcoord, &wcoord[0], &wcoord[1], &wcoord[2]); /* transform that */ general_transform_point(&xfm, wcoord[0], wcoord[1], wcoord[2], &wcoord_t[0], &wcoord_t[1], &wcoord_t[2]); /* write out dx, dy and dz */ for(v = nelem[3]; v--;){ value = wcoord_t[v] - wcoord[v]; if(value < min){ min = value; } if(value > max){ max = value; } set_volume_real_value(def_grid, x, y, z, v, 0, value); } } } update_progress_report(&progress, x + 1); } terminate_progress_report(&progress); /* set the range */ if(verbose){ fprintf(stdout, " + data range: [%g:%g]\n", min, max); } set_volume_real_range(def_grid, min, max); /* output the result */ if(verbose){ fprintf(stdout, "Outputting %s...\n", out_fn); } if(output_volume(out_fn, dtype, is_signed, 0.0, 0.0, def_grid, history, NULL) != OK){ fprintf(stderr, "Problems outputing: %s\n\n", out_fn); exit(EXIT_FAILURE); } /* tidy up */ delete_volume(def_grid); delete_general_transform(&xfm); return (EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/xfm/xfmflip.man10000644000175000000620000000170012574624760017767 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH XFMFLIP 1 "$Date: 2007-08-24 04:05:44 $" "" "MINC User's Guide" .SH NAME xfmflip \- flip an MNI transform file .SH SYNOPSIS \fBxfmflip\fR\ \fIin.xfm\fR\ \fIout.xfm\fR .SH DESCRIPTION \fIxfmflip\fR flips an input transform \fIinput.xfm\fR about an axis (x, y or z) and puts the result in the file \fIresult.xfm\fR. The default flip is about the X axis (Left-Right) but can be changed to the Y axis (via the -y option) or Z axis (-z) .SH OPTIONS .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .TP \fB\-verbose\fR Print out extra progress information while running. .TP \fB\-clobber\fR Overwrite any existing output file .TP \fB\-fake\fR Dont actually run the commands, just echo them to STDOUT .SH AUTHOR Andrew Janke Vladimir Fonov .SH COPYRIGHTS Copyright \(co 2007 Andrew Janke - a.janke@gmail.com minc-tools-2.3.00+dfsg/progs/xfm/transformtags.c0000644000175000000620000001344212574624760020610 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : transformtags.c @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to transform a tag file using a transform file @METHOD : @GLOBALS : @CALLS : @CREATED : September 13, 1993 (Peter Neelin) @MODIFIED : * $Log: transformtags.c,v $ * Revision 6.4 2008-01-17 02:33:06 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:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.2 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.1 1999/10/19 14:45:31 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:28 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:29 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:08 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:03 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:33:24 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:33:21 neelin * Pre-release * * Revision 1.4 93/10/12 12:52:16 neelin * Replaced def_mni.h with volume_io.h * * Revision 1.3 93/09/16 09:39:00 neelin * Use dave's open_file_with_default_suffix and input_transform_file and * output_transform_file to add suffixes to file names. * * Revision 1.2 93/09/14 09:50:20 neelin * changed name to from tagtransform to transformtags * * Revision 1.1 93/09/13 16:16:55 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Variables for argument parsing */ int volume_to_transform = 2; char *xfmfile = NULL; /* Argument table */ ArgvInfo argTable[] = { {"-vol1", ARGV_CONSTANT, (char *) 1, (char *) &volume_to_transform, "VIO_Transform tags for volume 1."}, {"-vol2", ARGV_CONSTANT, (char *) 2, (char *) &volume_to_transform, "VIO_Transform tags for volume 2 (default)."}, {"-transformation", ARGV_STRING, (char *) NULL, (char *) &xfmfile, "Name of transformation file (default = identity)."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *pname, *intagfile, *outtagfile; int n_volumes, n_tag_points, ipoint; VIO_Real **tags_volume1, **tags_volume2, **tag_list; VIO_General_transform transform; FILE *fp; char comment_string[512]; char *comment = comment_string; VIO_Real *weights; int *structure_ids; int *patient_ids; char **labels; /* Parse arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { (void) fprintf(stderr, "\nUsage: %s [] infile.tag outfile.tag\n\n", argv[0]); exit(EXIT_FAILURE); } intagfile = argv[1]; outtagfile = argv[2]; /* Read in tag file */ if ((open_file_with_default_suffix(intagfile, get_default_tag_file_suffix(), READ_FILE, ASCII_FORMAT, &fp) != VIO_OK) || (input_tag_points(fp, &n_volumes, &n_tag_points, &tags_volume1, &tags_volume2, &weights, &structure_ids, &patient_ids, &labels) != VIO_OK)) { (void) fprintf(stderr, "%s: Error reading tag file %s\n", pname, intagfile); exit(EXIT_FAILURE); } (void) close_file(fp); /* Check number of volumes */ if (n_volumes > 2) { (void) fprintf(stderr, "%s: Wrong number of volumes in %s\n", pname, intagfile); exit(EXIT_FAILURE); } /* Get the list of tags to transform */ if (n_volumes == 1) volume_to_transform = 1; if (volume_to_transform == 1) { tag_list = tags_volume1; } else { tag_list = tags_volume2; } /* Get the transform */ if (xfmfile == NULL) { create_linear_transform(&transform, NULL); } else { if (input_transform_file(xfmfile, &transform) != VIO_OK) { (void) fprintf(stderr, "%s: Error reading transform file %s\n", pname, xfmfile); exit(EXIT_FAILURE); } } /* VIO_Transform the points */ for (ipoint=0; ipoint < n_tag_points; ipoint++) { general_transform_point(&transform, tag_list[ipoint][0], tag_list[ipoint][1], tag_list[ipoint][2], &tag_list[ipoint][0], &tag_list[ipoint][1], &tag_list[ipoint][2]); } /* Create a comment for the new file */ if (xfmfile != NULL) { (void) sprintf(comment, " Volume %d points transformed using xfm file %s", volume_to_transform, xfmfile); } else { comment = NULL; } /* Write out the new tag file */ if ((open_file_with_default_suffix(outtagfile, get_default_tag_file_suffix(), WRITE_FILE, ASCII_FORMAT, &fp) != VIO_OK) || (output_tag_points(fp, comment, n_volumes, n_tag_points, tags_volume1, tags_volume2, weights, structure_ids, patient_ids, labels) != VIO_OK)) { (void) fprintf(stderr, "%s: Error writing new tag file %s\n", pname, outtagfile); exit(EXIT_FAILURE); } (void) close_file(fp); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/xfm/xfm2def.man10000644000175000000620000000427012574624760017662 0ustar stevestaff.TH XFM2DEF 1 "$Date: 2010-08-11 04:06:42 $" "" "MINC User's Guide" .SH NAME xfm2def \- convert a MNI transform file to a deformation volume .SH SYNOPSIS \fBxfm2def\fR\ [options] \fIinput.xfm\fR\ \fIdef_vol.mnc\fR .SH DESCRIPTION \fIxfm2def\fR takes an input transform \fIinput.xfm\fR that can consist of a series of concatenated transforms in a single xfm file and converts this into a single deformation volume that is output as \fIdef_vol.mnc\fR. Note that the output is not a tranform file itself, you can then make use of this volume in a MINC tranform by creating an .xfm file as such: MNI Transform File Transform_Type = Grid_Transform; Displacement_Volume = def_vol.mnc; The output resolution and sampling of the deformation grid must be specified or a default size will be used. (100x100x100 1mm steps centred around the origin). .SH OPTIONS .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .TP \fB\-verbose\fR Print out extra progress information while running. .TP \fB\-clobber\fR Overwrite any existing output file .TP \fB\-xnelements\fR\ \fInx\fR Number of elements along the xspace dimension. .TP \fB\-ynelements\fR\ \fIny\fR Number of elements along the yspace dimension. .TP \fB\-znelements\fR\ \fInz\fR Number of elements along the zspace dimension. .TP \fB\-xstep\fR\ \fIxstep\fR Step between voxels along the xspace dimension. .TP \fB\-ystep\fR\ \fIystep\fR Step between voxels along the yspace dimension. .TP \fB\-zstep\fR\ \fIzstep\fR Step between voxels along the zspace dimension. .TP \fB\-xstart\fR\ \fIxstart\fR Position of centre of first voxel along the xspace dimension. .TP \fB\-ystart\fR\ \fIystart\fR Position of centre of first voxel along the yspace dimension. .TP \fB\-zstart\fR\ \fIzstart\fR Position of centre of first voxel along the zspace dimension. .TP \fB\-xdircos\fR\ \fIx1\ x2\ x3\fR Direction cosines for the xspace dimension. .TP \fB\-ydircos\fR\ \fIy1\ y2\ y3\fR Direction cosines for the yspace dimension. .TP \fB\-zdircos\fR\ \fIz1\ z2\ z3\fR Direction cosines for the zspace dimension. .SH AUTHOR Andrew Janke .SH COPYRIGHTS Copyright \(co 2010 Andrew Janke - a.janke@gmail.com minc-tools-2.3.00+dfsg/progs/rawtominc/0002755000175000000620000000000012574624760016761 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/rawtominc/rawtominc.man10000644000175000000620000003200412574624760021537 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" 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. .\" .\" $Header: /private-cvsroot/minc/progs/rawtominc/rawtominc.man1,v 6.6 2005-02-09 19:27:18 bert Exp $ .\" .TH RAWTOMINC 1 "$Date: 2005-02-09 19:27:18 $" "" "MINC User's Guide" .SH NAME rawtominc - converts a stream of binary image data to a minc format file .SH SYNOPSIS .B rawtominc [\fIoptions\fR] \fIoutput.mnc\fR [[\fIsz4\fR] \fIsz3\fR] \fIsz2\fR \fIsz1\fR .SH DESCRIPTION .I Rawtominc reads a stream of binary data (byte, short, long, float or double) from standard input (unless the \fB\-input\fR option is used) and writes it into the minc format file \fIoutput.mnc\fR. The user specifies the dimension sizes from slowest varying to fastest varying. At least two dimensions must be given (an image) but there can be up to four. Options give the user control over dimension names, data types and voxel to world coordinate conversion. Vector type data (such as RGB pixel data) can be read in as well. .SH PIXEL VALUE SPECIFICATION Pixel values are specified by a type and a sign (e.g. signed short integer). They are also characterized by a range of legal values. For example, many scanners produce images stored with short integer pixel values. Some have values in the range 0 to 4095, others 0 to 32000, others -32768 to 32767. This range is the valid range, specified by the \fB\-range\fR option (for floating point values, the valid range is the maximum and minimum of the whole dataset). \fIRawtominc\fR allows the user to specify both the input type, sign and range as well as the output type, sign and range (read short values, store byte values, for example). .P There is a further twist. Integer pixel values are generally taken to be simply scaled pixel representations of real (meaningful) physical values. Floating point values are taken to be the real value itself. Thus floating point values are scanned for the maximum and minimum, since they could be anything (they are stored in the MINC variables \fBimage-max\fR and \fBimage-min\fR). Integer values, however, are not scanned by default, since their range can be given by an option. To force scanning of integer values when the maximum and minimum are not known (some scanners produce files with variable ranges), use the option \fB\-scan_range\fR. .SH WORLD COORDINATES World coordinates refer to millimetric coordinates relative to some physical origin (either the scanner or some anatomical structure). Voxel coordinates are simply the indices into the image volume of a given voxel. It is worth describing briefly how MINC coordinate conversions work since this will affect how successful the new MINC file will be. Each dimension of MINC image is specified by name - the spatial dimensions are \fIxspace\fR, \fIyspace\fR and \fIzspace\fR. The convention is that positive \fIxspace\fR coordinates run from the patient's left side to right side, positive \fIyspace\fR coordinates run from patient posterior to anterior and positive \fIzspace\fR coordinates run from inferior to superior. For each of these spatial dimensions, the world coordinate conversion is specified by a pair of attributes: \fIstep\fR and \fIstart\fR. The \fIxspace\fR world coordinate, for example, is calculated using x = v*step + start, where x is the x world coordinate and v is the voxel count (starting at zero). Thus the magnitude of the \fIstep\fR attribute specifies the distance between voxels and the sign of the \fIstep\fR attribute specifies the orientation of the axis. Programs will use this information to display images with the correct aspect ratio and orientation, so make sure that you get it right. Many scanners store transverse images with the first pixel at the patient's anterior/right side, so it would be necessary to give negative x and y step values. Other conventions have the opposite: first pixel at patient's posterior/left, so step values are positive. Sometimes the first slice is inferior, so the z step should be positive. Other times it is superior, so z step is negative. The image axes do not have to be aligned with the world coordinate axes. The axis directions are recorded in the file as direction cosines - unit vectors - one for each spatial axis. In this case, the \fIstep\fR and \fIstart\fR attributes described in the previous paragraph refer to distances along the axis, not to coordinates of the first voxel. This makes them invariant under a change of axis direction (the whole coordinate system can in fact be rotated just by changing the direction cosines). If the coordinate of the first voxel is known, then it can be converted (projected) to a set of start values by using the \fB\-origin\fR option. .SH OPTIONS .SH Dimension ordering .TP \fB\-transverse\fR Transverse images : [[time] z] y x (Default) .TP \fB\-sagittal\fR Sagittal images : [[time] x] z y .TP \fB\-coronal\fR Coronal images : [[time] y] z x .TP \fB\-time\fR Time ordered images : [[z] time] y x .TP \fB\-xyz\fR Dimension order : [[time] x] y z .TP \fB\-xzy\fR Dimension order : [[time] x] z y .TP \fB\-yxz\fR Dimension order : [[time] y] x z .TP \fB\-yzx\fR Dimension order : [[time] y] z x .TP \fB\-zxy\fR Dimension order : [[time] z] x y .TP \fB\-zyx\fR Dimension order : [[time] z] y x .TP \fB\-dimorder\fR\ \fIdim1\fR,\fIdim2\fR[,\fIdim3\fR[,\fIdim4\fR]] Specify an arbitrary dimension order, given by an comma-separated list of between 2 and 4 dimension names. .TP \fB\-vector\fR\ \fIsize\fR Gives the size of a vector dimension (always the fastest varying dimension). Default is no vector dimension. .SH Input data type and range .TP \fB-byte\fR 8-bit integer values (default). .TP \fB\-short\fR 16-bit integer values. .TP \fB\-int\fR 32-bit integer values. .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB\-float\fR Single-precision floating point values. .TP \fB\-double\fR Double-precision floating point values. .TP \fB\-signed\fR Values are signed integers (default for short and long). Ignored for floating point types. .TP \fB\-unsigned\fR Values are unsigned integers (default for byte). Ignored for floating point types. .TP \fB\-range\fR\ \fImin\fR \fImax\fR specifies the valid range of pixel values. Default is the full range for the type and sign. This option is ignored for floating point values. .TP \fB\-real_range\fR\ \fImin\fR \fImax\fR specifies the real range of image values that corresponds to the pixel values of option \fB\-range\fR. Default is to not store the real image minimum and maximum. If \fB\-scan_range\fR is used, then the image minimum and maximum corresponding to the scanned pixel minimum and maximum are calculated and stored. This option is ignored for floating point values. .TP \fB\-swap_bytes\fR Input values (either \fB\-short\fR or \fB\-int\fR) need to be converted between Motorola (big-endian) and Intel (little-endian) data format. If "short" input is specified, adjacent bytes are swapped. If "int" input is specified, inner and outer byte pairs are swapped. This option has no effect with other input types. .SH Output data type and range .TP \fB\-obyte\fR Store 8-bit integer values (default is input type). .TP \fB\-oshort\fR Store 16-bit integer values (default is input type). .TP \fB\-oint\fR Store 32-bit integer values (default is input type). .TP \fB\-olong\fR Superseded by \fB\-oint\fR. .TP \fB\-ofloat\fR Single-precision floating point values (default is input type). .TP \fB\-odouble\fR Double-precision floating point values (default is input type). .TP \fB\-osigned\fR Values are signed integers (default for short and long). Ignored for floating point types. If output type is not specified, then default is input sign type. .TP \fB\-ounsigned\fR Values are unsigned integers (default for byte). Ignored for floating point types. If output type is not specified, then default is input sign type. .TP \fB\-orange\fR\ \fImin\fR \fImax\fR specifies the valid range of pixel values. Default is the full range for the type and sign. This option is ignored for floating point values. If output type and sign are not specified, then the default is the input range. .SH Scanning integers for range .TP \fB\-noscan_range\fR Do not scan integer values for their minimum and maximum - assume that the -range option gives the appropriate range of pixel values (default). No rescaling of pixel values is done (unless the output type differs from the input type) and the created images are assumed to have a real (not pixel value) minimum and maximum of zero and one. .TP \fB\-scan_range\fR Integer values are scanned for their minimum and maximum. Pixel values are rescaled to give the full range of pixel values and the real minimum and maximum are set to the pixel minimum and maximum (unless -real_range is used). This should be equivalent to converting the input to a floating point type and reading it in with -float -oshort (for example) assuming that -real_range is not used. .SH Writing output file .TP \fB\-2\fR Create MINC 2.0 format output files. .TP \fB\-clobber\fR Overwrite existing minc file (default). .TP \fB-noclobber\fR Don't overwrite existing minc file. .SH Reading from input file .TP \fB\-input\fR\ \fIinputfile\fR Read input data from \fIinputfile\fR instead of standard input. .TP \fB\-skip\fR\ \fIlength\fR Skip the first \fIlength\fR bytes of the input. .SH World coordinate conversion .TP \fB\-xstep\fR\ \fIxstep\fR Step size for x dimension (default = none). .TP \fB\-ystep\fR\ \fIystep\fR Step size for y dimension (default = none). .TP \fB\-zstep\fR\ \fIzstep\fR Step size for z dimension (default = none). .TP \fB\-xstart\fR\ \fIxstart\fR Starting coordinate for x dimension (default = none). This is a distance parallel to the axis. .TP \fB\-ystart\fR\ \fIystart\fR Starting coordinate for y dimension (default = none). This is a distance parallel to the axis. .TP \fB\-zstart\fR\ \fIzstart\fR Starting coordinate for z dimension (default = none). This is a distance parallel to the axis. .TP \fB\-xdircos\fR\ \fIx1\fR\ \fIx2\fR\ \fIx3\fR Direction cosines for x dimension (default = none). .TP \fB\-ydircos\fR\ \fIy1\fR\ \fIy2\fR\ \fIy3\fR Direction cosines for y dimension (default = none). .TP \fB\-zdircos\fR\ \fIz1\fR \fIz2\fR\ \fIz3\fR Direction cosines for z dimension (default = none). .TP \fB\-origin\fR \fIo1\fR\ \fIo2\fR\ \fIo3\fR Specify the spatial coordinates of the first voxel. If the direction cosines are not given or are the default ones, this option will give the same results as using the -start options. Otherwise, the coordinate is projected parallel to the axes to determine the appropriate start values. .SH Frame time and length specification .TP \fB\-frame_times\fR \fIt1\fR,\fIt2\fR,\fIt3\fR,... Specify the start of each time frame. The number of values given must be equal to the length of the time dimension specified on the command line. All of the values given must be in one argument (no spaces between them, or the string must be quoted). Separation by spaces instead of commas is permitted. .TP \fB\-frame_widths\fR\ \fIw1\fR,\fIw2\fR,\fIw3\fR,... Specify the length of each time frame. The comments for \fI-frame_times\fR apply here as well. .P To set the start and step values for a functional file with a constant frame times, use the \fI\-dattribute\fR\ flag described below as follows: -dattribute time:step=1 -dattribute time:start=0 .SH Imaging modality .TP \fB-nomodality\fR Do not store modality type in file (default). .TP \fB\-pet\fR PET data. .TP \fB\-mri\fR MRI data. .TP \fB\-spect\fR SPECT data. .TP \fB\-gamma\fR Data from a gamma camera. .TP \fB\-mrs\fR MR spectroscopy data. .TP \fB\-mra\fR MR angiography data. .TP \fB\-ct\fR CT data. .TP \fB-dsa\fR DSA data .TP \fB\-dr\fR Digital radiography data. .SH Attribute specification .TP \fB\-sattribute\fR \fIvariable\fR:\fIattribute\fR=\fIvalue\fR Specify that \fIvariable\fR should be created with string \fIattribute\fR set to \fIvalue\fR. The complete specification, including \fIvariable\fR, \fIattribute\fR and \fIvalue\fR, should be contained in only one argument to the program - quoting may be needed for strings containing blanks. .TP \fB\-dattribute\fR\ \fIvariable\fR:\fIattribute\fR=\fIvalue\fR : Like \fB\-sattribute\fR, but for specifying double-precision attribute values. .TP \fB\-attribute\fR\ \fIvariable\fR:\fIattribute\fR=\fIvalue\fR Like \fB\-sattribute\fR or \fB\-dattribute\fR, except that the type is chosen by first trying to interpret the value as double precision - if that fails, then the value is assumed to be a string. .SH Generic options .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin minc-tools-2.3.00+dfsg/progs/rawtominc/rawtominc.c0000644000175000000620000020061512574624760021132 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : rawtominc @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts a raw volume of data values (read from standard input) to a minc format file. @METHOD : @GLOBALS : @CALLS : @CREATED : September 25, 1992 (Peter Neelin) @MODIFIED : * $Log: rawtominc.c,v $ * Revision 6.29 2008-08-13 06:26:29 rotor * * added Claudes (many) 64 bit fixes to dicom code and updates * * 64 bit fix for rawtominc's use of ParseArgv * * Revision 6.28 2008/01/17 02:33:06 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.27 2008/01/13 04:30:28 stever * Add braces around static initializers. * * Revision 6.26 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.25 2008/01/11 07:17:08 stever * Remove unused variables. * * Revision 6.24 2007/02/20 18:24:45 baghdadi * make sure the global scaling happens when input is float and output is float or double * * Revision 6.23 2007/02/02 18:53:46 baghdadi * Create global scaling for float dat * * Revision 6.22 2007/01/09 21:56:59 baghdadi * Added -2 option for minc2.0 support * * Revision 6.21 2006/07/28 18:17:15 baghdadi * Added -like option to list of arguments similar behaviour like mincresample. * * Revision 6.20 2006/05/11 15:09:57 claude * added float and double to -swap_bytes * * Revision 6.13.2.2 2005/03/16 19:02:52 bert * Port changes from 2.0 branch * * Revision 6.13.2.1 2004/09/28 20:07:30 bert * Minor portability fixes for Windows * * Revision 6.13 2004/02/02 18:24:58 bert * Include a ARGV_VERINFO record in the argTable[] * * Revision 6.12 2003/11/14 16:52:24 stever * More last-minute fixes. * * Revision 6.11 2003/11/03 19:43:18 bert * Handle unspecified dimension order correctly * * Revision 6.10 2003/10/29 17:50:18 bert * Added -dimorder option * * Revision 6.9 2003/10/21 22:22:09 bert * Added -swap_bytes option for int or short input, per A. Janke. * * Revision 6.8 2002/08/05 00:53:50 neelin * Added slightly modified code from Colin Holmes to support -skip option * * Revision 6.7 2001/09/18 15:33:00 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:37 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/04/24 13:38:46 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.4 2001/04/17 18:40:25 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.3 1999/10/19 14:45:31 neelin * Fixed Log subsitutions for CVS * * Revision 6.2 1998/06/22 14:06:01 neelin * Fixed bug in handling of default types and signs. * * Revision 6.1 1997/09/29 12:22:46 neelin * Clarified argument error messages. * * Revision 6.0 1997/09/12 13:23:25 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 19:59:55 neelin * Release of minc version 0.4 * * Revision 3.3 1997/04/21 20:19:17 neelin * Added -origin option. * * Revision 3.2 1996/06/19 18:24:16 neelin * Check errors on fopen when -input is used. * Try opening input before creating minc file. * * Revision 3.1 1995/11/16 13:11:16 neelin * Added include of math.h to get declaration of strtod under SunOs * * Revision 3.0 1995/05/15 19:31:00 neelin * Release of minc version 0.3 * * Revision 2.7 1995/03/30 13:00:05 neelin * Added -sattribute and -dattribute options. * * Revision 2.6 1995/02/15 18:10:35 neelin * Added check for global attribute specified with -attribute. * * Revision 2.5 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.4 1995/01/23 09:07:46 neelin * changed ncclose to miclose. * * Revision 2.3 95/01/23 08:59:31 neelin * Changed nccreate to micreate * * Revision 2.2 95/01/03 13:09:24 neelin * Added direction cosine support. * * Revision 2.1 94/10/11 16:18:50 neelin * Fixed scanning of integers for max and min (conversion to double had * signed and unsigned reversed). * * Revision 2.0 94/09/28 10:33:01 neelin * Release of minc version 0.2 * * Revision 1.15 94/09/28 10:32:55 neelin * Pre-release * * Revision 1.14 94/09/23 08:30:52 neelin * Added -xyz, etc options for image/volume orientation. * * Revision 1.13 94/06/10 15:24:41 neelin * Added option -real_range. * * Revision 1.12 94/05/05 10:31:53 neelin * Added -scan_range, -frame_time, -frame_width, -input, -attribute and * modality options. * * Revision 1.11 93/10/06 10:14:17 neelin * Added include of string.h. * * Revision 1.10 93/08/11 15:25:05 neelin * Added RCS logging to source. * December 2, 1992 (P.N.) - changed to parse argv with ParseArgv @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 "config.h" #include #include #include #include #include #include #include #if HAVE_UNISTD_H #define __USE_XOPEN #include #endif /* HAVE_UNISTD_H */ #include #include #include /* Some constants */ #define VECTOR_SEPARATOR ',' #define NORMAL_STATUS 0 #define ERROR_STATUS 1 #define MIN_DIMS 2 #define MAX_DIMS 4 #define DEF_TYPE 0 #define BYTE_TYPE 1 #define SHORT_TYPE 2 #define INT_TYPE 3 #define FLOAT_TYPE 4 #define DOUBLE_TYPE 5 #define DEF_SIGN 0 #define SIGNED 1 #define UNSIGNED 2 #define TRUE 1 #define FALSE 0 #define X 0 #define Y 1 #define Z 2 #define WORLD_NDIMS 3 #define DEF_STEP DBL_MAX #define DEF_START DBL_MAX #define DEF_RANGE DBL_MAX #define DEF_DIRCOS DBL_MAX #define DEF_ORIGIN DBL_MAX #define ARG_SEPARATOR ',' /* LB. needed for volume_def */ #define VOL_NDIMS 3 /* Number of volume dimensions */ #define WORLD_NDIMS 3 /* Number of world spatial dimensions */ int Specified_like = FALSE; /* Macros */ #define STR_EQ(s1,s2) (strcmp(s1,s2)==0) /* LB. For referring to world axes in arrays subscripted by WORLD_NDIMS */ #define NO_AXIS -1 #define XAXIS 0 #define YAXIS 1 #define ZAXIS 2 typedef struct { char *name; int mincid; int imgid; int maxid; int minid; int ndims; nc_type datatype; int is_signed; double vrange[2]; /* [0]=min, [1]=max */ long nelements[MAX_VAR_DIMS]; /* Size of each dimension */ int world_axes[MAX_VAR_DIMS]; /* Relates variable index to X, Y, Z or NO_AXIS */ int indices[VOL_NDIMS]; /* Indices of volume dimenions (subscripted from slowest to fastest) */ int axes[WORLD_NDIMS]; /* Relates world X,Y,Z (index) to dimension order (value=0,1,2; 0=slowest varying) */ int using_icv; /* True if we are using an icv to read data */ int icvid; /* Id of icv (if used) */ long slices_per_image; /* Number of volume slices (row, column) per minc file image */ long images_per_file; /* Number of minc file images in the file */ int do_slice_renormalization; /* Flag indicating that we need to loop through the data a second time, recomputing the slices to normalize images properly */ int keep_real_range; /* Flag indicating whether we should keep the real range of the input data or not */ } File_Info; typedef struct { int axes[WORLD_NDIMS]; /* Relates world X,Y,Z (index) to dimension order (value=0,1,2; 0=slowest varying) */ long nelements[WORLD_NDIMS]; /* These are subscripted by X, Y and Z */ double step[WORLD_NDIMS]; double start[WORLD_NDIMS]; double dircos[WORLD_NDIMS][WORLD_NDIMS]; double *coords[WORLD_NDIMS]; char units[WORLD_NDIMS][MI_MAX_ATTSTR_LEN]; char spacetype[WORLD_NDIMS][MI_MAX_ATTSTR_LEN]; } Volume_Definition; /* Function declarations */ static void parse_args(int argc, char *argv[]); static void usage_error(char *pname); static int get_attribute(char *dst, char *key, char *nextarg); static int get_times(char *dst, char *key, char *nextarg); static int get_axis_order(char *dst, char *key, char *nextArg); /* LB. function prototypes */ static void get_file_info(char *filename, int initialized_volume_def, Volume_Definition *volume_def, File_Info *file_info); static int get_model_file(char *dst, char *key, char *nextArg); /* Array containing information about signs. It is subscripted by [signtype][type]. Note that the first row should never be used, since default_signs should be used instead. */ char *sign_names[][6] = { {MI_SIGNED, MI_UNSIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED}, {MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED}, {MI_SIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED} }; /* Array containing default signs subscripted by [type] (default, byte, short, int, float, double) */ int default_signs[] = { SIGNED, UNSIGNED, SIGNED, SIGNED, SIGNED, SIGNED }; /* Information converting types for argument processing to NetCDF types */ nc_type convert_types[] = { MI_ORIGINAL_TYPE, NC_BYTE, NC_SHORT, NC_INT, NC_FLOAT, NC_DOUBLE }; /* enum for modality types to avoid problems with passing pointers in ParseArgv */ typedef enum { MODALITY_NONE, MODALITY_PET, MODALITY_MRI, MODALITY_SPECT, MODALITY_GAMMA, MODALITY_MRS, MODALITY_MRA, MODALITY_CT, MODALITY_DSA, MODALITY_DR } Modality_code; /* Argument variables */ char *pname; char *filename; int clobber=FALSE; #if MINC2 int v2format=FALSE; /* Version 2.0 file format? */ #endif /* MINC2 */ char *dimname[MAX_VAR_DIMS]; long dimlength[MAX_VAR_DIMS]; int ndims; /* LB. */ int NDims=0; int type = BYTE_TYPE; int signtype = DEF_SIGN; nc_type datatype; char *sign; double valid_range[2] = {0.0, -1.0}; int vrange_set; int otype = DEF_TYPE; int osigntype = DEF_SIGN; nc_type odatatype; char *osign; double ovalid_range[2] = {0.0, -1.0}; int ovrange_set; double dimstep[3] = {DEF_STEP, DEF_STEP, DEF_STEP}; double dimstart[3] = {DEF_START, DEF_START, DEF_START}; double dimdircos[3][3] = { { DEF_DIRCOS, DEF_DIRCOS, DEF_DIRCOS }, { DEF_DIRCOS, DEF_DIRCOS, DEF_DIRCOS }, { DEF_DIRCOS, DEF_DIRCOS, DEF_DIRCOS } }; double origin[3] = {DEF_ORIGIN, DEF_ORIGIN, DEF_ORIGIN}; int vector_dimsize = -1; Modality_code modality = MODALITY_NONE; int attribute_list_size = 0; int attribute_list_alloc = 0; struct { char *variable; char *attribute; char *value; double double_value; } *attribute_list = NULL; int num_frame_times = 0; double *frame_times = NULL; int num_frame_widths = 0; double *frame_widths = NULL; char *inputfile = NULL; int do_minmax = FALSE; double real_range[2] = {DEF_RANGE, DEF_RANGE}; long skip_length; int swap_bytes = FALSE; char *axis_order[MAX_DIMS+1] = { MItime, MIzspace, MIyspace, MIxspace }; /* LB. */ static Volume_Definition volume_def; /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_VERINFO, (char *) VERSION, (char *) NULL, NULL}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Options to specify input dimension order (from slowest to fastest)."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, " Default = -transverse."}, {"-transverse", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Transverse images : [[time] z] y x"}, {"-sagittal", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Sagittal images : [[time] x] z y"}, {"-coronal", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Coronal images : [[time] y] z x"}, {"-time", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Time ordered images : [[z] time] y x"}, {"-xyz", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] x] y z"}, {"-xzy", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] x] z y"}, {"-yxz", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] y] x z"}, {"-yzx", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] y] z x"}, {"-zxy", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] z] x y"}, {"-zyx", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Dimension order : [[time] z] y x"}, {"-dimorder", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Arbitrary dimensions: ,[,[,:=)."}, {"-dattribute", ARGV_FUNC, (char *) get_attribute, NULL, "Set a double precision attribute (:=)."}, {"-attribute", ARGV_FUNC, (char *) get_attribute, NULL, "Set an attribute, guessing the type (:=)."}, {NULL, ARGV_HELP, NULL, NULL, "Options for specifying time dimension coordinates."}, {"-frame_times", ARGV_FUNC, (char *) get_times, NULL, "Specify the frame starting times (\",,,...\")."}, {"-frame_widths", ARGV_FUNC, (char *) get_times, NULL, "Specify the frame lengths (\",,,...\")."}, {"-like", ARGV_FUNC, (char *) get_model_file, (char *) &volume_def, "Specifies a model file."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { int do_vrange; int cdfid, imgid, maxid, minid, varid; int icv; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; long end[MAX_VAR_DIMS]; int dim[MAX_VAR_DIMS]; void *image; double imgmax, imgmin, value; long image_size, image_pix, nread, fastdim; int pix_size; int image_dims; int i, j; int index; FILE *instream; char *tm_stamp; int iatt; long time_start, time_count; int is_signed; void *ptr; int floating_type; int do_real_range; int status; double scale, offset, denom, pixel_min, pixel_max; double dircos[WORLD_NDIMS][WORLD_NDIMS]; int cflags; /* Save time stamp and args */ tm_stamp = time_stamp(argc, argv); /* Parse arguments */ parse_args(argc, argv); /* Do normalization if input type is float */ floating_type = (( datatype==NC_FLOAT) || ( datatype==NC_DOUBLE)); do_minmax = do_minmax || floating_type; /* Find max and min for vrange if output type is float */ do_vrange = do_minmax && (( odatatype==NC_FLOAT) || ( odatatype==NC_DOUBLE)); /* Did the user provide a real range */ do_real_range = (real_range[0] != DEF_RANGE) && !floating_type; /* Check that only start or origin are specified, not both */ for (i=0; i 0) { varid = micreate_std_variable(cdfid, MItime, NC_DOUBLE, 1, &dim[i]); } if (num_frame_widths > 0) { varid = micreate_std_variable(cdfid, MItime_width, NC_DOUBLE, 1, &dim[i]); } } else { varid = micreate_std_variable(cdfid, dimname[i], NC_INT, 0, NULL); /* Write out step and start and direction cosine */ if (STR_EQ(dimname[i], MIxspace)) index = X; if (STR_EQ(dimname[i], MIyspace)) index = Y; if (STR_EQ(dimname[i], MIzspace)) index = Z; if (dimstep[index] != DEF_STEP) { (void) miattputdbl(cdfid, varid, MIstep, dimstep[index]); } if (dimstart[index] != DEF_START) { (void) miattputdbl(cdfid, varid, MIstart, dimstart[index]); } if (dimdircos[index][0] != DEF_DIRCOS) { (void) ncattput(cdfid, varid, MIdirection_cosines, NC_DOUBLE, 3, dimdircos[index]); } } /* Set variables for looping through images */ start[i]=0; count[i]= ((i 0) { ndims++; image_dims++; dim[ndims-1] = ncdimdef(cdfid, MIvector_dimension, (long) vector_dimsize); start[ndims-1] = 0; count[ndims-1] = vector_dimsize; end[ndims-1] = vector_dimsize; } /* Create the modality attribute */ if (modality != MODALITY_NONE) { varid = micreate_group_variable(cdfid, MIstudy); switch (modality){ case MODALITY_PET: (void) miattputstr(cdfid, varid, MImodality, MI_PET); break; case MODALITY_MRI: (void) miattputstr(cdfid, varid, MImodality, MI_MRI); break; case MODALITY_SPECT: (void) miattputstr(cdfid, varid, MImodality, MI_SPECT); break; case MODALITY_GAMMA: (void) miattputstr(cdfid, varid, MImodality, MI_GAMMA); break; case MODALITY_MRS: (void) miattputstr(cdfid, varid, MImodality, MI_MRS); break; case MODALITY_MRA: (void) miattputstr(cdfid, varid, MImodality, MI_MRA); break; case MODALITY_CT: (void) miattputstr(cdfid, varid, MImodality, MI_CT); break; case MODALITY_DSA: (void) miattputstr(cdfid, varid, MImodality, MI_DSA); break; case MODALITY_DR: (void) miattputstr(cdfid, varid, MImodality, MI_DR); break; case MODALITY_NONE: default: (void) fprintf(stderr, "%s: Unknown Modality, this should not happen (call houston).\n", pname); exit(ERROR_STATUS); break; } } /* Create any special attributes */ ncopts = 0; for (iatt=0; iatt < attribute_list_size; iatt++) { if (strlen(attribute_list[iatt].variable) == 0) { varid = NC_GLOBAL; } else { varid = ncvarid(cdfid, attribute_list[iatt].variable); if (varid == MI_ERROR) { varid = micreate_group_variable(cdfid, attribute_list[iatt].variable); } if (varid == MI_ERROR) { varid = ncvardef(cdfid, attribute_list[iatt].variable, NC_INT, 0, NULL); } if (varid == MI_ERROR) { continue; } } if (attribute_list[iatt].value != NULL) { (void) miattputstr(cdfid, varid, attribute_list[iatt].attribute, attribute_list[iatt].value); } else { (void) miattputdbl(cdfid, varid, attribute_list[iatt].attribute, attribute_list[iatt].double_value); } } ncopts = NC_VERBOSE | NC_FATAL; /* Create the image */ if (do_minmax || do_real_range) { if (!floating_type || (otype != FLOAT_TYPE && otype != DOUBLE_TYPE)) { maxid = micreate_std_variable(cdfid, MIimagemax, NC_DOUBLE, ndims-image_dims, dim); minid = micreate_std_variable(cdfid, MIimagemin, NC_DOUBLE, ndims-image_dims, dim); } else { maxid = micreate_std_variable(cdfid, MIimagemax, NC_DOUBLE, 0, NULL); minid = micreate_std_variable(cdfid, MIimagemin, NC_DOUBLE, 0, NULL); } } imgid=micreate_std_variable(cdfid, MIimage, odatatype, ndims, dim); (void) miattputstr(cdfid, imgid, MIcomplete, MI_FALSE); (void) miattputstr(cdfid, imgid, MIsigntype, osign); if (ovrange_set) (void) miset_valid_range(cdfid, imgid, ovalid_range); /* End definition mode */ (void) ncendef(cdfid); /* Write out the frame times and widths */ time_start = 0; time_count = num_frame_times; if (num_frame_times > 0) { (void) mivarput(cdfid, ncvarid(cdfid, MItime), &time_start, &time_count, NC_DOUBLE, NULL, frame_times); } if (num_frame_widths > 0) { (void) mivarput(cdfid, ncvarid(cdfid, MItime_width), &time_start, &time_count, NC_DOUBLE, NULL, frame_widths); } /* Attach the icv */ (void) miicv_attach(icv, cdfid, imgid); /* Get a buffer for reading images */ image_pix = 1; for (i=1; i<=image_dims; i++) image_pix *= end[ndims-i]; pix_size=nctypelen(datatype); image_size=image_pix*pix_size; image=malloc(image_size); /* Loop through the images */ fastdim=ndims-image_dims-1; if (fastdim<0) fastdim=0; if (do_vrange) { ovalid_range[0]=DBL_MAX; ovalid_range[1]=(-DBL_MAX); } /* CJH - July 02 - Skip over any header bytes */ if (skip_length > 0) { /* First try seeking over the header */ if (fseek(instream, skip_length, SEEK_SET) == 0) { /* OK instream was a file and we skipped the requested bytes */ } else { char buffer[8192]; int bytes_copied = 0; while ( bytes_copied < skip_length ) { int bytes_to_read = sizeof(buffer); if ( bytes_to_read + bytes_copied > skip_length ) bytes_to_read = skip_length - bytes_copied; nread = fread(buffer, sizeof(char), bytes_to_read, instream); bytes_copied += nread; if (nread != bytes_to_read) { (void) fprintf(stderr, "%s: Premature end of file.\n", pname); perror("While skipping over small header"); exit(ERROR_STATUS); } } } } while (start[0] < end[0]) { /* Read in image */ nread=fread(image, pix_size, image_pix, instream); if (nread!=image_pix) { (void) fprintf(stderr, "%s: Premature end of file.\n", pname); exit(ERROR_STATUS); } /* If the user wants to swap bytes, do it here before any further * processing of the image. */ if (swap_bytes) { switch (datatype) { case NC_SHORT: /* Easy case - call swab() */ if ((image_size & 1) != 0) { fprintf(stderr, "%s: image size must be even for -swap_bytes\n", pname); exit(ERROR_STATUS); } else { swab(image, image, image_size); } break; case NC_INT: case NC_FLOAT: /* Harder case - have to do a more complex 4-byte swap. */ if ((image_size & 3) != 0) { fprintf(stderr, "%s: image size must be divisible by 4 for -swap_bytes!\n", pname); exit(ERROR_STATUS); } else { unsigned char *img_ptr = image; unsigned char *img_end = (unsigned char*)image + image_size; for ( ; img_ptr < img_end; img_ptr += 4) { unsigned char tmp; /* Swap the inner 2 bytes */ tmp = img_ptr[1]; img_ptr[1] = img_ptr[2]; img_ptr[2] = tmp; /* Swap the outer 2 bytes */ tmp = img_ptr[0]; img_ptr[0] = img_ptr[3]; img_ptr[3] = tmp; } } break; case NC_DOUBLE : /* Harder case - have to do a more complex 8-byte swap. */ if ((image_size & 7) != 0) { fprintf(stderr, "%s: image size must be divisible by 8 for -swap_bytes!\n", pname); exit(ERROR_STATUS); } else { unsigned char *img_ptr = image; unsigned char *img_end = (unsigned char*)image + image_size; for ( ; img_ptr < img_end; img_ptr += 8) { unsigned char tmp; /* Swap bytes [0,1,2,3,4,5,6,7] to [7,6,5,4,3,2,1,0] */ tmp = img_ptr[3]; img_ptr[3] = img_ptr[4]; img_ptr[4] = tmp; tmp = img_ptr[2]; img_ptr[2] = img_ptr[5]; img_ptr[5] = tmp; tmp = img_ptr[1]; img_ptr[1] = img_ptr[6]; img_ptr[6] = tmp; tmp = img_ptr[0]; img_ptr[0] = img_ptr[7]; img_ptr[7] = tmp; } } break; default: fprintf(stderr, "Warning: you specified -swap_bytes, but I can't swap this type of input\n"); break; } } /* Search for max and min for float and double */ if (do_minmax) { imgmax=(-DBL_MAX); imgmin=DBL_MAX; is_signed = (signtype == SIGNED); for (j=0; jimgmax) imgmax=value; } if (do_vrange) { if (imgminovalid_range[1]) ovalid_range[1]=imgmax; } } else { imgmin = pixel_min; imgmax = pixel_max; } /* Change the valid range for integer types if needed */ if (do_minmax && !floating_type) { (void) miicv_detach(icv); (void) miicv_setdbl(icv, MI_ICV_VALID_MIN, imgmin); (void) miicv_setdbl(icv, MI_ICV_VALID_MAX, imgmax); (void) miicv_attach(icv, cdfid, imgid); } /* Write the image max and min after re-scaling */ if (do_minmax || do_real_range) { imgmin = imgmin * scale + offset; imgmax = imgmax * scale + offset; if (!floating_type || (otype != FLOAT_TYPE && otype != DOUBLE_TYPE)) { (void) mivarput1(cdfid, minid, start, NC_DOUBLE, NULL, &imgmin); (void) mivarput1(cdfid, maxid, start, NC_DOUBLE, NULL, &imgmax); } } /* Write the image */ (void) miicv_put(icv, start, count, image); /* Increment the counters */ start[fastdim] += count[fastdim]; i=fastdim; while ((i>0) && (start[i]>=end[i])) { start[i] = 0; i--; start[i] += count[i]; } } /* Free the memory */ free(image); /* Write the valid max and min */ if (do_vrange) { (void) miset_valid_range(cdfid, imgid, ovalid_range); } /* Write Global scaling for float data*/ if (floating_type && (otype == FLOAT_TYPE || otype == DOUBLE_TYPE)) { (void) mivarput1(cdfid, minid, 0, NC_DOUBLE, NULL, &ovalid_range[0]); (void) mivarput1(cdfid, maxid, 0, NC_DOUBLE, NULL, &ovalid_range[1]); } /* Close the file */ (void) miattputstr(cdfid, imgid, MIcomplete, MI_TRUE); (void) miclose(cdfid); exit(NORMAL_STATUS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : parse_args @INPUT : argc - number of command line arguments argv - array of arguments @OUTPUT : @RETURNS : (nothing). @DESCRIPTION: Parses command line arguments. @METHOD : @GLOBALS : pname - program name filename - file to write clobber - overwrite? dimname - names of dimensions dimlength - lengths of dimensions ndims - number of dimensions type - type of input data signtype - sign of input data datatype - NetCDF type of input data sign - string sign of input valid_range - valid range of input data vrange_set - valid range used? otype - type of output data osigntype - sign of output data datatype - NetCDF type of output data osign - string sign of output ovalid_range - valid range of output data ovrange_set - valid range used? @CALLS : @CREATED : December 3, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ static void parse_args(int argc, char *argv[]) { char *ptr; int time_size; int i; /* Parse the command line */ pname=argv[0]; if (ParseArgv(&argc, argv, argTable, 0)) { usage_error(pname); } /* Get filename */ filename = argv[1]; /* Check dimensions */ ndims = argc - 2; /* LB. Modified to take -like with no dimension length provided */ if (ndims == 0) { if (!Specified_like) { (void) fprintf(stderr, "\nEither provide dimension lengths or use -like.\n"); usage_error(pname); } else { ndims = NDims; for (i=0; iMAX_DIMS)) { (void) fprintf(stderr, "\nWrong number of arguments.\n"); usage_error(pname); } /* Get dimensions */ for (i=0; i 0)) || ((num_frame_widths != time_size) && (num_frame_widths > 0))) { (void) fprintf(stderr, "Number of frame times or widths does not match number of frames.\n"); exit(EXIT_FAILURE); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : usage_error @INPUT : progname - program name @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Prints a usage error message and exits. @METHOD : @GLOBALS : @CALLS : @CREATED : September 25, 1992 (Peter Neelin) @MODIFIED : December 2, 1992 (P.N.) ---------------------------------------------------------------------------- */ static void usage_error(char *progname) { (void) fprintf(stderr, "\nUsage: %s [] ", progname); (void) fprintf(stderr, "[[] ] \n"); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(ERROR_STATUS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_attribute @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 attributes from command line. Syntax for argument is ":=". Numeric values are converted to double precision. @METHOD : @GLOBALS : @CALLS : @CREATED : May 3, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_attribute(char *dst, char *key, char *nextarg) /* ARGSUSED */ { int need_string, need_double; char *variable; char *attribute; char *value; char *end; double dvalue; /* Check for a following argument */ if (nextarg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Figure out whether we need a string or a double */ need_string = (strcmp(key, "-sattribute") == 0); need_double = (strcmp(key, "-dattribute") == 0); /* Get the variable name */ variable = nextarg; attribute = strchr(variable, ':'); if (attribute == NULL) { (void) fprintf(stderr, "%s option requires argument :=\n", key); exit(EXIT_FAILURE); } *attribute = '\0'; attribute++; /* Get the value */ value = strchr(attribute, '='); if (value == NULL) { (void) fprintf(stderr, "%s option requires argument :=\n", key); exit(EXIT_FAILURE); } *value = '\0'; value++; /* Save the information */ attribute_list_size++; if (attribute_list_size > attribute_list_alloc) { attribute_list_alloc += 10; if (attribute_list == NULL) { attribute_list = malloc(attribute_list_alloc * sizeof(*attribute_list)); } else { attribute_list = realloc(attribute_list, attribute_list_alloc * sizeof(*attribute_list)); } } attribute_list[attribute_list_size-1].variable = variable; attribute_list[attribute_list_size-1].attribute = attribute; attribute_list[attribute_list_size-1].value = value; /* Try to get a double precision value */ if (!need_string) { dvalue = strtod(value, &end); if ((end != value) && (*end == '\0')) { attribute_list[attribute_list_size-1].value = NULL; attribute_list[attribute_list_size-1].double_value = dvalue; } else if (need_double) { (void) fprintf(stderr, "\"%s\" option requires a numeric argument\n", key); exit(EXIT_FAILURE); } } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_times @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 frame start times or frame lengths from next argument. @METHOD : @GLOBALS : @CALLS : @CREATED : May 3, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_times(char *dst, char *key, char *nextarg) /* ARGSUSED */ { int *num_elements_ptr; double **time_list_ptr; int num_elements; int num_alloc; double *time_list; double dvalue; char *cur, *end, *prev; /* 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 global variables */ if (strcmp(key, "-frame_times") == 0) { num_elements_ptr = &num_frame_times; time_list_ptr = &frame_times; } else if (strcmp(key, "-frame_widths") == 0) { num_elements_ptr = &num_frame_widths; time_list_ptr = &frame_widths; } else { (void) fprintf(stderr, "Unknown option \"%s\".\n", key); exit(EXIT_FAILURE); } /* 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; time_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 (time_list == NULL) { time_list = malloc(num_alloc * sizeof(*time_list)); } else { time_list = realloc(time_list, num_alloc * sizeof(*time_list)); } } time_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 */ *num_elements_ptr = num_elements; if (*time_list_ptr != NULL) { free(*time_list_ptr); } *time_list_ptr = time_list; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_axis_order @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE or FALSE (so that ParseArgv will discard nextArg only when needed) @DESCRIPTION: Routine called by ParseArgv to set the axis order @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : Copied from mincreshape.c by bert, 29 Oct 2003 ---------------------------------------------------------------------------- */ /* "Standard" orientations - index the orientation_names table defined * below. */ #define TRANSVERSE_ORIENTATION 0 #define SAGITTAL_ORIENTATION 1 #define CORONAL_ORIENTATION 2 #define TIME_FAST_ORIENTATION 3 #define XYZ_ORIENTATION 4 #define YXZ_ORIENTATION 5 #define ZXY_ORIENTATION 6 #define STD_ORIENTATION_COUNT 7 static int get_axis_order(char *dst, char *key, char *nextArg) { char **dim_name_array; char *argp; int i, j; int orientation; /* Structure containing information about orientation */ static char *orientation_names[STD_ORIENTATION_COUNT][MAX_DIMS] = { {MItime, MIzspace, MIyspace, MIxspace}, /* TRANSVERSE_ORIENTATION */ {MItime, MIxspace, MIzspace, MIyspace}, /* SAGITTAL_ORIENTATION */ {MItime, MIyspace, MIzspace, MIxspace}, /* CORONAL_ORIENTATION */ {MIzspace, MItime, MIyspace, MIxspace}, /* TIME_FAST_ORIENTATION */ {MItime, MIxspace, MIyspace, MIzspace}, /* XYZ_ORIENTATION */ {MItime, MIyspace, MIxspace, MIzspace}, /* YXZ_ORIENTATION */ {MItime, MIzspace, MIxspace, MIyspace}, /* ZXY_ORIENTATION */ }; /* LB. To avoid confusion for dimension order with -like*/ if (Specified_like) { (void) fprintf(stderr, "\"%s\" can not use this option with -like\n", key); exit(EXIT_FAILURE); } /* Get pointer to client data */ dim_name_array = (char **) dst; if (!strcmp(key, "-dimorder")) { /* Check for next argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Set up pointers to end of string and first non-space character */ argp = nextArg; while (isspace(*argp)) argp++; /* Loop through string looking for space or comma-separated names */ for (i = 0; i < MAX_DIMS && *argp != '\0'; i++) { dim_name_array[i] = argp; /* Get string */ /* Search for end of dimension name */ while (!isspace(*argp) && *argp != ARG_SEPARATOR && *argp != '\0') argp++; if (*argp != '\0') { *argp = '\0'; argp++; } /* Skip any spaces */ while (isspace(*argp)) argp++; } /* BEGIN WEIRDNESS */ /* parse_args() assumes that the axis_order[] array contains * MAX_DIMS (4) entries, with optional values at the * _beginning_ of the array. To make the array we just * constructed compatible with that assumption, I need to copy * all of the just-specified dimensions to the end of this * array, and fill the earlier entries up with some sort of * dummy value. */ if (i < MAX_DIMS) { int deficit = MAX_DIMS - i; /* How many we're missing */ for (j = i; j >= 0; j--) { dim_name_array[j + deficit] = dim_name_array[j]; } for (j = 0; j < deficit; j++) { dim_name_array[j] = "unknown"; /* Just a dummy value. */ } } /* END WEIRDNESS */ /* Return TRUE to let the argv processor know that we used an * extra argument. */ return (TRUE); } else if (!strcmp(key, "-transverse") || !strcmp(key, "-zyx")) { orientation = TRANSVERSE_ORIENTATION; } else if (!strcmp(key, "-sagittal") || !strcmp(key, "-xzy")) { orientation = SAGITTAL_ORIENTATION; } else if (!strcmp(key, "-coronal") || !strcmp(key, "-yzx")) { orientation = CORONAL_ORIENTATION; } else if (!strcmp(key, "-time")) { orientation = TIME_FAST_ORIENTATION; } else if (!strcmp(key, "-xyz")) { orientation = XYZ_ORIENTATION; } else if (!strcmp(key, "-yxz")) { orientation = YXZ_ORIENTATION; } else if (!strcmp(key, "-zxy")) { orientation = ZXY_ORIENTATION; } else { (void) fprintf(stderr, "Unrecognized option \"%s\": internal program error.\n", key); exit(EXIT_FAILURE); } /* Copy from the appropriate row of the static array into the * dimension name array. */ for (i = 0; i < MAX_DIMS; i++) { dim_name_array[i] = orientation_names[orientation][i]; } /* Return FALSE to let the argv processor know we didn't use anything * extra from the argument list. */ return (FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_info @INPUT : filename - name of file to read initialized_volume_def - if TRUE, then volume_def is taken as being properly initialized and arrays are freed if non-NULL. Otherwise arrays are not freed. @OUTPUT : volume_def - description of volume file_info - description of file @RETURNS : (nothing) @DESCRIPTION: Routine to get information about the volume definition of a minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : February 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_file_info(char *filename, int initialized_volume_def, Volume_Definition *volume_def, File_Info *file_info) { int dim[MAX_VAR_DIMS], dimid; int axis_counter, idim, jdim, cur_axis; int varndims, vardim[MAX_VAR_DIMS]; long varstart, varcount, dimlength1; char attstr[MI_MAX_ATTSTR_LEN]; char dimname1[MAX_NC_NAME]; enum {UNKNOWN, REGULAR, IRREGULAR} coord_spacing; /* Open the minc file */ file_info->mincid = miopen(filename, NC_NOWRITE); file_info->name = filename; /* Get variable identifiers */ file_info->imgid = ncvarid(file_info->mincid, MIimage); ncopts = 0; file_info->maxid = ncvarid(file_info->mincid, MIimagemax); file_info->minid = ncvarid(file_info->mincid, MIimagemin); ncopts = NC_VERBOSE | NC_FATAL; /* Get information about datatype dimensions of variable */ (void) miget_datatype(file_info->mincid, file_info->imgid, &file_info->datatype, &file_info->is_signed); /* Get valid max and min */ (void) miget_valid_range(file_info->mincid, file_info->imgid, file_info->vrange); /* Get information about dimensions */ (void) ncvarinq(file_info->mincid, file_info->imgid, NULL, NULL, &file_info->ndims, dim, NULL); /* Set variables for keeping track of spatial dimensions */ axis_counter = 0; /* Keeps track of values for axes */ /* Initialize volume definition variables */ for (idim=0; idim < WORLD_NDIMS; idim++) { volume_def->axes[idim] = NO_AXIS; volume_def->step[idim] = 1.0; volume_def->start[idim] = 0.0; for (jdim=0; jdim < WORLD_NDIMS; jdim++) { if (jdim==idim) volume_def->dircos[idim][jdim] = 1.0; else volume_def->dircos[idim][jdim] = 0.0; } if (initialized_volume_def && (volume_def->coords[idim] != NULL)) { free(volume_def->coords[idim]); } volume_def->coords[idim] = NULL; (void) strcpy(volume_def->units[idim], "mm"); (void) strcpy(volume_def->spacetype[idim], MI_NATIVE); } /* Loop through dimensions, getting dimension information */ for (idim=0; idim < file_info->ndims; idim++) { /* Get size of dimension */ (void) ncdiminq(file_info->mincid, dim[idim], dimname1, &file_info->nelements[idim]); /* Check variable name */ /* LB. added minor modification to change axis_order for dimension order and name */ cur_axis = NO_AXIS; if (strcmp(dimname1, MIxspace)==0) { cur_axis = XAXIS; axis_order[idim+1]= MIxspace; } else if (strcmp(dimname1, MIyspace)==0) { cur_axis = YAXIS; axis_order[idim+1]= MIyspace; } else if (strcmp(dimname1, MIzspace)==0) { cur_axis = ZAXIS; axis_order[idim+1]= MIzspace; } /* Save world axis info */ file_info->world_axes[idim] = cur_axis; /* Check for spatial dimension */ if (cur_axis == NO_AXIS) continue; /* Set axis */ if (volume_def->axes[cur_axis] != NO_AXIS) { (void) fprintf(stderr, "Repeated spatial dimension %s in file %s.\n", dimname1, filename); exit(EXIT_FAILURE); } volume_def->axes[cur_axis] = axis_counter++; /* Save spatial axis specific info */ file_info->axes[cur_axis] = volume_def->axes[cur_axis]; file_info->indices[volume_def->axes[cur_axis]] = idim; volume_def->nelements[cur_axis] = file_info->nelements[idim]; /* Check for existence of variable */ ncopts = 0; dimid = ncvarid(file_info->mincid, dimname1); ncopts = NC_VERBOSE | NC_FATAL; if (dimid == MI_ERROR) continue; /* Get attributes from variable */ ncopts = 0; (void) miattget1(file_info->mincid, dimid, MIstep, NC_DOUBLE, &volume_def->step[cur_axis]); if (volume_def->step[cur_axis] == 0.0) volume_def->step[cur_axis] = 1.0; (void) miattget1(file_info->mincid, dimid, MIstart, NC_DOUBLE, &volume_def->start[cur_axis]); (void) miattget(file_info->mincid, dimid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, volume_def->dircos[cur_axis], NULL); (void) miattgetstr(file_info->mincid, dimid, MIunits, MI_MAX_ATTSTR_LEN, volume_def->units[cur_axis]); (void) miattgetstr(file_info->mincid, dimid, MIspacetype, MI_MAX_ATTSTR_LEN, volume_def->spacetype[cur_axis]); ncopts = NC_VERBOSE | NC_FATAL; /* Normalize the direction cosine */ // normalize_vector(volume_def->dircos[cur_axis]); /* Look for irregular coordinates for dimension variable */ ncopts = 0; coord_spacing = UNKNOWN; dimlength1 = volume_def->nelements[cur_axis]; if (miattgetstr(file_info->mincid, dimid, MIspacing, MI_MAX_ATTSTR_LEN, attstr) != NULL) { if (strcmp(attstr, MI_IRREGULAR) == 0) coord_spacing = IRREGULAR; else if (strcmp(attstr, MI_REGULAR) == 0) coord_spacing = REGULAR; } if (ncvarinq(file_info->mincid, dimid, NULL, NULL, &varndims, vardim, NULL) == MI_ERROR) { ncopts = NC_VERBOSE | NC_FATAL; continue; } if ((coord_spacing != REGULAR) && (varndims == 1) && (vardim[0] == dim[idim])) { coord_spacing = IRREGULAR; } if ((coord_spacing == UNKNOWN) || (dimlength1 <= 1)) { coord_spacing = REGULAR; } if (coord_spacing == IRREGULAR) { volume_def->coords[cur_axis] = malloc(sizeof(double) * dimlength1); varstart = 0; varcount = dimlength1; if (mivarget(file_info->mincid, dimid, &varstart, &varcount, NC_DOUBLE, MI_SIGNED, volume_def->coords[cur_axis]) == MI_ERROR) { ncopts = NC_VERBOSE | NC_FATAL; free(volume_def->coords[cur_axis]); volume_def->coords[cur_axis] = NULL; continue; } volume_def->start[cur_axis] = volume_def->coords[cur_axis][0]; if (dimlength1 > 1) { volume_def->step[cur_axis] = (volume_def->coords[cur_axis][dimlength1-1] - volume_def->coords[cur_axis][0]) / (dimlength1 - 1); if (volume_def->step[cur_axis] == 0.0) volume_def->step[cur_axis] = 1.0; } } ncopts = NC_VERBOSE | NC_FATAL; } /* End of loop over dimensions */ /* Check that we have the correct number of spatial dimensions */ if (axis_counter != WORLD_NDIMS) { (void) fprintf(stderr, "Incorrect number of spatial dimensions in file %s.\n", filename); exit(EXIT_FAILURE); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_model_file @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE so that ParseArgv will discard nextArg unless there is no following argument. @DESCRIPTION: Routine called by ParseArgv to read in a model file (-like) @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_model_file(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; File_Info file; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* set this flag so we know that -like is used */ Specified_like = TRUE; /* Get pointer to volume definition structure */ volume_def = (Volume_Definition *) dst; /* Get file information */ get_file_info(nextArg, TRUE, volume_def, &file); /* LB. Set number of dimensions */ NDims = file.ndims; /* LB. Set dimension lengths and other info */ dimlength[0] = volume_def->nelements[file.world_axes[0]]; dimlength[1] = volume_def->nelements[file.world_axes[1]]; dimlength[2] = volume_def->nelements[file.world_axes[2]]; /* start */ dimstart[0]= volume_def->start[0]; dimstart[1]= volume_def->start[1]; dimstart[2]= volume_def->start[2]; /* step */ dimstep[0]= volume_def->step[0]; dimstep[1]= volume_def->step[1]; dimstep[2]= volume_def->step[2]; /* modify dircos X */ dimdircos[0][0] = volume_def->dircos[0][0]; dimdircos[0][1] = volume_def->dircos[0][1]; dimdircos[0][2] = volume_def->dircos[0][2]; /* modify dircos Y */ dimdircos[1][0] = volume_def->dircos[1][0]; dimdircos[1][1] = volume_def->dircos[1][1]; dimdircos[1][2] = volume_def->dircos[1][2]; /* modify dircos Z */ dimdircos[2][0] = volume_def->dircos[2][0]; dimdircos[2][1] = volume_def->dircos[2][1]; dimdircos[2][2] = volume_def->dircos[2][2]; /* Close the file */ (void) miclose(file.mincid); return TRUE; } minc-tools-2.3.00+dfsg/progs/mincdump/0002755000175000000620000000000012574624760016572 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincdump/mincdump.man10000644000175000000620000002153412574624760021167 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" $Header: /private-cvsroot/minc/progs/mincdump/mincdump.man1,v 1.2 2004-05-20 21:52:08 bert Exp $ .TH MINCDUMP 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincdump \- Convert minc files to ASCII form (CDL) .SH SYNOPSIS .ft B .HP mincdump .nh \%[-c] \%[-h] \%[-v \fIvar1,...\fP] \%[-b \fIlang\fP] \%[-f \fIlang\fP] \%[-l \fIlen\fP] \%[-n \fIname\fP] \%[-p \fIf_digits[,d_digits]\fP] \%\fIfile\fP .hy .ft .SH DESCRIPTION \fBmincdump\fP is based upon the netCDF tool \fBncdump\fR, modified to work with both MINC 1 (netCDF) and MINC 2 (HDF5) format files. It is intended for use primarily with scripts such as \fBmincdiff\fR and \fBmincheader\fR. Since it was not created at the Montreal Neurological Insititute it does not follow the usual conventions for MINC programs. \fBmincdump\fP generates an ASCII representation of a specified minc file on standard output. The ASCII representation is in a form called CDL (``network Common Data form Language'') that can be viewed, edited, or serve as input to \fBncgen\fP. \fBncgen\fP is a companion program that can generate a binary minc file from a CDL file. Hence \fBncgen\fP and \fBmincdump\fP can be used as inverses to transform the data representation between binary and ASCII representations. See \fBncgen\fP for a description of CDL and netCDF representations. .LP \fBmincdump\fP defines a default format used for each type of netCDF data, but this can be changed if a `C_format' attribute is defined for a netCDF variable. In this case, \fBmincdump\fP will use the `C_format' attribute to format each value. For example, if floating-point data for the netCDF variable `Z' is known to be accurate to only three significant digits, it would be appropriate to use the variable attribute .RS .HP Z:C_format = "%.3g" .RE .LP \fBmincdump\fP may also be used as a simple browser for netCDF data files, to display the dimension names and sizes; variable names, types, and shapes; attribute names and values; and optionally, the values of data for all variables or selected variables in a netCDF file. .LP \fBmincdump\fP uses `_' to represent data values that are equal to the `_FillValue' attribute for a variable, intended to represent data that has not yet been written. If a variable has no `_FillValue' attribute, the default fill value for the variable type is used if the variable is not of byte type. .SH OPTIONS .IP "\fB-c\fP" Show the values of \fIcoordinate\fP variables (variables that are also dimensions) as well as the declarations of all dimensions, variables, and attribute values. Data values of non-coordinate variables are not included in the output. This is the most suitable option to use for a brief look at the structure and contents of a netCDF file. .IP "\fB-h\fP" Show only the \fIheader\fP information in the output, that is the declarations of dimensions, variables, and attributes but no data values for any variables. The output is identical to using the \fB-c\fP option except that the values of coordinate variables are not included. (At most one of \fB-c\fP or \fB-h\fP options may be present.) .IP "\fB-v\fP \fIvar1,...,varn\fP" The output will include data values for the specified variables, in addition to the declarations of all dimensions, variables, and attributes. One or more variables must be specified by name in the comma-delimited list following this option. The list must be a single argument to the command, hence cannot contain blanks or other white space characters. The named variables must be valid netCDF variables in the input-file. The default, without this option and in the absence of the \fB-c\fP or \fB-h\fP options, is to include data values for \fIall\fP variables in the output. .IP "\fB-b\fP \fIlang\fP" A brief annotation in the form of a CDL comment (text beginning with the characters ``//'') will be included in the data section of the output for each `row' of data, to help identify data values for multidimensional variables. If \fIlang\fP begins with `C' or `c', then C language conventions will be used (zero-based indices, last dimension varying fastest). If \fIlang\fP begins with `F' or `f', then Fortran language conventions will be used (one-based indices, first dimension varying fastest). In either case, the data will be presented in the same order; only the annotations will differ. This option is useful for browsing through large volumes of multidimensional data. .IP "\fB-f\fP \fIlang\fP" Full annotations in the form of trailing CDL comments (text beginning with the characters ``//'') for every data value (except individual characters in character arrays) will be included in the data section. If \fIlang\fP begins with `C' or `c', then C language conventions will be used (zero-based indices, last dimension varying fastest). If \fIlang\fP begins with `F' or `f', then Fortran language conventions will be used (one-based indices, first dimension varying fastest). In either case, the data will be presented in the same order; only the annotations will differ. This option may be useful for piping data into other filters, since each data value appears on a separate line, fully identified. .IP "\fB-l\fP \fIlen\fP" Changes the default maximum line length (80) used in formatting lists of non-character data values. .IP "\fB-n\fP \fIname\fP" CDL requires a name for a netCDF data set, for use by \fBncgen -b\fP in generating a default netCDF file name. By default, \fImincdump\fP constructs this name from the last component of the pathname of the input netCDF file by stripping off any extension it has. Use the \fB-n\fP option to specify a different name. Although the output file name used by \fBncgen -b\fP can be specified, it may be wise to have \fImincdump\fP change the default name to avoid inadvertantly overwriting a valuable netCDF file when using \fBmincdump\fP, editing the resulting CDL file, and using \fBncgen -b\fP to generate a new netCDF file from the edited CDL file. .IP "\fB-p\fP \fIfloat_digits[,double_digits]\fP" Specifies default precision (number of significant digits) to use in displaying floating-point or double precision data values for attributes and variables. If specified, this value overrides the value of the `C_format' attribute for any variable that has such an attribute. Floating-point data will be displayed with \fIfloat_digits\fP significant digits. If \fIdouble_digits\fP is also specified, double-precision values will be displayed with that many significant digits. In the absence of any \fB-p\fP specifications, floating-point and double-precision data are displayed with 7 and 15 significant digits respectively. CDL files can be made smaller if less precision is required. If both floating-point and double-presision precisions are specified, the two values must appear separated by a comma (no blanks) as a single argument to the command. If you really want every last bit of precision from the netCDF file represented in the CDL file for all possible floating-point values, you will have to specify this with \fB-p 9,17\fP (according to Theorem 15 of the paper listed under REFERENCES). .SH EXAMPLES .LP Look at the structure of the data in the netCDF file `\fBfoo.mnc\fP': .RS .HP mincdump -c foo.mnc .RE .LP Produce an annotated CDL version of the structure and data in the netCDF file `\fBfoo.mnc\fP', using C-style indexing for the annotations: .RS .HP mincdump -b c foo.mnc > foo.cdl .RE .LP Output data for only the variables `uwind' and `vwind' from the netCDF file `\fBfoo.mnc\fP', and show the floating-point data with only three significant digits of precision: .RS .HP mincdump -v uwind,vwind -p 3 foo.mnc .RE .LP Produce a fully-annotated (one data value per line) listing of the data for the variable `omega', using Fortran conventions for indices, and changing the netCDF dataset name in the resulting CDL file to `omega': .RS .HP mincdump -v omega -f fortran -n omega foo.mnc > Z.cdl .RE .SH AUTHOR Originally written by members of the Unidata Program at the University Corporation for Atmospheric Research. Modified by Bert Vincent (bert@bic.mni.mcgill.ca) for use with both netCDF and HDF5 files. .SH COPYRIGHTS Copyright \(co University Corporation for Atmospheric Research .SH REFERENCES \fIWhat Every Computer Scientist should Know About Floating-Point Arithmetic\fP, D. Goldberg, \fBACM Computing Surveys, Vol. 23, No. 1\fP, March 1991, pp. 5-48. .SH "SEE ALSO" .LP .BR ncdump (1), .BR ncgen (1), .BR netcdf (3) .SH BUGS .LP Character arrays that contain a null-byte are treated like C strings, so no characters after the null byte appear in the output. Multidimensional character string arrays are not handled well, since the CDL syntax for breaking a long character string into several shorter lines is weak. There should be a way to specify that the data should be displayed in `record' order, that is with the all the values for `record' variables together that have the same value of the record dimension. minc-tools-2.3.00+dfsg/progs/mincdump/COPYRIGHT0000644000175000000620000000346712574624760020075 0ustar stevestaffCopyright 1993-2001 University Corporation for Atmospheric Research/Unidata Portions of this software were developed by the Unidata Program at the University Corporation for Atmospheric Research. Access and use of this software shall impose the following obligations and understandings on the user. The user is granted the right, without any fee or cost, to use, copy, modify, alter, enhance and distribute this software, and any derivative works thereof, and its supporting documentation for any purpose whatsoever, provided that this entire notice appears in all copies of the software, derivative works and supporting documentation. Further, UCAR requests that the user credit UCAR/Unidata in any publications that result from the use of this software or in any product that includes this software. The names UCAR and/or Unidata, however, may not be used in any advertising or publicity to endorse or promote any products or commercial entity unless specific written permission is obtained from UCAR/Unidata. The user also understands that UCAR/Unidata is not obligated to provide the user with any support, consulting, training or assistance of any kind with regard to the use, operation and performance of this software nor to provide the user with any updates, revisions, new versions or "bug fixes." THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. minc-tools-2.3.00+dfsg/progs/mincdump/dumplib.h0000644000175000000620000000410112574624760020371 0ustar stevestaff/********************************************************************* * Copyright 1993, University Corporation for Atmospheric Research * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincdump/dumplib.h,v 1.1 2004-04-27 15:35:15 bert Exp $ *********************************************************************/ extern char *progname; /* for error messages */ #define NO_NETCDF_2 /* assert we aren't using any netcdf-2 stuff */ #ifndef EXIT_FAILURE #ifndef vms #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 #else #define EXIT_SUCCESS 1 #define EXIT_FAILURE 0 #endif #endif #define FLT_DIGITS 7 /* default sig. digits for float data */ #define DBL_DIGITS 15 /* default sig. digits for double data */ extern int float_precision_specified; /* -p option specified float precision */ extern int double_precision_specified; /* -p option specified double precision */ extern char float_var_fmt[]; extern char double_var_fmt[]; extern char float_att_fmt[]; extern char double_att_fmt[]; #ifdef __cplusplus extern "C" { #endif /* Print error message to stderr and exit */ extern void error ( const char *fmt, ... ); /* set position in line before lput() calls */ extern void set_indent ( int indent ); /* set maximum line length */ extern void set_max_len ( int len ); /* splits lines to keep them short */ extern void lput ( const char *string ); /* In case different formats specified with -d option, set them here. */ extern void set_formats ( int flt_digs, int dbl_digs ); /* Determine print format to use for each value for this variable. */ char * get_fmt ( int ncid, int varid, nc_type type ); /* structure for list of variables specified with -v option */ struct vnode { struct vnode* next; int id; }; typedef struct vnode vnode; /* Get new variable list */ extern vnode* newvlist ( void ); /* Add a variable id to variable list */ extern void varadd ( vnode* vlist, int varid ); /* Test if a variable id is in variable list */ extern int varmember ( const vnode* vlist, int varid ); #ifdef __cplusplus } #endif minc-tools-2.3.00+dfsg/progs/mincdump/vardata.c0000644000175000000620000005462312574624760020370 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincdump/vardata.c,v 1.5 2009-01-20 11:58:13 rotor Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifndef NO_FLOAT_H #include /* for FLT_EPSILON, DBL_EPSILON */ #endif /* NO_FLOAT_H */ #include #include "mincdump.h" #include "dumplib.h" #include "vardata.h" static float float_epsilon(void); static double double_epsilon(void); static void init_epsilons(void); static void printbval(char* sout, const char* fmt, const struct ncvar* varp, signed char val); static void printsval(char* sout, const char* fmt, const struct ncvar* varp, short val); static void printival(char* sout, const char* fmt, const struct ncvar* varp, int val); static void printfval(char* sout, const char* fmt, const struct ncvar* varp, float val); static void printdval(char* sout, const char* fmt, const struct ncvar* varp, double val); static void lastdelim(boolean more, boolean lastrow); static void annotate(const struct ncvar* vp, const struct fspec* fsp, const long* cor, long iel); static void pr_tvals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const char *vals, const struct fspec* fsp, const long *cor); static void pr_bvals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const signed char *vals, const struct fspec* fsp, const long *cor); static void pr_svals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const short *vals, const struct fspec* fsp, const long *cor); static void pr_ivals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const int *vals, const struct fspec* fsp, const long *cor); static void pr_fvals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const float *vals, const struct fspec* fsp, const long *cor); static void pr_dvals(const struct ncvar *vp, long len, const char *fmt, boolean more, boolean lastrow, const double *vals, const struct fspec* fsp, const long *cor); static int upcorner(const long* dims, int ndims, long* odom, const long* add); static void lastdelim2 (boolean more, boolean lastrow); #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) static float float_eps; static double double_eps; static float float_epsilon(void) { float float_eps; #ifndef NO_FLOAT_H float_eps = FLT_EPSILON; #else /* NO_FLOAT_H */ { float etop, ebot, eps; float one = 1.0; float two = 2.0; etop = 1.0; ebot = 0.0; eps = ebot + (etop - ebot)/two; while (eps != ebot && eps != etop) { float epsp1; epsp1 = one + eps; if (epsp1 > one) etop = eps; else ebot = eps; eps = ebot + (etop - ebot)/two; } float_eps = two * etop; } #endif /* NO_FLOAT_H */ return float_eps; } static double double_epsilon(void) { double double_eps; #ifndef NO_FLOAT_H double_eps = DBL_EPSILON; #else /* NO_FLOAT_H */ { double etop, ebot, eps; double one = 1.0; double two = 2.0; etop = 1.0; ebot = 0.0; eps = ebot + (etop - ebot)/two; while (eps != ebot && eps != etop) { double epsp1; epsp1 = one + eps; if (epsp1 > one) etop = eps; else ebot = eps; eps = ebot + (etop - ebot)/two; } double_eps = two * etop; } #endif /* NO_FLOAT_H */ return double_eps; } static void init_epsilons(void) { float_eps = float_epsilon(); double_eps = double_epsilon(); } /* * Output a value of a byte variable, except if there is a fill value for * the variable and the value is the fill value, print the fill-value string * instead. */ static void printbval( char *sout, /* string where output goes */ const char *fmt, /* printf format used for value */ const struct ncvar *varp, /* variable */ signed char val /* value */ ) { if (varp->has_fillval) { double fillval = varp->fillval; if(fillval == val) { (void) sprintf(sout, FILL_STRING); return; } } (void) sprintf(sout, fmt, val); } /* * Output a value of a short variable, except if there is a fill value for * the variable and the value is the fill value, print the fill-value string * instead. */ static void printsval( char *sout, /* string where output goes */ const char *fmt, /* printf format used for value */ const struct ncvar *varp, /* variable */ short val /* value */ ) { if (varp->has_fillval) { double fillval = varp->fillval; if(fillval == val) { (void) sprintf(sout, FILL_STRING); return; } } (void) sprintf(sout, fmt, val); } /* * Output a value of an int variable, except if there is a fill value for * the variable and the value is the fill value, print the fill-value string * instead. */ static void printival( char *sout, /* string where output goes */ const char *fmt, /* printf format used for value */ const struct ncvar *varp, /* variable */ int val /* value */ ) { if (varp->has_fillval) { int fillval = (int)varp->fillval; if(fillval == val) { (void) sprintf(sout, FILL_STRING); return; } } (void) sprintf(sout, fmt, val); } #define absval(x) ( (x) < 0 ? -(x) : (x) ) /* * Output a value of a float variable, except if there is a fill value for * the variable and the value is the fill value, print the fill-value string * instead. Floating-point fill values need only be within machine epsilon of * defined fill value. */ static void printfval( char *sout, /* string where output goes */ const char *fmt, /* printf format used for value */ const struct ncvar *varp, /* variable */ float val /* value */ ) { if(varp->has_fillval) { double fillval = varp->fillval; if((val > 0) == (fillval > 0) && /* prevents potential overflow */ (absval(val - fillval) <= absval(float_eps * fillval))) { (void) sprintf(sout, FILL_STRING); return; } } (void) sprintf(sout, fmt, val); } /* * Output a value of a double variable, except if there is a fill value for * the variable and the value is the fill value, print the fill-value string * instead. Floating-point fill values need only be within machine epsilon of * defined fill value. */ static void printdval( char *sout, /* string where output goes */ const char *fmt, /* printf format used for value */ const struct ncvar *varp, /* variable */ double val /* value */ ) { if(varp->has_fillval) { double fillval = varp->fillval; if((val > 0) == (fillval > 0) && /* prevents potential overflow */ (absval(val - fillval) <= absval(double_eps * fillval))) { (void) sprintf(sout, FILL_STRING); return; } } (void) sprintf(sout, fmt, val); } /* * print last delimiter in each line before annotation (, or ;) */ static void lastdelim (boolean more, boolean lastrow) { if (more) { Printf(", "); } else { if(lastrow) { Printf(";"); } else { Printf(","); } } } /* * print last delimiter in each line before annotation (, or ;) */ static void lastdelim2 (boolean more, boolean lastrow) { if (more) { lput(", "); } else { if(lastrow) { lput(" ;"); lput("\n"); } else { lput(",\n"); lput(" "); } } } /* * Annotates a value in data section with var name and indices in comment */ static void annotate( const struct ncvar *vp, /* variable */ const struct fspec* fsp, /* formatting specs */ const long *cor, /* corner coordinates */ long iel /* which element in current row */ ) { int vrank = vp->ndims; int id; /* print indices according to data_lang */ (void) printf(" // %s(", vp->name); switch (fsp->data_lang) { case LANG_C: /* C variable indices */ for (id = 0; id < vrank-1; id++) Printf("%lu,", (unsigned long) cor[id]); Printf("%lu", (unsigned long) cor[id] + iel); break; case LANG_F: /* Fortran variable indices */ Printf("%lu", (unsigned long) cor[vrank-1] + iel + 1); for (id = vrank-2; id >=0 ; id--) { Printf(",%lu", 1 + (unsigned long) cor[id]); } break; } Printf(")\n "); } /* * Print a number of char variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_tvals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const char *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; const char *sp; unsigned char uc; char sout[100]; /* temporary string for each encoded output */ if (fmt == 0 || STREQ(fmt,"%s") || STREQ(fmt,"")) { /* as string */ Printf("\""); /* adjust len so trailing nulls don't get printed */ sp = vals + len; while (len != 0 && *--sp == '\0') len--; for (iel = 0; iel < len; iel++) switch (uc = *vals++ & 0377) { case '\b': Printf("\\b"); break; case '\f': Printf("\\f"); break; case '\n': /* generate linebreaks after new-lines */ Printf("\\n\",\n \""); break; case '\r': Printf("\\r"); break; case '\t': Printf("\\t"); break; case '\v': Printf("\\v"); break; case '\\': Printf("\\\\"); break; case '\'': Printf("\\\'"); break; case '\"': Printf("\\\""); break; default: if (isprint(uc)) Printf("%c",uc); else Printf("\\%.3o",uc); break; } Printf("\""); if (fsp->full_data_cmnts) { lastdelim (more, lastrow); annotate (vp, fsp, cor, 0L); } } else { /* use format from C_format attribute */ for (iel = 0; iel < len-1; iel++) { if (fsp->full_data_cmnts) { Printf(fmt, *vals++); Printf(", "); annotate (vp, fsp, cor, iel); } else { (void) sprintf(sout, fmt, *vals++); (void) strcat(sout, ", "); lput(sout); } } if (fsp->full_data_cmnts) { Printf(fmt, *vals++); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { (void) sprintf(sout, fmt, *vals++); lput(sout); } } if (!fsp->full_data_cmnts) { lastdelim2 (more, lastrow); } } /* * Print a number of byte variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_bvals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const signed char *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; char sout[100]; /* temporary string for each encoded output */ for (iel = 0; iel < len-1; iel++) { printbval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); Printf(","); annotate (vp, fsp, cor, iel); } else { (void) strcat(sout, ", "); lput(sout); } } printbval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { lput(sout); lastdelim2 (more, lastrow); } } /* * Print a number of short variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_svals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const short *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; char sout[100]; /* temporary string for each encoded output */ for (iel = 0; iel < len-1; iel++) { printsval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); Printf(","); annotate (vp, fsp, cor, iel); } else { (void) strcat(sout, ", "); lput(sout); } } printsval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { lput(sout); lastdelim2 (more, lastrow); } } /* * Print a number of int variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_ivals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const int *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; char sout[100]; /* temporary string for each encoded output */ for (iel = 0; iel < len-1; iel++) { printival(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); Printf(","); annotate (vp, fsp, cor, iel); } else { (void) strcat(sout, ", "); lput(sout); } } printival(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { lput(sout); lastdelim2 (more, lastrow); } } /* * Print a number of float variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_fvals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const float *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; char sout[100]; /* temporary string for each encoded output */ for (iel = 0; iel < len-1; iel++) { printfval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); Printf(","); annotate (vp, fsp, cor, iel); } else { (void) strcat(sout, ", "); lput(sout); } } printfval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { lput(sout); lastdelim2 (more, lastrow); } } /* * Print a number of double variable values, where the optional comments * for each value identify the variable, and each dimension index. */ static void pr_dvals( const struct ncvar *vp, /* variable */ long len, /* number of values to print */ const char *fmt, /* printf format used for each value. If * nc_type is NC_CHAR and this is NULL, * character arrays will be printed as * strings enclosed in quotes. */ boolean more, /* true if more data for this row will * follow, so add trailing comma */ boolean lastrow, /* true if this is the last row for this * variable, so terminate with ";" instead * of "," */ const double *vals, /* pointer to block of values */ const struct fspec* fsp, /* formatting specs */ const long *cor /* corner coordinates */ ) { long iel; char sout[100]; /* temporary string for each encoded output */ for (iel = 0; iel < len-1; iel++) { printdval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); Printf(","); annotate (vp, fsp, cor, iel); } else { (void) strcat(sout, ", "); lput(sout); } } printdval(sout, fmt, vp, *vals++); if (fsp->full_data_cmnts) { Printf("%s", sout); lastdelim (more, lastrow); annotate (vp, fsp, cor, iel); } else { lput(sout); lastdelim2 (more, lastrow); } } /* * Updates a vector of ints, odometer style. Returns 0 if odometer * overflowed, else 1. */ static int upcorner( const long *dims, /* The "odometer" limits for each dimension */ int ndims, /* Number of dimensions */ long * odom, /* The "odometer" vector to be updated */ const long * add /* A vector to "add" to odom on each update */ ) { int id; int ret = 1; for (id = ndims-1; id > 0; id--) { odom[id] += add[id]; if(odom[id] >= dims[id]) { odom[id-1]++; odom[id] -= dims[id]; } } odom[0] += add[0]; if (odom[0] >= dims[0]) ret = 0; return ret; } /* Output the data for a single variable, in CDL syntax. */ int vardata( const struct ncvar *vp, /* variable */ long vdims[], /* variable dimension sizes */ int ncid, /* netcdf id */ int varid, /* variable id */ const struct fspec* fsp /* formatting specs */ ) { long cor[NC_MAX_DIMS]; /* corner coordinates */ long edg[NC_MAX_DIMS]; /* edges of hypercube */ long add[NC_MAX_DIMS]; /* "odometer" increment to next "row" */ #define VALBUFSIZ 1000 double vals[VALBUFSIZ] ; /* aligned buffer */ int gulp = VALBUFSIZ; int id; int ir; long nels; long ncols; long nrows; int vrank = vp->ndims; static int initeps = 0; /* printf format used to print each value */ char *fmt = get_fmt(ncid, varid, vp->type); if (!initeps) { /* make sure epsilons get initialized */ init_epsilons(); initeps = 1; } nels = 1; for (id = 0; id < vrank; id++) { cor[id] = 0; edg[id] = 1; nels *= vdims[id]; /* total number of values for variable */ } if (vrank <= 1) { Printf("\n %s = ", vp->name); set_indent ((int)strlen(vp->name) + 4); } else { Printf("\n %s =\n ", vp->name); set_indent (2); } if (vrank < 1) { ncols = 1; } else { ncols = vdims[vrank-1]; /* size of "row" along last dimension */ edg[vrank-1] = vdims[vrank-1]; for (id = 0; id < vrank; id++) add[id] = 0; if (vrank > 1) add[vrank-2] = 1; } nrows = nels/ncols; /* number of "rows" */ for (ir = 0; ir < nrows; ir++) { /* * rather than just printing a whole row at once (which might exceed * the capacity of MSDOS platforms, for example), we break each row * into smaller chunks, if necessary. */ long corsav; int left = (int)ncols; boolean lastrow; if (vrank > 0) { corsav = cor[vrank-1]; if (fsp->brief_data_cmnts != false && vrank > 1 && left > 0) { /* print brief comment with indices range */ Printf("// %s(",vp->name); switch (fsp->data_lang) { case LANG_C: /* print brief comment with C variable indices */ for (id = 0; id < vrank-1; id++) Printf("%lu,", (unsigned long)cor[id]); if (vdims[vrank-1] == 1) Printf("0"); else Printf(" 0-%lu", (unsigned long)vdims[vrank-1]-1); break; case LANG_F: /* print brief comment with Fortran variable indices */ if (vdims[vrank-1] == 1) Printf("1"); else Printf("1-%lu ", (unsigned long)vdims[vrank-1]); for (id = vrank-2; id >=0 ; id--) { Printf(",%lu", (unsigned long)(1 + cor[id])); } break; } Printf(")\n "); set_indent(4); } } lastrow = (boolean)(ir == nrows-1); while (left > 0) { long toget = left < gulp ? left : gulp; if (vrank > 0) edg[vrank-1] = toget; switch(vp->type) { case NC_CHAR: NC_CHECK( ncvarget(ncid, varid, cor, edg, (char *)vals) ); pr_tvals(vp, toget, fmt, left > toget, lastrow, (char *) vals, fsp, cor); break; case NC_BYTE: NC_CHECK( ncvarget(ncid, varid, cor, edg, (signed char *)vals) ); pr_bvals(vp, toget, fmt, left > toget, lastrow, (signed char *) vals, fsp, cor); break; case NC_SHORT: NC_CHECK( ncvarget(ncid, varid, cor, edg, (short *)vals) ); pr_svals(vp, toget, fmt, left > toget, lastrow, (short *) vals, fsp, cor); break; case NC_INT: NC_CHECK( ncvarget(ncid, varid, cor, edg, (int *)vals) ); pr_ivals(vp, toget, fmt, left > toget, lastrow, (int *) vals, fsp, cor); break; case NC_FLOAT: NC_CHECK( ncvarget(ncid, varid, cor, edg, (float *)vals) ); pr_fvals(vp, toget, fmt, left > toget, lastrow, (float *) vals, fsp, cor); break; case NC_DOUBLE: NC_CHECK( ncvarget(ncid, varid, cor, edg, (double *)vals) ); pr_dvals(vp, toget, fmt, left > toget, lastrow, (double *) vals, fsp, cor); break; default: error("vardata: bad type"); } left -= toget; if (vrank > 0) cor[vrank-1] += toget; } if (vrank > 0) cor[vrank-1] = corsav; if (ir < nrows-1) if (!upcorner(vdims,vp->ndims,cor,add)) error("vardata: odometer overflowed!"); set_indent(2); } return 0; } minc-tools-2.3.00+dfsg/progs/mincdump/dumplib.c0000644000175000000620000001151512574624760020373 0ustar stevestaff/********************************************************************* * Copyright 1993, University Corporation for Atmospheric Research * See netcdf/README file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincdump/dumplib.c,v 1.4 2008-01-12 19:08:15 stever Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif /* * We potentially include before in order to obtain a * definition for va_list from the GNU C compiler. */ #include #include #include #include #include #include "dumplib.h" static char* has_c_format_att(int ncid, int varid); static vnode* newvnode(void); int float_precision_specified = 0; /* -p option specified float precision */ int double_precision_specified = 0; /* -p option specified double precision */ char float_var_fmt[] = "%.NNg"; char double_var_fmt[] = "%.NNg"; char float_att_fmt[] = "%#.NNgf"; char double_att_fmt[] = "%#.NNg"; /* * Print error message to stderr and exit */ void error(const char *fmt, ...) { va_list args ; (void) fprintf(stderr,"%s: ", progname); va_start(args, fmt) ; (void) vfprintf(stderr,fmt,args) ; va_end(args) ; (void) fprintf(stderr, "\n") ; (void) fflush(stderr); /* to ensure log files are current */ exit(EXIT_FAILURE); } #define LINEPIND " " /* indent of continued lines */ static int linep; static int max_line_len; void set_indent(int in) { linep = in; } void set_max_len(int len) { max_line_len = len-2; } void lput(const char *cp) { size_t nn = strlen(cp); if (nn+linep > (size_t)max_line_len && nn > 2) { (void) fputs("\n", stdout); (void) fputs(LINEPIND, stdout); linep = (int)strlen(LINEPIND); } (void) fputs(cp,stdout); linep += nn; } /* In case different formats specified with -d option, set them here. */ void set_formats(int float_digits, int double_digits) { (void) sprintf(float_var_fmt, "%%.%dg", float_digits); (void) sprintf(double_var_fmt, "%%.%dg", double_digits); (void) sprintf(float_att_fmt, "%%#.%dgf", float_digits); (void) sprintf(double_att_fmt, "%%#.%dg", double_digits); } static char * has_c_format_att( int ncid, /* netcdf id */ int varid /* variable id */ ) { nc_type cfmt_type; int cfmt_len; #define C_FMT_NAME "C_format" /* name of C format attribute */ #define MAX_CFMT_LEN 100 /* max length of C format attribute */ static char cfmt[MAX_CFMT_LEN]; /* we expect nc_inq_att to fail if there is no "C_format" attribute */ int old_nc_opts; int nc_stat; old_nc_opts = ncopts; ncopts = 0; nc_stat = ncattinq(ncid, varid, "C_format", &cfmt_type, &cfmt_len); ncopts = old_nc_opts; if (nc_stat == -1) { return 0; } if (cfmt_type == NC_CHAR && cfmt_len != 0 && cfmt_len < MAX_CFMT_LEN) { nc_stat = ncattget(ncid, varid, "C_format", cfmt); if(nc_stat != 1) nc_advise("Getting 'C_format' attribute", nc_stat, ""); return &cfmt[0]; } return 0; } /* * Determine print format to use for each value for this variable. Use value * of attribute C_format if it exists, otherwise a sensible default. */ char * get_fmt( int ncid, /* netcdf id */ int varid, /* variable id */ nc_type type /* netCDF data type */ ) { char *c_format_att; /* float or double precision specified with -p option overrides any C_format attribute value, so check for that first. */ if (float_precision_specified && type == NC_FLOAT) return float_var_fmt; if (double_precision_specified && type == NC_DOUBLE) return double_var_fmt; /* If C_format attribute exists, return it */ c_format_att = has_c_format_att(ncid, varid); if (c_format_att) return c_format_att; /* Otherwise return sensible default. */ switch (type) { case NC_BYTE: return "%d"; case NC_CHAR: return "%s"; case NC_SHORT: return "%d"; case NC_INT: return "%d"; case NC_FLOAT: return float_var_fmt; case NC_DOUBLE: return double_var_fmt; default: error("pr_vals: bad type"); } return 0; } static vnode* newvnode(void) { vnode *newvp = (vnode*) malloc(sizeof(vnode)); if (!newvp) { error("out of memory!"); } return newvp; } /* * Get a new, empty variable list. */ vnode* newvlist(void) { vnode *vp = newvnode(); vp -> next = 0; vp -> id = -1; /* bad id */ return vp; } void varadd(vnode* vlist, int varid) { vnode *newvp = newvnode(); newvp -> next = vlist -> next; newvp -> id = varid; vlist -> next = newvp; } int varmember(const vnode* vlist, int varid) { vnode *vp = vlist -> next; for (; vp ; vp = vp->next) if (vp->id == varid) return 1; return 0; } minc-tools-2.3.00+dfsg/progs/mincdump/mincdump.h0000644000175000000620000000435412574624760020563 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincdump/mincdump.h,v 1.1 2004-04-27 15:35:15 bert Exp $ *********************************************************************/ /* error checking macro */ #define NC_CHECK(status) {\ int nc_status = status;\ if(nc_status < 0)\ error(nc_strerror(nc_status));\ } #define Printf (void) printf typedef int boolean; enum {false=0, true=1}; struct ncdim { /* dimension */ char name[NC_MAX_NAME]; long size; }; struct ncvar { /* variable */ char name[NC_MAX_NAME]; nc_type type; int ndims; int dims[MAX_VAR_DIMS]; int natts; boolean has_fillval; double fillval; }; struct ncatt { /* attribute */ int var; char name[NC_MAX_NAME]; nc_type type; int len; char *string; /* for text attributes (type = NC_CHAR) */ double *vals; /* for numeric attributes of all types */ }; typedef enum {LANG_C, LANG_F} Nclang; struct fspec { /* specification for how to format dump */ char *name; /* name specified with -n or derived from * file name */ boolean header_only; /* if true, don't print any variable data */ boolean coord_vals; /* if true, print header and coordinate * dimension values (values of variables * that are also dimensions), but no other * variable data */ boolean brief_data_cmnts; /* if true, put // comments in data section * identifying variable and indices, useful * for navigating through large * multi-dimensional data lists. */ boolean full_data_cmnts; /* if true, put // comments in data section * identifying every value, useful for * navigating through large * multi-dimensional data lists. */ Nclang data_lang; /* Specifies index conventions used in data * comments, either LANG_C (C, 0-based, * column major) or LANG_F (Fortran, * 1-based, row major) */ int nlvars; /* Number of variables specified with -v * option on command line */ char** lvars; /* list of variable names specified with -v * option on command line */ }; minc-tools-2.3.00+dfsg/progs/mincdump/vardata.h0000644000175000000620000000166612574624760020374 0ustar stevestaff/********************************************************************* * Copyright 1993, University Corporation for Atmospheric Research * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincdump/vardata.h,v 1.1 2004-04-27 15:35:15 bert Exp $ *********************************************************************/ extern char *progname; /* for error messages */ /* Display for user-defined fill values and default floating-point fill values; should match what ncgen looks for in ../ncgen/ncgen.l */ #define FILL_STRING "_" #ifdef __cplusplus extern "C" { #endif /* Output the data for a single variable, in CDL syntax. */ extern int vardata ( const struct ncvar*, /* variable */ long [], /* variable dimension lengths */ int, /* netcdf id */ int, /* variable id */ const struct fspec* /* formatting specs */ ); #ifdef __cplusplus } #endif minc-tools-2.3.00+dfsg/progs/mincdump/mincdump.c0000644000175000000620000004440712574624760020561 0ustar stevestaff/********************************************************************* * Copyright 1993, University Corporation for Atmospheric Research * See netcdf/README file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincdump/mincdump.c,v 1.10 2008-01-11 07:17:08 stever Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "mincdump.h" #include "dumplib.h" #include "vardata.h" static void usage(void); static char* name_path(const char* path); static char* type_name(nc_type type); static void tztrim(char* ss); static void pr_att_string(long len, const char* string); static void pr_att_vals(nc_type type, long len, const void * vals); static void pr_att(int ncid, int varid, const char *varname, int ia); static void do_ncdump(char* path, struct fspec* specp); int main(int argc, char** argv); #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) char *progname; static void usage(void) { (void) fprintf(stderr, "netcdf library version %s\n", nc_inq_libvers()); exit(EXIT_FAILURE); } /* * convert pathname of netcdf file into name for cdl unit, by taking * last component of path and stripping off any extension. */ static char * name_path(const char *path) { const char *cp; char *new; char *sp; #ifdef vms #define FILE_DELIMITER ']' #endif #ifdef MSDOS #define FILE_DELIMITER '\\' #endif #ifndef FILE_DELIMITER /* default to unix */ #define FILE_DELIMITER '/' #endif cp = strrchr(path, FILE_DELIMITER); if (cp == 0) /* no delimiter */ cp = path; else /* skip delimeter */ cp++; new = (char *) malloc((unsigned) (strlen(cp)+1)); if (new == 0) { error("out of memory!"); } (void) strcpy(new, cp); /* copy last component of path */ if ((sp = strrchr(new, '.')) != NULL) *sp = '\0'; /* strip off any extension */ return new; } static char * type_name(nc_type type) { switch (type) { 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"; default: error("type_name: bad type %d", type); return "bogus"; } } /* * Remove trailing zeros (after decimal point) but not trailing decimal * point from ss, a string representation of a floating-point number that * might include an exponent part. */ static void tztrim(char *ss) { char *cp, *ep; cp = ss; if (*cp == '-') cp++; while(isdigit((int)*cp) || *cp == '.') cp++; if (*--cp == '.') return; ep = cp+1; while (*cp == '0') cp--; cp++; if (cp == ep) return; while (*ep) *cp++ = *ep++; *cp = '\0'; return; } /* * Print attribute string, for text attributes. */ static void pr_att_string( long len, const char *string ) { int iel; const char *cp; const char *sp; unsigned char uc; cp = string; Printf ("\""); /* adjust len so trailing nulls don't get printed */ sp = cp + len - 1; while (len != 0 && *sp-- == '\0') len--; for (iel = 0; iel < len; iel++) switch (uc = *cp++ & 0377) { case '\b': Printf ("\\b"); break; case '\f': Printf ("\\f"); break; case '\n': /* generate linebreaks after new-lines */ Printf ("\\n\",\n\t\t\t\""); break; case '\r': Printf ("\\r"); break; case '\t': Printf ("\\t"); break; case '\v': Printf ("\\v"); break; case '\\': Printf ("\\\\"); break; case '\'': Printf ("\\'"); break; case '\"': Printf ("\\\""); break; default: Printf ("%c",uc); break; } Printf ("\""); } /* * Print list of attribute values, for numeric attributes. Attribute values * must be printed with explicit type tags, because CDL doesn't have explicit * syntax to declare an attribute type. */ static void pr_att_vals( nc_type type, long len, const void *vals ) { int iel; signed char sc; short ss; int ii; char gps[30]; float ff; double dd; /* See if we would prefer to handle this as a string. */ if (type == NC_BYTE && len > 2) { int isstring = 1; int ch; ch = ((const signed char *)vals)[len-1]; if (isprint(ch) || ch == '\0' || ch == '\n') { for (iel = 0; iel < len-1; iel++) { ch = ((const signed char *)vals)[iel]; if (!isprint(ch) && ch != '\n' && ch != '\t' && (ch & 0xff) != 0xa0) { isstring = 0; break; } } if (isstring) { pr_att_string(len, vals); return; } } } if (len == 0) return; for (iel = 0; iel < len-1; iel++) { switch (type) { case NC_BYTE: sc = ((const signed char *) vals)[iel] & 0377; Printf ("%db, ", sc); break; case NC_SHORT: ss = ((const short *)vals)[iel]; Printf ("%ds, ", ss); break; case NC_INT: ii = ((const int *)vals)[iel]; Printf ("%d, ", ii); break; case NC_FLOAT: ff = ((const float *)vals)[iel]; (void) sprintf(gps, float_att_fmt, ff); tztrim(gps); /* trim trailing 0's after '.' */ Printf ("%s, ", gps); break; case NC_DOUBLE: dd = ((const double *)vals)[iel]; (void) sprintf(gps, double_att_fmt, dd); tztrim(gps); Printf ("%s, ", gps); break; default: error("pr_att_vals: bad type"); } } switch (type) { case NC_BYTE: sc = ((const signed char *) vals)[iel] & 0377; Printf ("%db", sc); break; case NC_SHORT: ss = ((const short *)vals)[iel]; Printf ("%ds", ss); break; case NC_INT: ii = ((const int *)vals)[iel]; Printf ("%d", ii); break; case NC_FLOAT: ff = ((const float *)vals)[iel]; (void) sprintf(gps, float_att_fmt, ff); tztrim(gps); Printf ("%s", gps); break; case NC_DOUBLE: dd = ((const double *)vals)[iel]; (void) sprintf(gps, double_att_fmt, dd); tztrim(gps); Printf ("%s", gps); break; default: error("pr_att_vals: bad type"); } } static void pr_att( int ncid, int varid, const char *varname, int ia ) { struct ncatt att; /* attribute */ ncattname(ncid, varid, ia, att.name); Printf ("\t\t%s:%s = ", varname, att.name); ncattinq(ncid, varid, att.name, &att.type, &att.len); if (att.len == 0) { /* show 0-length attributes as empty strings */ att.type = NC_CHAR; /* att.len = 1; */ } switch (att.type) { case NC_CHAR: att.string = (char *) malloc(att.len + 1); /* + 1 for null byte */ if (!att.string) { error("Out of memory!"); NC_CHECK( ncclose(ncid) ); return; } *att.string = '\0'; /* Initialize in case att.len == 0 */ ncattget(ncid, varid, att.name, att.string ); pr_att_string(att.len, att.string); free(att.string); break; default: att.vals = (double *) malloc(att.len * sizeof(double)); if (!att.vals) { error("Out of memory!"); NC_CHECK( ncclose(ncid) ); return; } ncattget(ncid, varid, att.name, att.vals ); pr_att_vals(att.type, att.len, att.vals); free(att.vals); break; } Printf (" ;\n"); } static void do_ncdump(char *path, struct fspec* specp) { int ndims; /* number of dimensions */ int nvars; /* number of variables */ int ngatts; /* number of global attributes */ int xdimid; /* id of unlimited dimension */ int dimid; /* dimension id */ int varid; /* variable id */ struct ncdim dims[NC_MAX_DIMS]; /* dimensions */ long vdims[NC_MAX_DIMS]; /* dimension sizes for a single variable */ struct ncvar var; /* variable */ struct ncatt att; /* attribute */ int id; /* dimension number per variable */ int ia; /* attribute number */ int iv; /* variable number */ int is_coord; /* true if variable is a coordinate variable */ int ncid; /* netCDF id */ vnode* vlist = 0; /* list for vars specified with -v option */ int nc_status; int old_nc_opts; ncid = miopen(path, NC_NOWRITE); if (ncid < 0) { error("%s: can't open\n", path); } /* * If any vars were specified with -v option, get list of associated * variable ids */ if (specp->nlvars > 0) { vlist = newvlist(); /* list for vars specified with -v option */ for (iv=0; iv < specp->nlvars; iv++) { varid = ncvarid(ncid, specp->lvars[iv]); varadd(vlist, varid); } } /* if name not specified, derive it from path */ if (specp->name == (char *)0) { specp->name = name_path (path); } if (MI2_ISH5OBJ(ncid)) { Printf ("hdf5 %s {\n", specp->name); } else { Printf ("netcdf %s {\n", specp->name); } /* * get number of dimensions, number of variables, number of global * atts, and dimension id of unlimited dimension, if any */ nc_status = ncinquire(ncid, &ndims, &nvars, &ngatts, &xdimid); /* get dimension info */ if (ndims > 0) Printf ("dimensions:\n"); for (dimid = 0; dimid < ndims; dimid++) { NC_CHECK(ncdiminq(ncid, dimid, dims[dimid].name, &dims[dimid].size)); if (dimid == xdimid) Printf ("\t%s = %s ; // (%ld currently)\n",dims[dimid].name, "UNLIMITED", (long)dims[dimid].size); else Printf ("\t%s = %ld ;\n", dims[dimid].name, (long)dims[dimid].size); } if (nvars > 0) Printf ("variables:\n"); /* get variable info, with variable attributes */ for (varid = 0; varid < nvars; varid++) { NC_CHECK(ncvarinq(ncid, varid, var.name, &var.type, &var.ndims, var.dims, &var.natts)); Printf ("\t%s %s", type_name(var.type), var.name); if (var.ndims > 0) Printf ("("); for (id = 0; id < var.ndims; id++) { Printf ("%s%s", dims[var.dims[id]].name, id < var.ndims-1 ? ", " : ")"); } Printf (" ;\n"); /* get variable attributes */ for (ia = 0; ia < var.natts; ia++) pr_att(ncid, varid, var.name, ia); /* print ia-th attribute */ } /* get global attributes */ if (ngatts > 0) Printf ("\n// global attributes:\n"); for (ia = 0; ia < ngatts; ia++) pr_att(ncid, NC_GLOBAL, "", ia); /* print ia-th global attribute */ if (! specp->header_only) { if (nvars > 0) { Printf ("data:\n"); } /* output variable data */ for (varid = 0; varid < nvars; varid++) { /* if var list specified, test for membership */ if (specp->nlvars > 0 && ! varmember(vlist, varid)) continue; NC_CHECK( ncvarinq(ncid, varid, var.name, &var.type, &var.ndims, var.dims, &var.natts) ); if (specp->coord_vals) { /* Find out if this is a coordinate variable */ is_coord = 0; for (dimid = 0; dimid < ndims; dimid++) { if (strcmp(dims[dimid].name, var.name) == 0 && var.ndims == 1) { is_coord = 1; break; } } if (! is_coord) /* don't get data for non-coordinate vars */ continue; } /* * Only get data for variable if it is not a record variable, * or if it is a record variable and at least one record has * been written. */ if (var.ndims == 0 || var.dims[0] != xdimid || dims[xdimid].size != 0) { /* Collect variable's dim sizes */ for (id = 0; id < var.ndims; id++) vdims[id] = dims[var.dims[id]].size; var.has_fillval = 1; /* by default, but turn off for bytes */ /* get _FillValue attribute */ old_nc_opts = ncopts; ncopts = 0; nc_status = ncattinq(ncid,varid,_FillValue,&att.type,&att.len); ncopts = old_nc_opts; if(nc_status == NC_NOERR && att.type == var.type && att.len == 1) { if(var.type == NC_CHAR) { char fillc; ncattget(ncid, varid, _FillValue, &fillc ); var.fillval = fillc; } else { ncattget(ncid, varid, _FillValue, &var.fillval); } } else { switch (var.type) { case NC_BYTE: /* don't do default fill-values for bytes, too risky */ var.has_fillval = 0; break; case NC_CHAR: var.fillval = NC_FILL_CHAR; break; case NC_SHORT: var.fillval = NC_FILL_SHORT; break; case NC_INT: var.fillval = NC_FILL_INT; break; case NC_FLOAT: var.fillval = NC_FILL_FLOAT; break; case NC_DOUBLE: var.fillval = NC_FILL_DOUBLE; break; default: break; } } if (vardata(&var, vdims, ncid, varid, specp) == -1) { error("can't output data for variable %s", var.name); NC_CHECK( miclose(ncid) ); if (vlist) free(vlist); return; } } } } Printf ("}\n"); NC_CHECK( miclose(ncid) ); if (vlist) free(vlist); } static void set_brief(struct fspec * fspecp, char *key, char *optarg) { fspecp->brief_data_cmnts = true; switch (tolower(optarg[0])) { case 'c': fspecp->data_lang = LANG_C; break; case 'f': fspecp->data_lang = LANG_F; break; default: error("invalid value for -b option: %s", optarg); } } static void set_full(struct fspec * fspecp, char *key, char *optarg) { fspecp->full_data_cmnts = true; switch (tolower(optarg[0])) { case 'c': fspecp->data_lang = LANG_C; break; case 'f': fspecp->data_lang = LANG_F; break; default: error("invalid value for -f option: %s", optarg); } } static int make_lvars(struct fspec * fspecp, char *key, char *optarg) { char *cp = optarg; int nvars = 1; char ** cpp; /* compute number of variable names in comma-delimited list */ fspecp->nlvars = 1; while (*cp++) if (*cp == ',') nvars++; fspecp->lvars = (char **) malloc(nvars * sizeof(char*)); if (!fspecp->lvars) { error("out of memory"); } cpp = fspecp->lvars; /* copy variable names into list */ for (cp = strtok(optarg, ","); cp != NULL; cp = strtok((char *) NULL, ",")) { *cpp = (char *) malloc(strlen(cp) + 1); if (!*cpp) { error("out of memory"); } strcpy(*cpp, cp); cpp++; } fspecp->nlvars = nvars; return 1; } /* * Extract the significant-digits specifiers from the -d argument on the * command-line and update the default data formats appropriately. */ static int set_sigdigs(char *dest, char *key, const char *optarg) { char *ptr1 = 0; char *ptr2 = 0; int flt_digits = FLT_DIGITS; /* default floating-point digits */ int dbl_digits = DBL_DIGITS; /* default double-precision digits */ if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') flt_digits = (int)strtol(optarg, &ptr1, 10); if (flt_digits < 1 || flt_digits > 20) { error("unreasonable value for float significant digits: %d", flt_digits); } if (*ptr1 == ',') dbl_digits = (int)strtol(ptr1+1, &ptr2, 10); if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) { error("unreasonable value for double significant digits: %d", dbl_digits); } set_formats(flt_digits, dbl_digits); return 1; } /* * Extract the significant-digits specifiers from the -p argument on the * command-line, set flags so we can override C_format attributes (if any), * and update the default data formats appropriately. */ static int set_precision(char *dest, char *key, const char *optarg) { char *ptr1 = 0; char *ptr2 = 0; int flt_digits = FLT_DIGITS; /* default floating-point digits */ int dbl_digits = DBL_DIGITS; /* default double-precision digits */ if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') { flt_digits = (int)strtol(optarg, &ptr1, 10); float_precision_specified = 1; } if (flt_digits < 1 || flt_digits > 20) { error("unreasonable value for float significant digits: %d", flt_digits); } if (*ptr1 == ',') { dbl_digits = (int) strtol(ptr1+1, &ptr2, 10); double_precision_specified = 1; } if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) { error("unreasonable value for double significant digits: %d", dbl_digits); } set_formats(flt_digits, dbl_digits); return 1; } int main(int argc, char *argv[]) { static struct fspec fspec = /* defaults, overridden on command line */ { 0, /* construct netcdf name from file name */ false, /* print header info only, no data? */ false, /* just print coord vars? */ false, /* brief comments in data section? */ false, /* full annotations in data section? */ LANG_C, /* language conventions for indices */ 0, /* if -v specified, number of variables */ 0 /* if -v specified, list of variable names */ }; int i; static int max_len = 80; /* default maximum line length */ static ArgvInfo argTable[] = { {"-b", ARGV_FUNC, (char *) set_brief, (char *) &fspec, "Brief annotations for C or Fortran indices in data" }, {"-c", ARGV_CONSTANT, (char *) true, (char *) &fspec.coord_vals, "Coordinate variable data and header information" }, {"-d", ARGV_FUNC, (char *) set_sigdigs, (char *) NULL, "Obsolete option for setting significant digits" }, {"-f", ARGV_FUNC, (char *) set_full, (char *) &fspec, "Full annotations for C or Fortran indices in data" }, {"-h", ARGV_CONSTANT, (char *) true, (char *) &fspec.header_only, "Header information only, no data" }, {"-l", ARGV_INT, (char *) 1, (char *) &max_len, "Line length maximum in data section" }, {"-n", ARGV_STRING, (char *) 1, (char *) &fspec.name, "Name for netCDF (default derived from file name)" }, {"-p", ARGV_FUNC, (char *) set_precision, (char *) NULL, "Display floating-point values with less precision" }, {"-v", ARGV_FUNC, (char *) make_lvars, (char *) &fspec, "Data for variable(s) ,... only" }, {NULL, ARGV_END, NULL, NULL, NULL} }; progname = argv[0]; set_formats(FLT_DIGITS, DBL_DIGITS); /* default for float, double data */ if (ParseArgv(&argc, argv, argTable, 0)) { usage(); } set_max_len(max_len); argc--; argv++; i = 0; do { if (argc > 0) do_ncdump(argv[i], &fspec); } while (++i < argc); #ifdef vms exit(EXIT_SUCCESS); #else return EXIT_SUCCESS; #endif } minc-tools-2.3.00+dfsg/progs/mincgen/0002755000175000000620000000000012574624760016376 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincgen/ncgentab.y0000644000175000000620000005331312574624760020354 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Id: ncgentab.y,v 1.3 2008-01-13 06:49:41 stever Exp $ *********************************************************************/ /* yacc source for "ncgen", a netCDL parser and netCDF generator */ %{ #include #include #include #include "generic.h" #include "ncgen.h" #include "genlib.h" /* for grow_darray() et al */ YYSTYPE symlist; /* symbol table: linked list */ extern int derror_count; /* counts errors in netcdf definition */ extern int lineno; /* line number for error messages */ static int not_a_string; /* whether last constant read was a string */ char termstring[MAXTRST]; /* last terminal string read */ double double_val; /* last double value read */ float float_val; /* last float value read */ int int_val; /* last int value read */ short short_val; /* last short value read */ static char char_val; /* last char value read */ signed char byte_val; /* last byte value read */ static nc_type type_code; /* holds declared type for variables */ static nc_type atype_code; /* holds derived type for attributes */ char *netcdfname; /* to construct netcdf file name */ static void *att_space; /* pointer to block for attribute values */ static nc_type valtype; /* type code for list of attribute values */ static char *char_valp; /* pointers used to accumulate data values */ static signed char *byte_valp; static short *short_valp; static int *int_valp; static float *float_valp; static double *double_valp; static void *rec_cur; /* pointer to where next data value goes */ static void *rec_start; /* start of space for data */ %} /* DECLARATIONS */ %token NC_UNLIMITED_K /* keyword for unbounded record dimension */ BYTE_K /* keyword for byte datatype */ CHAR_K /* keyword for char datatype */ SHORT_K /* keyword for short datatype */ INT_K /* keyword for int datatype */ FLOAT_K /* keyword for float datatype */ DOUBLE_K /* keyword for double datatype */ IDENT /* name for a dimension, variable, or attribute */ TERMSTRING /* terminal string */ BYTE_CONST /* byte constant */ CHAR_CONST /* char constant */ SHORT_CONST /* short constant */ INT_CONST /* int constant */ FLOAT_CONST /* float constant */ DOUBLE_CONST /* double constant */ DIMENSIONS /* keyword starting dimensions section, if any */ VARIABLES /* keyword starting variables section, if any */ NETCDF /* keyword declaring netcdf name */ HDF5 /* keyword declaring hdf5 name */ DATA /* keyword starting data section, if any */ FILLVALUE /* fill value, from _FillValue attribute or default */ %start ncdesc /* start symbol for grammar */ %% /* RULES */ ncdesc: NETCDF '{' { init_netcdf(0); } dimsection /* dimension declarations */ vasection /* variable and attribute declarations */ { if (derror_count == 0) define_netcdf(netcdfname); } datasection /* data, variables loaded as encountered */ '}' { if (derror_count == 0) close_netcdf(); } | HDF5 '{' { init_netcdf(1); } dimsection /* dimension declarations */ vasection /* variable and attribute declarations */ { if (derror_count == 0) define_netcdf(netcdfname); } datasection /* data, variables loaded as encountered */ '}' { if (derror_count == 0) close_netcdf(); } ; dimsection: /* empty */ | DIMENSIONS dimdecls ; dimdecls: dimdecline ';' | dimdecls dimdecline ';' ; dimdecline: dimdecl | dimdecline ',' dimdecl ; dimdecl: dimd '=' INT_CONST { if (int_val <= 0) derror("dimension length must be positive"); dims[ndims].size = int_val; ndims++; } | dimd '=' NC_UNLIMITED_K { if (rec_dim != -1) derror("only one NC_UNLIMITED dimension allowed"); rec_dim = ndims; /* the unlimited (record) dimension */ dims[ndims].size = NC_UNLIMITED; ndims++; } ; dimd: dim { if ($1->is_dim == 1) { derror( "duplicate dimension declaration for %s", $1->name); } $1->is_dim = 1; $1->dnum = ndims; /* make sure dims array will hold dimensions */ grow_darray(ndims, /* must hold ndims+1 dims */ &dims); /* grow as needed */ dims[ndims].name = (char *) emalloc(strlen($1->name)+1); (void) strcpy(dims[ndims].name, $1->name); /* name for use in generated Fortran and C variables */ dims[ndims].lname = decodify($1->name); } ; dim: IDENT ; vasection: /* empty */ | VARIABLES vadecls | gattdecls ; vadecls: vadecl ';' | vadecls vadecl ';' ; vadecl: vardecl | attdecl ; gattdecls: gattdecl ';' | gattdecls gattdecl ';' ; gattdecl: attdecl /* causes a shift/reduce conflict */ ; vardecl: type varlist ; type: BYTE_K { type_code = NC_BYTE; } | CHAR_K { type_code = NC_CHAR; } | SHORT_K { type_code = NC_SHORT; } | INT_K { type_code = NC_INT; } | FLOAT_K { type_code = NC_FLOAT; } | DOUBLE_K{ type_code = NC_DOUBLE; } ; varlist: varspec | varlist ',' varspec ; varspec: var { static struct vars dummyvar; dummyvar.name = "dummy"; dummyvar.type = NC_DOUBLE; dummyvar.ndims = 0; dummyvar.dims = 0; dummyvar.fill_value.doublev = NC_FILL_DOUBLE; dummyvar.has_data = 0; nvdims = 0; /* make sure variable not re-declared */ if ($1->is_var == 1) { derror( "duplicate variable declaration for %s", $1->name); } $1->is_var = 1; $1->vnum = nvars; /* make sure vars array will hold variables */ grow_varray(nvars, /* must hold nvars+1 vars */ &vars); /* grow as needed */ vars[nvars] = dummyvar; /* to make Purify happy */ vars[nvars].name = (char *) emalloc(strlen($1->name)+1); (void) strcpy(vars[nvars].name, $1->name); /* name for use in generated Fortran and C variables */ vars[nvars].lname = decodify($1->name); vars[nvars].type = type_code; /* set default fill value. You can override this with * the variable attribute "_FillValue". */ nc_getfill(type_code, &vars[nvars].fill_value); vars[nvars].has_data = 0; /* has no data (yet) */ } dimspec { vars[nvars].ndims = nvdims; nvars++; } ; var: IDENT ; dimspec: /* empty */ | '(' dimlist ')' ; dimlist: vdim | dimlist ',' vdim ; vdim: dim { if (nvdims >= NC_MAX_VAR_DIMS) { derror("%s has too many dimensions",vars[nvars].name); } if ($1->is_dim == 1) dimnum = $1->dnum; else { derror( "%s is not declared as a dimension", $1->name); dimnum = ndims; } if (rec_dim != -1 && dimnum == rec_dim && nvdims != 0) { derror("unlimited dimension must be first"); } grow_iarray(nvdims, /* must hold nvdims+1 ints */ &vars[nvars].dims); /* grow as needed */ vars[nvars].dims[nvdims] = dimnum; nvdims++; } ; attdecl: att { valnum = 0; valtype = NC_UNSPECIFIED; /* get a large block for attributes, realloc later */ att_space = emalloc(MAX_NC_ATTSIZE); /* make all kinds of pointers point to it */ char_valp = (char *) att_space; byte_valp = (signed char *) att_space; short_valp = (short *) att_space; int_valp = (int *) att_space; float_valp = (float *) att_space; double_valp = (double *) att_space; } '=' attvallist { { /* check if duplicate attribute for this var */ int i; for(i=0; iis_var == 1) varnum = $1->vnum; else { derror("%s not declared as a variable, fatal error", $1->name); YYABORT; } } ; attr: IDENT { /* make sure atts array will hold attributes */ grow_aarray(natts, /* must hold natts+1 atts */ &atts); /* grow as needed */ atts[natts].name = (char *) emalloc(strlen($1->name)+1); (void) strcpy(atts[natts].name,$1->name); /* name for use in generated Fortran and C variables */ atts[natts].lname = decodify($1->name); } ; attvallist: aconst | attvallist ',' aconst ; aconst: attconst { if (valtype == NC_UNSPECIFIED) valtype = atype_code; if (valtype != atype_code) derror("values for attribute must be all of same type"); } ; attconst: CHAR_CONST { atype_code = NC_CHAR; *char_valp++ = char_val; valnum++; } | TERMSTRING { atype_code = NC_CHAR; { /* don't null-terminate attribute strings */ size_t len = strlen(termstring); if (len == 0) /* need null if that's only value */ len = 1; (void)strncpy(char_valp,termstring,len); valnum += len; char_valp += len; } } | BYTE_CONST { atype_code = NC_BYTE; *byte_valp++ = byte_val; valnum++; } | SHORT_CONST { atype_code = NC_SHORT; *short_valp++ = short_val; valnum++; } | INT_CONST { atype_code = NC_INT; *int_valp++ = int_val; valnum++; } | FLOAT_CONST { atype_code = NC_FLOAT; *float_valp++ = float_val; valnum++; } | DOUBLE_CONST { atype_code = NC_DOUBLE; *double_valp++ = double_val; valnum++; } ; datasection: /* empty */ | DATA datadecls ; datadecls: datadecl ';' | datadecls datadecl ';' ; datadecl: avar { valtype = vars[varnum].type; /* variable type */ valnum = 0; /* values accumulated for variable */ vars[varnum].has_data = 1; /* compute dimensions product */ var_size = nctypesize(valtype); if (vars[varnum].ndims == 0) { /* scalar */ var_len = 1; } else if (vars[varnum].dims[0] == rec_dim) { var_len = 1; /* one record for unlimited vars */ } else { var_len = dims[vars[varnum].dims[0]].size; } for(dimnum = 1; dimnum < vars[varnum].ndims; dimnum++) var_len = var_len*dims[vars[varnum].dims[dimnum]].size; /* allocate memory for variable data */ if (var_len*var_size != (size_t)(var_len*var_size)) { derror("variable %s too large for memory", vars[varnum].name); exit(9); } rec_len = var_len; rec_start = malloc ((size_t)(rec_len*var_size)); if (rec_start == 0) { derror ("out of memory\n"); exit(3); } rec_cur = rec_start; switch (valtype) { case NC_CHAR: char_valp = (char *) rec_start; break; case NC_BYTE: byte_valp = (signed char *) rec_start; break; case NC_SHORT: short_valp = (short *) rec_start; break; case NC_INT: int_valp = (int *) rec_start; break; case NC_FLOAT: float_valp = (float *) rec_start; break; case NC_DOUBLE: double_valp = (double *) rec_start; break; } } '=' constlist { if (valnum < var_len) { /* leftovers */ nc_fill(valtype, var_len - valnum, rec_cur, vars[varnum].fill_value); } /* put out var_len values */ vars[varnum].nrecs = valnum / rec_len; if (derror_count == 0) put_variable(rec_start); free ((char *) rec_start); } ; constlist: dconst | constlist ',' dconst ; dconst: { if(valnum >= var_len) { if (vars[varnum].dims[0] != rec_dim) { /* not recvar */ derror("too many values for this variable, %d >= %d", valnum, var_len); exit (4); } else { /* a record variable, so grow data container and increment var_len by multiple of record size */ ptrdiff_t rec_inc = (char *)rec_cur - (char *)rec_start; var_len = rec_len * (1 + valnum / rec_len); rec_start = erealloc(rec_start, var_len*var_size); rec_cur = (char *)rec_start + rec_inc; char_valp = (char *) rec_cur; byte_valp = (signed char *) rec_cur; short_valp = (short *) rec_cur; int_valp = (int *) rec_cur; float_valp = (float *) rec_cur; double_valp = (double *) rec_cur; } } not_a_string = 1; } const { if (not_a_string) { switch (valtype) { case NC_CHAR: rec_cur = (void *) char_valp; break; case NC_BYTE: rec_cur = (void *) byte_valp; break; case NC_SHORT: rec_cur = (void *) short_valp; break; case NC_INT: rec_cur = (void *) int_valp; break; case NC_FLOAT: rec_cur = (void *) float_valp; break; case NC_DOUBLE: rec_cur = (void *) double_valp; break; } } } ; const: CHAR_CONST { atype_code = NC_CHAR; switch (valtype) { case NC_CHAR: *char_valp++ = char_val; break; case NC_BYTE: *byte_valp++ = char_val; break; case NC_SHORT: *short_valp++ = char_val; break; case NC_INT: *int_valp++ = char_val; break; case NC_FLOAT: *float_valp++ = char_val; break; case NC_DOUBLE: *double_valp++ = char_val; break; } valnum++; } | TERMSTRING { not_a_string = 0; atype_code = NC_CHAR; { size_t len = strlen(termstring); if(valnum + len > var_len) { if (vars[varnum].dims[0] != rec_dim) { derror("too many values for this variable, %d>%d", valnum+len, var_len); exit (5); } else {/* a record variable so grow it */ ptrdiff_t rec_inc = (char *)rec_cur - (char *)rec_start; var_len += rec_len * (len + valnum - var_len)/rec_len; rec_start = erealloc(rec_start, var_len*var_size); rec_cur = (char *)rec_start + rec_inc; char_valp = (char *) rec_cur; } } switch (valtype) { case NC_CHAR: { int ld; size_t i, sl; (void)strncpy(char_valp,termstring,len); ld = vars[varnum].ndims-1; if (ld > 0) {/* null-fill to size of last dim */ sl = dims[vars[varnum].dims[ld]].size; for (i =len;i next) if (STREQ(sp -> name, sname)) { return sp; } return 0; /* 0 ==> not found */ } YYSTYPE install( /* install sname in symbol table */ char *sname) { YYSTYPE sp; sp = (YYSTYPE) emalloc (sizeof (struct Symbol)); sp -> name = (char *) emalloc (strlen (sname) + 1);/* +1 for '\0' */ (void) strcpy (sp -> name, sname); sp -> next = symlist; /* put at front of list */ sp -> is_dim = 0; sp -> is_var = 0; sp -> is_att = 0; symlist = sp; return sp; } void clearout(void) /* reset symbol table to empty */ { YYSTYPE sp, tp; for (sp = symlist; sp != (YYSTYPE) 0;) { tp = sp -> next; free (sp -> name); free ((char *) sp); sp = tp; } symlist = 0; } minc-tools-2.3.00+dfsg/progs/mincgen/generic.h0000644000175000000620000000104512574624760020161 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincgen/generic.h,v 1.1 2004-06-15 20:14:40 bert Exp $ *********************************************************************/ #ifndef UD_GENERIC_H #define UD_GENERIC_H union generic { /* used to hold any kind of fill_value */ float floatv; double doublev; int intv; short shortv; char charv; }; #endif minc-tools-2.3.00+dfsg/progs/mincgen/main.c0000644000175000000620000000571512574624760017474 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincgen/main.c,v 1.6 2008-01-11 07:17:08 stever Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef __hpux #include #endif #include #include #include "generic.h" #include "ncgen.h" #include "genlib.h" extern int yyparse(void); const char *progname; /* for error messages */ const char *cdlname; int c_flag; int fortran_flag; int netcdf_flag; char *netcdf_name = NULL; /* name of output netCDF file to write */ extern FILE *yyin; static const char* ubasename ( const char* av0 ); static void usage ( void ); int main ( int argc, char** argv ); /* strip off leading path */ static const char * ubasename( const char *av0) { const char *logident ; #ifdef VMS #define SEP ']' #endif #ifdef MSDOS #define SEP '\\' #endif #ifndef SEP #define SEP '/' #endif if ((logident = strrchr(av0, SEP)) == NULL) logident = av0 ; else logident++ ; return logident ; } static void usage(void) { derror("Usage: %s [ -b ] [ -c ] [ -f ] [ -o outfile] [ file ... ]", progname); derror("netcdf library version %s", nc_inq_libvers()); } int main( int argc, char *argv[]) { FILE *fp; static ArgvInfo argTable[] = { { "-b", ARGV_CONSTANT, (char *) 1, (char *) &netcdf_flag, "Select binary netCDF output" }, { "-c", ARGV_CONSTANT, (char *) 1, (char *) &c_flag, "Select 'C' output" }, { "-f", ARGV_CONSTANT, (char *) 1, (char *) &fortran_flag, "Select FORTRAN output" }, { "-o", ARGV_STRING, (char *) 1, (char *) &netcdf_name, "Select name for netCDF output" }, { NULL, ARGV_END, NULL, NULL, NULL } }; #ifdef __hpux setlocale(LC_CTYPE,""); #endif #ifdef MDEBUG malloc_debug(2) ; /* helps find malloc/free errors on Sun */ #endif /* MDEBUG */ progname = ubasename(argv[0]); cdlname = "-"; c_flag = 0; fortran_flag = 0; netcdf_flag = 0; #if _CRAYMPP && 0 /* initialize CRAY MPP parallel-I/O library */ (void) par_io_init(32, 32); #endif if (ParseArgv(&argc, argv, argTable, 0)) { usage(); return (8); } if (fortran_flag && c_flag) { derror("Only one of -c or -f may be specified"); return(8); } if (netcdf_name != NULL) { netcdf_flag = 1; } argc -= 1; argv += 1; if (argc > 1) { derror ("%s: only one input file argument permitted",progname); return(6); } fp = stdin; if (argc > 0 && strcmp(argv[0], "-") != 0) { if ((fp = fopen(argv[0], "r")) == NULL) { derror ("can't open file %s for reading: ", argv[0]); perror(""); return(7); } cdlname = argv[0]; } yyin = fp; return (yyparse()); } minc-tools-2.3.00+dfsg/progs/mincgen/ncgenyy.l0000644000175000000620000001607212574624760020233 0ustar stevestaff%{ /********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Id: ncgenyy.l,v 1.3 2005-06-22 21:02:11 bert Exp $ *********************************************************************/ /* lex specification for tokens for ncgen */ /* Fill value used by ncdump from version 2.4 and later. Should match definition of FILL_STRING in ../ncdump/vardata.h */ #define FILL_STRING "_" #define XDR_INT_MIN (-2147483647-1) #define XDR_INT_MAX 2147483647 char errstr[100]; /* for short error messages */ #include #include #include #include "ncgen.h" #include "genlib.h" #include "ncgentab.h" extern int int_val; extern float float_val; #if !defined (YYSTYPE) && !defined(YYSTYPE_IS_DECLARED) extern void * yylval; #define YYLVAL_NULL ((void *) NULL) #else #define YYLVAL_NULL ((YYSTYPE) NULL) #endif /* YYSTYPE defined */ extern int lineno; extern signed char byte_val; extern short short_val; extern double double_val; extern char *netcdfname; extern char termstring[]; #define YY_BREAK /* defining as nothing eliminates unreachable statement warnings from flex output, but make sure every action ends with "return" or "break"! */ %} %p 6000 escaped \\. nonquotes ([^"\\]|{escaped})* exp ([eE][+-]?[0-9]+) %% \/\/.* { /* comment */ break; } \"{nonquotes}\" { if(yyleng > MAXTRST) { yyerror("string too long, truncated\n"); yytext[MAXTRST-1] = '\0'; } expand_escapes(termstring,(char *)yytext,yyleng); return (TERMSTRING); } float|FLOAT|real|REAL {return (FLOAT_K);} char|CHAR {return (CHAR_K);} byte|BYTE {return (BYTE_K);} short|SHORT {return (SHORT_K);} long|LONG|int|INT|integer|INTEGER {return (INT_K);} double|DOUBLE {return (DOUBLE_K);} unlimited|UNLIMITED {int_val = -1; return (NC_UNLIMITED_K);} dimensions:|DIMENSIONS: {return (DIMENSIONS);} variables:|VARIABLES: {return (VARIABLES);} data:|DATA: {return (DATA);} (netcdf|NETCDF|netCDF)[ \t]+[^\{]+ { char *s = (char*)yytext+strlen("netcdf"); char *t = (char*)yytext+yyleng-1; while (isspace(*s)) s++; while (isspace(*t)) t--; t++; if (t-s+1 < 1) { yyerror("netCDF name required"); return (DATA); /* generate syntax error */ } netcdfname = (char *) emalloc(t-s+1); (void) strncpy(netcdfname, s, t-s); netcdfname[t-s] = '\0'; return (NETCDF); } (hdf5|HDF5)[ \t]+[^\{]+ { char *s = (char*)yytext+strlen("hdf5"); char *t = (char*)yytext+yyleng-1; while (isspace(*s)) s++; while (isspace(*t)) t--; t++; if (t-s+1 < 1) { yyerror("HDF5 name required"); return (DATA); /* generate syntax error */ } netcdfname = (char *) emalloc(t-s+1); (void) strncpy(netcdfname, s, t-s); netcdfname[t-s] = '\0'; return (HDF5); } DoubleInf|NaN|-?Infinity { /* missing value (pre-2.4 backward compatibility) */ if (yytext[0] == '-') { double_val = -NC_FILL_DOUBLE; } else { double_val = NC_FILL_DOUBLE; } return (DOUBLE_CONST); } FloatInf|-?Inff { /* missing value (pre-2.4 backward compatibility) */ if (yytext[0] == '-') { float_val = -NC_FILL_FLOAT; } else { float_val = NC_FILL_FLOAT; } return (FLOAT_CONST); } [A-Za-z_][A-Z.@#\[\]a-z_0-9-]* { if (STREQ((char *)yytext, FILL_STRING)) return (FILLVALUE); if ((yylval = lookup((char *)yytext)) == YYLVAL_NULL) { yylval = install((char *)yytext); } return (IDENT); } \n { lineno++ ; break; } [+-]?[0-9]*[0-9][Bb] { int ii; if (sscanf((char*)yytext, "%d", &ii) != 1) { sprintf(errstr,"bad byte constant: %s",(char*)yytext); yyerror(errstr); } byte_val = ii; if (ii != (int)byte_val) { sprintf(errstr,"byte constant out of range (-128,127): %s",(char*)yytext); yyerror(errstr); } return (BYTE_CONST); } [+-]?[0-9]*\.[0-9]*{exp}?[LlDd]?|[+-]?[0-9]*{exp}[LlDd]? { if (sscanf((char*)yytext, "%le", &double_val) != 1) { sprintf(errstr,"bad long or double constant: %s",(char*)yytext); yyerror(errstr); } return (DOUBLE_CONST); } [+-]?[0-9]*\.[0-9]*{exp}?[Ff]|[+-]?[0-9]*{exp}[Ff] { if (sscanf((char*)yytext, "%e", &float_val) != 1) { sprintf(errstr,"bad float constant: %s",(char*)yytext); yyerror(errstr); } return (FLOAT_CONST); } [+-]?[0-9]+[sS]|0[xX][0-9a-fA-F]+[sS] { if (sscanf((char*)yytext, "%hd", &short_val) != 1) { sprintf(errstr,"bad short constant: %s",(char*)yytext); yyerror(errstr); } return (SHORT_CONST); } [+-]?([1-9][0-9]*|0)[lL]? { char *ptr; errno = 0; double_val = strtod((char*)yytext, &ptr); if (errno != 0 && double_val == 0.0) { sprintf(errstr,"bad numerical constant: %s",(char*)yytext); yyerror(errstr); } if (double_val < XDR_INT_MIN ||double_val > XDR_INT_MAX) { return DOUBLE_CONST; } else { int_val = (int) double_val; return INT_CONST; } } 0[xX]?[0-9a-fA-F]+[lL]? { char *ptr; long long_val; errno = 0; long_val = strtol((char*)yytext, &ptr, 0); if (errno != 0) { sprintf(errstr,"bad long constant: %s",(char*)yytext); yyerror(errstr); } if (long_val < XDR_INT_MIN || long_val > XDR_INT_MAX) { double_val = (double) long_val; return DOUBLE_CONST; } else { int_val = (int) long_val; return INT_CONST; } } \'[^\\]\' { (void) sscanf((char*)&yytext[1],"%c",&byte_val); return (BYTE_CONST); } \'\\[0-7][0-7]?[0-7]?\' { byte_val = (char) strtol((char*)&yytext[2], (char **) 0, 8); return (BYTE_CONST); } \'\\[xX][0-9a-fA-F][0-9a-fA-F]?\' { byte_val = (char) strtol((char*)&yytext[3], (char **) 0, 16); return (BYTE_CONST); } \'\\.\' { switch ((char)yytext[2]) { case 'a': byte_val = '\007'; break; /* not everyone under- * stands '\a' yet */ case 'b': byte_val = '\b'; break; case 'f': byte_val = '\f'; break; case 'n': byte_val = '\n'; break; case 'r': byte_val = '\r'; break; case 't': byte_val = '\t'; break; case 'v': byte_val = '\v'; break; case '\\': byte_val = '\\'; break; case '?': byte_val = '\177'; break; case '\'': byte_val = '\''; break; default: byte_val = (char)yytext[2]; } return (BYTE_CONST); } [ \t\f]+ { /* whitespace */ break; } . return (yytext[0]) ; minc-tools-2.3.00+dfsg/progs/mincgen/genlib.c0000644000175000000620000013012712574624760020004 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincgen/genlib.c,v 1.5 2008-01-12 19:08:15 stever Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include /* for isprint() */ #ifndef NO_STDARG #include #else /* try varargs instead */ #include #endif /* !NO_STDARG */ #include #include "generic.h" #include "ncgen.h" #include "genlib.h" extern char *netcdf_name; /* output netCDF filename, if on command line. */ extern int netcdf_flag; extern int c_flag; extern int fortran_flag; int lineno = 1; int derror_count = 0; /* create netCDF from in-memory structure */ static void gen_netcdf( char *filename) /* name for output netcdf file */ { int idim, ivar, iatt; int dimid; int varid; int stat; #if MINC2 ncid = micreate(filename, (is_hdf5) ? (MI2_CREATE_V2 | NC_CLOBBER) : NC_CLOBBER); #else ncid = micreate(filename, NC_CLOBBER); #endif check_err(ncid); /* define dimensions from info in dims array */ for (idim = 0; idim < ndims; idim++) { dimid = ncdimdef(ncid, dims[idim].name, dims[idim].size); check_err(dimid); } /* define variables from info in vars array */ for (ivar = 0; ivar < nvars; ivar++) { varid = ncvardef(ncid, vars[ivar].name, vars[ivar].type, vars[ivar].ndims, vars[ivar].dims); check_err(varid); } /* define attributes from info in atts array */ for (iatt = 0; iatt < natts; iatt++) { varid = (atts[iatt].var == -1) ? NC_GLOBAL : atts[iatt].var; stat = ncattput(ncid, varid, atts[iatt].name, atts[iatt].type, atts[iatt].len, atts[iatt].val); check_err(stat); } stat = ncendef(ncid); check_err(stat); } /* * Given a netcdf type, a pointer to a vector of values of that type, * and the index of the vector element desired, returns a pointer to a * malloced string representing the value in C. */ static char * cstring( nc_type type, /* netCDF type code */ void *valp, /* pointer to vector of values */ int num) /* element of vector desired */ { static char *cp, *sp, ch; signed char *bytep; short *shortp; int *intp; float *floatp; double *doublep; switch (type) { case NC_CHAR: sp = cp = (char *) emalloc (7); *cp++ = '\''; ch = *((char *)valp + num); switch (ch) { case '\b': *cp++ = '\\'; *cp++ = 'b'; break; case '\f': *cp++ = '\\'; *cp++ = 'f'; break; case '\n': *cp++ = '\\'; *cp++ = 'n'; break; case '\r': *cp++ = '\\'; *cp++ = 'r'; break; case '\t': *cp++ = '\\'; *cp++ = 't'; break; case '\v': *cp++ = '\\'; *cp++ = 'v'; break; case '\\': *cp++ = '\\'; *cp++ = '\\'; break; case '\'': *cp++ = '\\'; *cp++ = '\''; break; default: if (!isprint(ch)) { static char octs[] = "01234567"; int rem = ((unsigned char)ch)%64; *cp++ = '\\'; *cp++ = octs[((unsigned char)ch)/64]; /* to get, e.g. '\177' */ *cp++ = octs[rem/8]; *cp++ = octs[rem%8]; } else { *cp++ = ch; } break; } *cp++ = '\''; *cp = '\0'; return sp; case NC_BYTE: cp = (char *) emalloc (7); bytep = (signed char *)valp; /* Need to convert '\377' to -1, for example, on all platforms */ (void) sprintf(cp,"%d", (signed char) *(bytep+num)); return cp; case NC_SHORT: cp = (char *) emalloc (10); shortp = (short *)valp; (void) sprintf(cp,"%d",* (shortp + num)); return cp; case NC_INT: cp = (char *) emalloc (20); intp = (int *)valp; (void) sprintf(cp,"%d",* (intp + num)); return cp; case NC_FLOAT: cp = (char *) emalloc (20); floatp = (float *)valp; (void) sprintf(cp,"%.8g",* (floatp + num)); return cp; case NC_DOUBLE: cp = (char *) emalloc (20); doublep = (double *)valp; (void) sprintf(cp,"%.16g",* (doublep + num)); return cp; default: derror("cstring: bad type code"); return 0; } } /* * Generate C code for creating netCDF from in-memory structure. */ static void gen_c( char *filename) { int idim, ivar, iatt, maxdims; unsigned int jatt; int vector_atts; char *val_string; char stmnt[C_MAX_STMNT]; /* wrap in main program */ cline("#include "); cline("#include "); cline("#include "); cline(""); cline("void"); cline("check_err(const int stat, const int line, const char *file) {"); cline(" if (stat != NC_NOERR) {"); cline(" (void) fprintf(stderr, \"line %d of %s: %s\\n\", line, file, nc_strerror(stat));"); cline(" exit(1);"); cline(" }"); cline("}"); cline(""); cline("int"); sprintf(stmnt, "main() {\t\t\t/* create %s */", filename); cline(stmnt); /* create necessary declarations */ cline(""); cline(" int ncid;\t\t\t/* netCDF id */"); if (ndims > 0) { cline(""); cline(" /* dimension ids */"); for (idim = 0; idim < ndims; idim++) { sprintf(stmnt, " int %s_dim;", dims[idim].lname); cline(stmnt); } cline(""); cline(" /* dimension lengths */"); for (idim = 0; idim < ndims; idim++) { if (dims[idim].size == NC_UNLIMITED) { sprintf(stmnt, " size_t %s_len = NC_UNLIMITED;", dims[idim].lname); } else { sprintf(stmnt, " size_t %s_len = %lu;", dims[idim].lname, (unsigned long) dims[idim].size); } cline(stmnt); } } maxdims = 0; /* most dimensions of any variable */ for (ivar = 0; ivar < nvars; ivar++) if (vars[ivar].ndims > maxdims) maxdims = vars[ivar].ndims; if (nvars > 0) { cline(""); cline(" /* variable ids */"); for (ivar = 0; ivar < nvars; ivar++) { sprintf(stmnt, " int %s_id;", vars[ivar].lname); cline(stmnt); } cline(""); cline(" /* rank (number of dimensions) for each variable */"); for (ivar = 0; ivar < nvars; ivar++) { sprintf(stmnt, "# define RANK_%s %d", vars[ivar].lname, vars[ivar].ndims); cline(stmnt); } if (maxdims > 0) { /* we have dimensioned variables */ cline(""); cline(" /* variable shapes */"); for (ivar = 0; ivar < nvars; ivar++) { if (vars[ivar].ndims > 0) { sprintf(stmnt, " int %s_dims[RANK_%s];", vars[ivar].lname, vars[ivar].lname); cline(stmnt); } } } } /* determine if we need any attribute vectors */ vector_atts = 0; for (iatt = 0; iatt < natts; iatt++) { if (atts[iatt].type != NC_CHAR) { vector_atts = 1; break; } } if (vector_atts) { cline(""); cline(" /* attribute vectors */"); for (iatt = 0; iatt < natts; iatt++) { if (atts[iatt].type != NC_CHAR) { sprintf(stmnt, " %s %s_%s[%lu];", ncatype(atts[iatt].type), atts[iatt].var == -1 ? "cdf" : vars[atts[iatt].var].lname, atts[iatt].lname, (unsigned long) atts[iatt].len); cline(stmnt); } } } /* create netCDF file, uses NC_CLOBBER mode */ cline(""); cline(" /* enter define mode */"); sprintf(stmnt, " int stat = nc_create(\"%s\", NC_CLOBBER, &ncid);", filename); cline(stmnt); cline(" check_err(stat,__LINE__,__FILE__);"); /* define dimensions from info in dims array */ if (ndims > 0) { cline(""); cline(" /* define dimensions */"); } for (idim = 0; idim < ndims; idim++) { sprintf(stmnt, " stat = nc_def_dim(ncid, \"%s\", %s_len, &%s_dim);", dims[idim].name, dims[idim].lname, dims[idim].lname); cline(stmnt); cline(" check_err(stat,__LINE__,__FILE__);"); } /* define variables from info in vars array */ if (nvars > 0) { cline(""); cline(" /* define variables */"); for (ivar = 0; ivar < nvars; ivar++) { cline(""); for (idim = 0; idim < vars[ivar].ndims; idim++) { sprintf(stmnt, " %s_dims[%d] = %s_dim;", vars[ivar].lname, idim, dims[vars[ivar].dims[idim]].lname); cline(stmnt); } if (vars[ivar].ndims > 0) { /* a dimensioned variable */ sprintf(stmnt, " stat = nc_def_var(ncid, \"%s\", %s, RANK_%s, %s_dims, &%s_id);", vars[ivar].name, nctype(vars[ivar].type), vars[ivar].lname, vars[ivar].lname, vars[ivar].lname); } else { /* a scalar */ sprintf(stmnt, " stat = nc_def_var(ncid, \"%s\", %s, RANK_%s, 0, &%s_id);", vars[ivar].name, nctype(vars[ivar].type), vars[ivar].lname, vars[ivar].lname); } cline(stmnt); cline(" check_err(stat,__LINE__,__FILE__);"); } } /* define attributes from info in atts array */ if (natts > 0) { cline(""); cline(" /* assign attributes */"); for (iatt = 0; iatt < natts; iatt++) { if (atts[iatt].type == NC_CHAR) { /* string */ val_string = cstrstr((char *) atts[iatt].val, atts[iatt].len); sprintf(stmnt, " stat = nc_put_att_text(ncid, %s%s, \"%s\", %lu, %s);", atts[iatt].var == -1 ? "NC_GLOBAL" : vars[atts[iatt].var].lname, atts[iatt].var == -1 ? "" : "_id", atts[iatt].name, (unsigned long) atts[iatt].len, val_string); cline(stmnt); free (val_string); } else { /* vector attribute */ for (jatt = 0; jatt < atts[iatt].len ; jatt++) { val_string = cstring(atts[iatt].type,atts[iatt].val,jatt); sprintf(stmnt, " %s_%s[%d] = %s;", atts[iatt].var == -1 ? "cdf" : vars[atts[iatt].var].lname, atts[iatt].lname, jatt, val_string); cline(stmnt); free (val_string); } sprintf(stmnt, " stat = nc_put_att_%s(ncid, %s%s, \"%s\", %s, %lu, %s_%s);", ncatype(atts[iatt].type), atts[iatt].var == -1 ? "NC_GLOBAL" : vars[atts[iatt].var].lname, atts[iatt].var == -1 ? "" : "_id", atts[iatt].name, nctype(atts[iatt].type), (unsigned long) atts[iatt].len, atts[iatt].var == -1 ? "cdf" : vars[atts[iatt].var].lname, atts[iatt].lname); cline(stmnt); } cline(" check_err(stat,__LINE__,__FILE__);"); } } cline(""); cline(" /* leave define mode */"); cline(" stat = nc_enddef (ncid);"); cline(" check_err(stat,__LINE__,__FILE__);"); } /* return Fortran type name for netCDF type, given type code */ static char * ncftype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "integer"; case NC_CHAR: return "character"; case NC_SHORT: return "integer"; case NC_INT: #ifdef MSDOS return "integer*4"; #else return "integer"; #endif case NC_FLOAT: return "real"; #ifdef _CRAY case NC_DOUBLE: return "real"; /* we don't support CRAY 128-bit doubles */ #else case NC_DOUBLE: return "double precision"; #endif default: derror("ncftype: bad type code"); return 0; } } /* return Fortran type suffix for netCDF type, given type code */ char * nfstype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "int1"; case NC_CHAR: return "text"; case NC_SHORT: return "int2"; case NC_INT: return "int"; case NC_FLOAT: return "real"; case NC_DOUBLE: return "double"; default: derror("nfstype: bad type code"); return 0; } } /* Return Fortran function suffix for netCDF type, given type code. * This should correspond to the Fortran type name in ncftype(). */ char * nfftype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "int"; case NC_CHAR: return "text"; case NC_SHORT: return "int"; case NC_INT: return "int"; case NC_FLOAT: return "real"; #ifdef _CRAY case NC_DOUBLE: return "real"; /* we don't support CRAY 128-bit doubles */ #else case NC_DOUBLE: return "double"; #endif default: derror("nfstype: bad type code"); return 0; } } /* return FORTRAN name for netCDF type, given type code */ static char * ftypename( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "NF_INT1"; case NC_CHAR: return "NF_CHAR"; case NC_SHORT: return "NF_INT2"; case NC_INT: return "NF_INT"; case NC_FLOAT: return "NF_REAL"; case NC_DOUBLE: return "NF_DOUBLE"; default: derror("ftypename: bad type code"); return 0; } } /* * Generate FORTRAN code for creating netCDF from in-memory structure. */ static void gen_fortran( char *filename) { int idim, ivar, iatt, itype, maxdims; unsigned int jatt; int vector_atts; char *val_string; char stmnt[FORT_MAX_STMNT]; char s2[NC_MAX_NAME + 10]; char *sp; /* Need how many netCDF types there are, because we create an array * for each type of attribute. */ int ntypes = 6; /* number of netCDF types, NC_BYTE, ... */ nc_type types[6]; /* at least ntypes */ size_t max_atts[NC_DOUBLE + 1]; types[0] = NC_BYTE; types[1] = NC_CHAR; types[2] = NC_SHORT; types[3] = NC_INT; types[4] = NC_FLOAT; types[5] = NC_DOUBLE; fline("program fgennc"); fline("include 'netcdf.inc'"); /* create necessary declarations */ fline("* error status return"); fline("integer iret"); fline("* netCDF id"); fline("integer ncid"); if (ndims > 0) { fline("* dimension ids"); for (idim = 0; idim < ndims; idim++) { sprintf(stmnt, "integer %s_dim", dims[idim].lname); fline(stmnt); } fline("* dimension lengths"); for (idim = 0; idim < ndims; idim++) { sprintf(stmnt, "integer %s_len", dims[idim].lname); fline(stmnt); } for (idim = 0; idim < ndims; idim++) { if (dims[idim].size == NC_UNLIMITED) { sprintf(stmnt, "parameter (%s_len = NF_UNLIMITED)", dims[idim].lname); } else { sprintf(stmnt, "parameter (%s_len = %lu)", dims[idim].lname, (unsigned long) dims[idim].size); } fline(stmnt); } } maxdims = 0; /* most dimensions of any variable */ for (ivar = 0; ivar < nvars; ivar++) if (vars[ivar].ndims > maxdims) maxdims = vars[ivar].ndims; if (nvars > 0) { fline("* variable ids"); for (ivar = 0; ivar < nvars; ivar++) { sprintf(stmnt, "integer %s_id", vars[ivar].lname); fline(stmnt); } fline("* rank (number of dimensions) for each variable"); for (ivar = 0; ivar < nvars; ivar++) { sprintf(stmnt, "integer %s_rank", vars[ivar].lname); fline(stmnt); } for (ivar = 0; ivar < nvars; ivar++) { sprintf(stmnt, "parameter (%s_rank = %d)", vars[ivar].lname, vars[ivar].ndims); fline(stmnt); } fline("* variable shapes"); for (ivar = 0; ivar < nvars; ivar++) { if (vars[ivar].ndims > 0) { sprintf(stmnt, "integer %s_dims(%s_rank)", vars[ivar].lname, vars[ivar].lname); fline(stmnt); } } } /* declarations for variables to be initialized */ if (nvars > 0) { /* we have variables */ fline("* data variables"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; /* Generate declarations here for non-record data variables only. Record variables are declared in separate subroutine later, when we know how big they are. */ if (v->ndims > 0 && v->dims[0] == rec_dim) { continue; } /* Make declarations for non-text variables only; for text variables, just include string in nf_put_var call */ if (v->type == NC_CHAR) { continue; } if (v->ndims == 0) { /* scalar */ sprintf(stmnt, "%s %s", ncftype(v->type), v->lname); } else { sprintf(stmnt, "%s %s(", ncftype(v->type), v->lname); /* reverse dimensions for FORTRAN */ for (idim = v->ndims-1; idim >= 0; idim--) { sprintf(s2, "%s_len, ", dims[v->dims[idim]].lname); strcat(stmnt, s2); } sp = strrchr(stmnt, ','); if(sp != NULL) { *sp = '\0'; } strcat(stmnt, ")"); } fline(stmnt); } } /* determine what attribute vectors needed */ for (itype = 0; itype < ntypes; itype++) max_atts[(int)types[itype]] = 0; vector_atts = 0; for (iatt = 0; iatt < natts; iatt++) { if (atts[iatt].len > max_atts[(int) atts[iatt].type]) { max_atts[(int)atts[iatt].type] = atts[iatt].len; vector_atts = 1; } } if (vector_atts) { fline("* attribute vectors"); for (itype = 0; itype < ntypes; itype++) { if (types[itype] != NC_CHAR && max_atts[(int)types[itype]] > 0) { sprintf(stmnt, "%s %sval(%lu)", ncftype(types[itype]), nfstype(types[itype]), (unsigned long) max_atts[(int)types[itype]]); fline(stmnt); } } } /* create netCDF file, uses NC_CLOBBER mode */ fline("* enter define mode"); sprintf(stmnt, "iret = nf_create(\'%s\', NF_CLOBBER, ncid)", filename); fline(stmnt); fline("call check_err(iret)"); /* define dimensions from info in dims array */ if (ndims > 0) fline("* define dimensions"); for (idim = 0; idim < ndims; idim++) { if (dims[idim].size == NC_UNLIMITED) sprintf(stmnt, "iret = nf_def_dim(ncid, \'%s\', NF_UNLIMITED, %s_dim)", dims[idim].name, dims[idim].lname); else sprintf(stmnt, "iret = nf_def_dim(ncid, \'%s\', %lu, %s_dim)", dims[idim].name, (unsigned long) dims[idim].size, dims[idim].lname); fline(stmnt); fline("call check_err(iret)"); } /* define variables from info in vars array */ if (nvars > 0) { fline("* define variables"); for (ivar = 0; ivar < nvars; ivar++) { for (idim = 0; idim < vars[ivar].ndims; idim++) { sprintf(stmnt, "%s_dims(%d) = %s_dim", vars[ivar].lname, vars[ivar].ndims - idim, /* reverse dimensions */ dims[vars[ivar].dims[idim]].lname); fline(stmnt); } if (vars[ivar].ndims > 0) { /* a dimensioned variable */ sprintf(stmnt, "iret = nf_def_var(ncid, \'%s\', %s, %s_rank, %s_dims, %s_id)", vars[ivar].name, ftypename(vars[ivar].type), vars[ivar].lname, vars[ivar].lname, vars[ivar].lname); } else { /* a scalar */ sprintf(stmnt, "iret = nf_def_var(ncid, \'%s\', %s, %s_rank, 0, %s_id)", vars[ivar].name, ftypename(vars[ivar].type), vars[ivar].lname, vars[ivar].lname); } fline(stmnt); fline("call check_err(iret)"); } } /* define attributes from info in atts array */ if (natts > 0) { fline("* assign attributes"); for (iatt = 0; iatt < natts; iatt++) { if (atts[iatt].type == NC_CHAR) { /* string */ val_string = fstrstr((char *) atts[iatt].val, atts[iatt].len); sprintf(stmnt, "iret = nf_put_att_text(ncid, %s%s, \'%s\', %lu, %s)", atts[iatt].var == -1 ? "NF_GLOBAL" : vars[atts[iatt].var].lname, atts[iatt].var == -1 ? "" : "_id", atts[iatt].name, (unsigned long) atts[iatt].len, val_string); fline(stmnt); fline("call check_err(iret)"); free(val_string); } else { for (jatt = 0; jatt < atts[iatt].len ; jatt++) { val_string = fstring(atts[iatt].type,atts[iatt].val,jatt); sprintf(stmnt, "%sval(%d) = %s", nfstype(atts[iatt].type), jatt+1, val_string); fline(stmnt); free (val_string); } sprintf(stmnt, "iret = nf_put_att_%s(ncid, %s%s, \'%s\', %s, %lu, %sval)", nfftype(atts[iatt].type), atts[iatt].var == -1 ? "NCGLOBAL" : vars[atts[iatt].var].lname, atts[iatt].var == -1 ? "" : "_id", atts[iatt].name, ftypename(atts[iatt].type), (unsigned long) atts[iatt].len, nfstype(atts[iatt].type)); fline(stmnt); fline("call check_err(iret)"); } } } fline("* leave define mode"); fline("iret = nf_enddef(ncid)"); fline("call check_err(iret)"); } /* * Output a C statement. */ void cline( const char *stmnt) { FILE *cout = stdout; fputs(stmnt, cout); fputs("\n", cout); } /* * From a long line FORTRAN statment, generates the necessary FORTRAN * lines with continuation characters in column 6. If stmnt starts with "*", * it is treated as a one-line comment. Statement labels are *not* handled, * but since we don't generate any labels, we don't care. */ void fline( const char *stmnt) { FILE *fout = stdout; int len = (int) strlen(stmnt); int line = 0; static char cont[] = { /* continuation characters */ ' ', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; if(stmnt[0] == '*') { fputs(stmnt, fout); fputs("\n", fout); return; } while (len > 0) { if (line >= FORT_MAX_LINES) derror("FORTRAN statement too long: %s",stmnt); (void) fprintf(fout, " %c", cont[line++]); (void) fprintf(fout, "%.66s\n", stmnt); len -= 66; if (len > 0) stmnt += 66; } } /* return C name for netCDF type, given type code */ char * nctype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "NC_BYTE"; case NC_CHAR: return "NC_CHAR"; case NC_SHORT: return "NC_SHORT"; case NC_INT: return "NC_INT"; case NC_FLOAT: return "NC_FLOAT"; case NC_DOUBLE: return "NC_DOUBLE"; default: derror("nctype: bad type code"); return 0; } } /* * Return C type name for netCDF type, given type code. */ char * ncctype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "signed char"; 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"; default: derror("ncctype: bad type code"); return 0; } } /* * Return C type name for netCDF type suffix, given type code. */ char * ncstype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "schar"; case NC_CHAR: return "text"; case NC_SHORT: return "short"; case NC_INT: return "int"; case NC_FLOAT: return "float"; case NC_DOUBLE: return "double"; default: derror("ncstype: bad type code"); return 0; } } /* * Return C type name for netCDF attribute container type, given type code. */ char * ncatype( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return "int"; /* avoids choosing between uchar and schar */ case NC_CHAR: return "text"; case NC_SHORT: return "short"; case NC_INT: return "int"; case NC_FLOAT: return "float"; case NC_DOUBLE: return "double"; default: derror("ncatype: bad type code"); return 0; } } /* return internal size for values of specified netCDF type */ size_t nctypesize( nc_type type) /* netCDF type code */ { switch (type) { case NC_BYTE: return sizeof(char); case NC_CHAR: return sizeof(char); case NC_SHORT: return sizeof(short); case NC_INT: return sizeof(int); case NC_FLOAT: return sizeof(float); case NC_DOUBLE: return sizeof(double); default: derror("nctypesize: bad type code"); return 0; } } /* * Given a netcdf numeric type, a pointer to a vector of values of that * type, and the index of the vector element desired, returns a pointer * to a malloced string representing the value in FORTRAN. Since this * may be used in a DATA statement, it must not include non-constant * expressions, such as "char(26)". */ char * fstring( nc_type type, /* netCDF type code */ void *valp, /* pointer to vector of values */ int num) /* element of vector desired */ { static char *cp; signed char *schp; short *shortp; int *intp; float *floatp; double *doublep; switch (type) { case NC_BYTE: cp = (char *) emalloc (10); schp = (signed char *)valp; sprintf(cp,"%d", schp[num]); return cp; case NC_SHORT: cp = (char *) emalloc (10); shortp = (short *)valp; (void) sprintf(cp,"%d",* (shortp + num)); return cp; case NC_INT: cp = (char *) emalloc (20); intp = (int *)valp; (void) sprintf(cp,"%d",* (intp + num)); return cp; case NC_FLOAT: cp = (char *) emalloc (20); floatp = (float *)valp; (void) sprintf(cp,"%.8g",* (floatp + num)); return cp; case NC_DOUBLE: cp = (char *) emalloc (25); doublep = (double *)valp; (void) sprintf(cp,"%.16g",* (doublep + num)); expe2d(cp); /* change 'e' to 'd' in exponent */ return cp; default: derror("fstring: bad type code"); return 0; } } /* * Given a pointer to a counted string, returns a pointer to a malloced string * representing the string as a C constant. */ char * cstrstr( const char *valp, /* pointer to vector of characters*/ size_t len) /* number of characters in valp */ { static char *sp; char *cp; char *istr, *istr0; /* for null-terminated copy */ size_t ii; if(4*len+3 != (unsigned)(4*len+3)) { derror("too much character data!"); exit(9); } sp = cp = (char *) emalloc(4*len+3); if(len == 1 && *valp == 0) { /* empty string */ strcpy(sp,"\"\""); return sp; } istr0 = istr = (char *) emalloc(len + 1); for(ii = 0; ii < len; ii++) { istr[ii] = valp[ii]; } istr[len] = '\0'; *cp++ = '"'; for(ii = 0; ii < len; ii++) { switch (*istr) { case '\0': *cp++ = '\\'; *cp++ = '0'; *cp++ = '0'; *cp++ = '0'; break; case '\b': *cp++ = '\\'; *cp++ = 'b'; break; case '\f': *cp++ = '\\'; *cp++ = 'f'; break; case '\n': *cp++ = '\\'; *cp++ = 'n'; break; case '\r': *cp++ = '\\'; *cp++ = 'r'; break; case '\t': *cp++ = '\\'; *cp++ = 't'; break; case '\v': *cp++ = '\\'; *cp++ = 'v'; break; case '\\': *cp++ = '\\'; *cp++ = '\\'; break; case '\"': *cp++ = '\\'; *cp++ = '\"'; break; default: if (!isprint(*istr)) { static char octs[] = "01234567"; int rem = ((unsigned char)*istr)%64; *cp++ = '\\'; *cp++ = octs[((unsigned char)*istr)/64]; /* to get, e.g. '\177' */ *cp++ = octs[rem/8]; *cp++ = octs[rem%8]; } else { *cp++ = *istr; } break; } istr++; } *cp++ = '"'; *cp = '\0'; free(istr0); return sp; } /* Given a pointer to a counted string (not necessarily * null-terminated), returns a pointer to a malloced string representing * the string as a FORTRAN string expression. For example, the string * "don't" would yield the FORTRAN string "'don''t'", and the string * "ab\ncd" would yield "'ab'//char(10)//'cd'". The common * interpretation of "\"-escaped characters is non-standard, so the * generated Fortran may require adjustment in compilers that don't * recognize "\" as anything special in a character context. */ char * fstrstr( const char *str, /* pointer to vector of characters */ size_t ilen) /* number of characters in istr */ { static char *ostr; char *cp, tstr[12]; int was_print = 0; /* true if last character was printable */ char *istr, *istr0; /* for null-terminated copy */ size_t ii; if(12*ilen != (size_t)(12*ilen)) { derror("too much character data!"); exit(9); } istr0 = istr = (char *) emalloc(ilen + 1); for(ii = 0; ii < ilen; ii++) { istr[ii] = str[ii]; } istr[ilen] = '\0'; if (*istr == '\0') { /* empty string input, not legal in FORTRAN */ ostr = (char*) emalloc(strlen("char(0)") + 1); strcpy(ostr, "char(0)"); free(istr0); return ostr; } ostr = cp = (char *) emalloc(12*ilen); *ostr = '\0'; if (isprint(*istr)) { /* handle first character in input */ *cp++ = '\''; switch (*istr) { case '\'': *cp++ = '\''; *cp++ = '\''; break; case '\\': *cp++ = '\\'; *cp++ = '\\'; break; default: *cp++ = *istr; break; } *cp = '\0'; was_print = 1; } else { sprintf(tstr, "char(%d)", (unsigned char)*istr); strcat(cp, tstr); cp += strlen(tstr); was_print = 0; } istr++; for(ii = 1; ii < ilen; ii++) { /* handle subsequent characters in input */ if (isprint(*istr)) { if (! was_print) { strcat(cp, "//'"); cp += 3; } switch (*istr) { case '\'': *cp++ = '\''; *cp++ = '\''; break; case '\\': *cp++ = '\\'; *cp++ = '\\'; break; default: *cp++ = *istr; break; } *cp = '\0'; was_print = 1; } else { if (was_print) { *cp++ = '\''; *cp = '\0'; } sprintf(tstr, "//char(%d)", (unsigned char)*istr); strcat(cp, tstr); cp += strlen(tstr); was_print = 0; } istr++; } if (was_print) *cp++ = '\''; *cp = '\0'; free(istr0); return ostr; } static void cl_netcdf(void) { int stat = miclose(ncid); check_err(stat); } static void cl_c(void) { cline(" stat = nc_close(ncid);"); cline(" check_err(stat,__LINE__,__FILE__);"); #ifndef vms cline(" return 0;"); #else cline(" return 1;"); #endif cline("}"); } /* Returns true if dimension used in at least one record variable, otherwise false. This is an inefficient algorithm, but we don't call it very often ... */ static int used_in_rec_var( int idim /* id of dimension */ ) { int ivar; for (ivar = 0; ivar < nvars; ivar++) { if (vars[ivar].ndims > 0 && vars[ivar].dims[0] == rec_dim) { int jdim; for (jdim = 0; jdim < vars[ivar].ndims; jdim++) { if (vars[ivar].dims[jdim] == idim) return 1; } } } return 0; } /* Return name for Fortran fill constant of specified type */ static char * f_fill_name( nc_type type ) { switch(type) { case NC_BYTE: return "NF_FILL_BYTE"; case NC_CHAR: return "NF_FILL_CHAR"; case NC_SHORT: return "NF_FILL_SHORT"; case NC_INT: return "NF_FILL_INT"; case NC_FLOAT: return "NF_FILL_FLOAT"; case NC_DOUBLE: return "NF_FILL_DOUBLE"; } derror("f_fill_name: bad type code"); return 0; } /* Generate Fortran for cleaning up and closing file */ static void cl_fortran(void) { int ivar; int idim; char stmnt[FORT_MAX_STMNT]; char s2[FORT_MAX_STMNT]; char*sp; int have_rec_var = 0; /* do we have any record variables? */ for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { have_rec_var = 1; break; } } if (have_rec_var) { fline(" "); fline("* Write record variables"); sprintf(stmnt, "call writerecs(ncid,"); /* generate parameter list for subroutine to write record vars */ for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; /* if a record variable, include id in parameter list */ if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(s2, "%s_id,", v->lname); strcat(stmnt, s2); } } sp = strrchr(stmnt, ','); if(sp != NULL) { *sp = '\0'; } strcat(stmnt, ")"); fline(stmnt); } fline(" "); fline("iret = nf_close(ncid)"); fline("call check_err(iret)"); fline("end"); fline(" "); if (have_rec_var) { sprintf(stmnt, "subroutine writerecs(ncid,"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(s2, "%s_id,", v->lname); strcat(stmnt, s2); } } sp = strrchr(stmnt, ','); if(sp != NULL) { *sp = '\0'; } strcat(stmnt, ")"); fline(stmnt); fline(" "); fline("* netCDF id"); fline("integer ncid"); fline("* variable ids"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(stmnt, "integer %s_id", v->lname); fline(stmnt); } } fline(" "); fline("include 'netcdf.inc'"); /* create necessary declarations */ fline("* error status return"); fline("integer iret"); /* generate integer/parameter declarations for all dimensions used in record variables, except record dimension. */ fline(" "); fline("* netCDF dimension sizes for dimensions used with record variables"); for (idim = 0; idim < ndims; idim++) { /* if used in a record variable and not record dimension */ if (used_in_rec_var(idim) && dims[idim].size != NC_UNLIMITED) { sprintf(stmnt, "integer %s_len", dims[idim].lname); fline(stmnt); sprintf(stmnt, "parameter (%s_len = %lu)", dims[idim].lname, (unsigned long) dims[idim].size); fline(stmnt); } } fline(" "); fline("* rank (number of dimensions) for each variable"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(stmnt, "integer %s_rank", v->lname); fline(stmnt); } } for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(stmnt, "parameter (%s_rank = %d)", v->lname, v->ndims); fline(stmnt); } } fline("* starts and counts for array sections of record variables"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { sprintf(stmnt, "integer %s_start(%s_rank), %s_count(%s_rank)", v->lname, v->lname, v->lname, v->lname); fline(stmnt); } } fline(" "); fline("* data variables"); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim) { char *sp; fline(" "); sprintf(stmnt, "integer %s_nr", v->lname); fline(stmnt); if (v->nrecs > 0) { sprintf(stmnt, "parameter (%s_nr = %lu)", v->lname, (unsigned long) v->nrecs); } else { sprintf(stmnt, "parameter (%s_nr = 1)", v->lname); } fline(stmnt); if (v->type != NC_CHAR) { sprintf(stmnt, "%s %s(", ncftype(v->type), v->lname); /* reverse dimensions for FORTRAN */ for (idim = v->ndims-1; idim >= 0; idim--) { if(v->dims[idim] == rec_dim) { sprintf(s2, "%s_nr, ", v->lname); } else { sprintf(s2, "%s_len, ", dims[v->dims[idim]].lname); } strcat(stmnt, s2); } sp = strrchr(stmnt, ','); if(sp != NULL) { *sp = '\0'; } strcat(stmnt, ")"); fline(stmnt); } } } fline(" "); /* Emit DATA statements after declarations, because f2c on Linux can't handle interspersing them */ for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; if (v->ndims > 0 && v->dims[0] == rec_dim && v->type != NC_CHAR) { if (v->has_data) { fline(v->data_stmnt); } else { /* generate data statement for FILL record */ size_t rec_len = 1; for (idim = 1; idim < v->ndims; idim++) { rec_len *= dims[v->dims[idim]].size; } sprintf(stmnt,"data %s /%lu * %s/", v->lname, (unsigned long) rec_len, f_fill_name(v->type)); fline(stmnt); } } } fline(" "); for (ivar = 0; ivar < nvars; ivar++) { struct vars *v = &vars[ivar]; /* if a record variable, declare starts and counts */ if (v->ndims > 0 && v->dims[0] == rec_dim) { if (!v->has_data) continue; sprintf(stmnt, "* store %s", v->name); fline(stmnt); for (idim = 0; idim < v->ndims; idim++) { sprintf(stmnt, "%s_start(%d) = 1", v->lname, idim+1); fline(stmnt); } for (idim = v->ndims-1; idim > 0; idim--) { sprintf(stmnt, "%s_count(%d) = %s_len", v->lname, v->ndims - idim, dims[v->dims[idim]].lname); fline(stmnt); } sprintf(stmnt, "%s_count(%d) = %s_nr", v->lname, v->ndims, v->lname); fline(stmnt); if (v->type != NC_CHAR) { sprintf(stmnt, "iret = nf_put_vara_%s(ncid, %s_id, %s_start, %s_count, %s)", nfftype(v->type), v->lname, v->lname, v->lname, v->lname); } else { sprintf(stmnt, "iret = nf_put_vara_%s(ncid, %s_id, %s_start, %s_count, %s)", nfftype(v->type), v->lname, v->lname, v->lname, v->data_stmnt); } fline(stmnt); fline("call check_err(iret)"); } } fline(" "); fline("end"); fline(" "); } fline("subroutine check_err(iret)"); fline("integer iret"); fline("include 'netcdf.inc'"); fline("if (iret .ne. NF_NOERR) then"); fline("print *, nf_strerror(iret)"); fline("stop"); fline("endif"); fline("end"); } /* invoke netcdf calls (or generate C or Fortran code) to create netcdf * from in-memory structure. */ void define_netcdf( char *netcdfname) { char *filename; /* output file name */ if (netcdf_name) { /* name given on command line */ filename = netcdf_name; } else { /* construct name from CDL name */ filename = (char *) emalloc(strlen(netcdfname) + 5); (void) strcpy(filename,netcdfname); (void) strcat(filename,".mnc"); } if (netcdf_flag) gen_netcdf(filename); /* create netcdf */ if (c_flag) /* create C code to create netcdf */ gen_c(filename); if (fortran_flag) /* create Fortran code to create netcdf */ gen_fortran(filename); if (!netcdf_name) { free(filename); } } void close_netcdf(void) { if (netcdf_flag) cl_netcdf(); /* close netcdf */ if (c_flag) /* create C code to close netcdf */ cl_c(); if (fortran_flag) /* create Fortran code to close netcdf */ cl_fortran(); } void check_err(int stat) { if (stat < 0) { fprintf(stderr, "ncgen: %s\n", nc_strerror(stat)); } } /* * For logging error conditions. */ #ifndef NO_STDARG void derror(const char *fmt, ...) #else /*VARARGS1*/ void derror(fmt, va_alist) const char *fmt ; /* error-message printf-style format */ va_dcl /* variable number of error args, if any */ #endif /* !NO_STDARG */ { va_list args ; if (lineno == 1) (void) fprintf(stderr,"%s: %s: ", progname, cdlname); else (void) fprintf(stderr,"%s: %s line %d: ", progname, cdlname, lineno); #ifndef NO_STDARG va_start(args ,fmt) ; #else va_start(args) ; #endif /* !NO_STDARG */ (void) vfprintf(stderr,fmt,args) ; va_end(args) ; (void) fputc('\n',stderr) ; (void) fflush(stderr); /* to ensure log files are current */ derror_count++; } void * emalloc ( /* check return from malloc */ size_t size) { void *p; p = (void *) malloc (size); if (p == 0) { derror ("out of memory\n"); exit(3); } return p; } void * ecalloc ( /* check return from calloc */ size_t size) { void *p; p = (void *) calloc (size, 1); if (p == 0) { derror ("out of memory\n"); exit(3); } return p; } void * erealloc ( /* check return from realloc */ void *ptr, size_t size) /* if 0, this is really a free */ { void *p; p = (void *) realloc (ptr, size); if (p == 0 && size != 0) { derror ("out of memory"); exit(3); } return p; } /* * For generated Fortran, change 'e' to 'd' in exponent of double precision * constants. */ void expe2d( char *cp) /* string containing double constant */ { char *expchar = strrchr(cp,'e'); if (expchar) { *expchar = 'd'; } } /* Returns non-zero if n is a power of 2, 0 otherwise */ int pow2( int n) { int m = n; int p = 1; while (m > 0) { m /= 2; p *= 2; } return p == 2*n; } /* * Grow an integer array as necessary. * * Assumption: nar never incremented by more than 1 from last call. * * Makes sure an array is within a factor of 2 of the size needed. * * Make sure *arpp points to enough space to hold nar integers. If not big * enough, malloc more space, copy over existing stuff, free old. When * called for first time, *arpp assumed to be uninitialized. */ void grow_iarray( int nar, /* array must be at least this big */ int **arpp) /* address of start of int array */ { if (nar == 0) { *arpp = (int *) emalloc(1 * sizeof(int)); return; } if (! pow2(nar)) /* return unless nar is a power of two */ return; *arpp = (int *) erealloc(*arpp, 2 * nar * sizeof(int)); } /* * Grow an array of variables as necessary. * * Assumption: nar never incremented by more than 1 from last call. * * Makes sure array is within a factor of 2 of the size needed. * * Make sure *arpp points to enough space to hold nar variables. If not big * enough, malloc more space, copy over existing stuff, free old. When * called for first time, *arpp assumed to be uninitialized. */ void grow_varray( int nar, /* array must be at least this big */ struct vars **arpp) /* address of start of var array */ { if (nar == 0) { *arpp = (struct vars *) emalloc(1 * sizeof(struct vars)); return; } if (! pow2(nar)) /* return unless nar is a power of two */ return; *arpp = (struct vars *) erealloc(*arpp, 2 * nar * sizeof(struct vars)); } /* * Grow an array of dimensions as necessary. * * Assumption: nar never incremented by more than 1 from last call. * * Makes sure array is within a factor of 2 of the size needed. * * Make sure *arpp points to enough space to hold nar dimensions. If not big * enough, malloc more space, copy over existing stuff, free old. When * called for first time, *arpp assumed to be uninitialized. */ void grow_darray( int nar, /* array must be at least this big */ struct dims **arpp) /* address of start of var array */ { if (nar == 0) { *arpp = (struct dims *) emalloc(1 * sizeof(struct dims)); return; } if (! pow2(nar)) /* return unless nar is a power of two */ return; *arpp = (struct dims *) erealloc(*arpp, 2 * nar * sizeof(struct dims)); } /* * Grow an array of attributes as necessary. * * Assumption: nar never incremented by more than 1 from last call. * * Makes sure array is within a factor of 2 of the size needed. * * Make sure *arpp points to enough space to hold nar attributes. If not big * enough, malloc more space, copy over existing stuff, free old. When * called for first time, *arpp assumed to be uninitialized. */ void grow_aarray( int nar, /* array must be at least this big */ struct atts **arpp) /* address of start of var array */ { if (nar == 0) { *arpp = (struct atts *) emalloc(1 * sizeof(struct atts)); return; } if (! pow2(nar)) /* return unless nar is a power of two */ return; *arpp = (struct atts *) erealloc(*arpp, 2 * nar * sizeof(struct atts)); } /* * Replace dashes and dots in name so it can be used in C and * Fortran without causing syntax errors. Here we just replace each "-" * in a name with "_dash_" and each "." with "_dot_", though any * similar replacement that doesn't clash with existing names would * work. */ extern char* decodify ( const char *name) { int count=0; /* number of minus signs in name */ char *newname; const char *cp = name; char *sp; while(*cp != '\0') { switch (*cp) { case '-': count += strlen("_dash_") - 1; break; case '.': count += strlen("_dot_") - 1; break; case '@': count += strlen("_at_") - 1; break; case '#': count += strlen("_hash_") - 1; break; case '[': count += strlen("_lbr_") - 1; break; case ']': count += strlen("_rbr_") - 1; break; default: break; } cp++; } newname = (char *) ecalloc(strlen(name) + count + 1); cp = name; sp = newname; while(*cp != '\0') { switch (*cp) { case '-': strcat(sp, "_dash_"); sp += strlen("_dash_"); break; case '.': strcat(sp, "_dot_"); sp += strlen("_dot_"); break; case '@': strcat(sp, "_at_"); sp += strlen("_at_"); break; case '#': strcat(sp, "_hash_"); sp += strlen("_hash_"); break; case '[': strcat(sp, "_lbr_"); sp += strlen("_lbr_"); break; case ']': strcat(sp, "_rbr_"); sp += strlen("_rbr_"); break; default: *sp++ = *cp; break; } cp++; } *sp = '\0'; return newname; } minc-tools-2.3.00+dfsg/progs/mincgen/mincgen.man10000644000175000000620000003116312574624760020576 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" $Header: /private-cvsroot/minc/progs/mincgen/mincgen.man1,v 1.3 2008-10-12 05:07:12 stever Exp $ .TH MINCGEN 1 "$Date: 2008-10-12 05:07:12 $" "" "MINC User's Guide" .SH NAME mincgen \- Generate a MINC file from a CDL file. .SH SYNOPSIS .HP mincgen .nh \%[-b] \%[-n] \%[-o \fIminc_filename\fP] \%\fIinput_file\fP .hy .ft .SH DESCRIPTION \fBmincgen\fP generates a MINC file. The input to \fBmincgen\fP is a description of a MINC file in a small language known as CDL (network Common Data form Language), described below. If no options are specified in invoking \fBmincgen\fP, it merely checks the syntax of the input CDL file, producing error messages for any violations of CDL syntax. Other options can be used to create the corresponding MINC file. .LP \fBmincgen\fP may be used with the companion program \fBmincdump\fP to perform some simple operations on MINC files. For example, to rename a dimension in a MINC file, use \fBmincdump\fP to get a CDL version of the MINC file, edit the CDL file to change the name of the dimensions, and use \fBmincgen\fP to generate the corresponding MINC file from the edited CDL file. .SH OPTIONS .IP "\fB-b\fP" Create a (binary) MINC file. If the \fB-o\fP option is absent, a default file name will be constructed from the MINC name (specified after the \fBnetcdf\fP or \fBhdf5\fP keyword in the input) by appending the `.mnc' extension. If a file already exists with the specified name, it will be overwritten. .IP "\fB-o\fP \fRminc_filename\fP" Name for the binary MINC file created. If this option is specified, it implies the "\fB-b\fP" option. (This option is necessary because MINC files cannot be written directly to standard output, since standard output is not seekable.) .SH EXAMPLES .LP Check the syntax of the CDL file `\fBfoo.cdl\fP': .RS .HP mincgen foo.cdl .RE .LP From the CDL file `\fBfoo.cdl\fP', generate an equivalent binary MINC file named `\fBx.mnc\fP': .RS .HP mincgen -o x.mnc foo.cdl .RE .LP .SH USAGE .SS "CDL Syntax Summary" .LP Below is an example of CDL syntax, describing a MINC file with several named dimensions (xspace, yspace, and zspace), variables (zspace, image), variable attributes (valid_range, signtype), and some data. CDL keywords are in boldface. (This example is intended to illustrate the syntax; a real CDL file would have a more complete set of attributes so that the data would be more completely self-describing.) .RS .nf \fBnetcdf\fP foo { // an example MINC specification in CDL \fBdimensions\fP: xspace = 8; yspace = 8; zspace = 5; \fBvariables\fP: \fBfloat\fP xspace; \fBfloat\fP yspace; \fBfloat\fP zspace(zspace); \fBshort\fP image(zspace,yspace,xspace); \fBdouble\fP image-min(zspace) \fBdouble\fP image-max(zspace) // variable attributes image:valid_range = 0,5; \fBdata\fP: image-min = -1,-1,-1,-1,-1; image-max = 1,1,1,1,1; image = 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 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,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,3,3,3,3, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5; zspace = 0,2,3.5,7,10; } .fi .RE .LP All CDL statements are terminated by a semicolon. Spaces, tabs, and newlines can be used freely for readability. Comments may follow the characters `//' on any line. .LP A CDL description consists of three optional parts: \fIdimensions\fP, \fIvariables\fP, and \fIdata\fP, beginning with the keyword .BR dimensions: , .BR variables: , and .BR data , respectively. The variable part may contain \fIvariable declarations\fP and \fIattribute assignments\fP. .LP A MINC \fIdimension\fP is used to define the shape of one or more of the multidimensional variables contained in the MINC file. A MINC dimension has a name, a size, and possibly several other attributes. .LP A \fIvariable\fP represents a multidimensional array of values of the same type. A variable has a name, a data type, and a shape described by its list of dimensions. Each variable may also have associated \fIattributes\fP (see below) as well as data values. The name, data type, and shape of a variable are specified by its declaration in the \fIvariable\fP section of a CDL description. A variable may have the same name as a dimension; by convention such a variable is one-dimensional and contains coordinates of the dimension it names. Dimensions need not have corresponding variables. .LP A netCDF \fIattribute\fP contains information about a netCDF variable or about the whole netCDF dataset. Attributes are used to specify such properties as units, special values, maximum and minimum valid values, scaling factors, offsets, and parameters. Attribute information is represented by single values or arrays of values. For example, "units" is an attribute represented by a character array such as "celsius". An attribute has an associated variable, a name, a data type, a length, and a value. In contrast to variables that are intended for data, attributes are intended for metadata (data about data). .LP In CDL, an attribute is designated by a variable and attribute name, separated by `:'. It is possible to assign \fIglobal\fP attributes not associated with any variable to the file as a whole by using `:' before the attribute name. The data type of an attribute in CDL is derived from the type of the value assigned to it. The length of an attribute is the number of data values assigned to it, or the number of characters in the character string assigned to it. Multiple values are assigned to non-character attributes by separating the values with commas. All values assigned to an attribute must be of the same type. .LP The names for CDL dimensions, variables, and attributes must begin with an alphabetic character or `_', and subsequent characters may be alphanumeric or `_' or `-'. .LP The optional \fIdata\fP section of a CDL specification is where variables may be initialized. The syntax of an initialization is simple: a variable name, an equals sign, and a comma-delimited list of constants (possibly separated by spaces, tabs and newlines) terminated with a semicolon. For multi-dimensional arrays, the last dimension varies fastest. Thus row-order rather than column order is used for matrices. If fewer values are supplied than are needed to fill a variable, it is extended with a type-dependent `fill value', which can be overridden by supplying a value for a distinguished variable attribute named `_FillValue'. The types of constants need not match the type declared for a variable; coercions are done to convert integers to floating point, for example. The constant `_' can be used to designate the fill value for a variable. .SS "Primitive Data Types" .LP .RS .nf \fBchar\fP characters \fBbyte\fP 8-bit data \fBshort\fP 16-bit signed integers \fBlong\fP 32-bit signed integers \fBint\fP (synonymous with \fBlong\fP) \fBfloat\fP IEEE single precision floating point (32 bits) \fBreal\fP (synonymous with \fBfloat\fP) \fBdouble\fP IEEE double precision floating point (64 bits) .fi .RE .LP Except for the added data-type \fBbyte\fP and the lack of \fBunsigned\fP, CDL supports the same primitive data types as C. The names for the primitive data types are reserved words in CDL, so the names of variables, dimensions, and attributes must not be type names. In declarations, type names may be specified in either upper or lower case. .LP Bytes differ from characters in that they are intended to hold a full eight bits of data, and the zero byte has no special significance, as it does for character data. .LP Shorts can hold values between -32768 and 32767. .LP Longs can hold values between -2147483648 and 2147483647. \fBint\fP and \fBinteger\fP are accepted as synonyms for \fBlong\fP in CDL declarations. Now that there are platforms with 64-bit representations for C longs, it may be better to use the \fBint\fP synonym to avoid confusion. .LP Floats can hold values between about -3.4+38 and 3.4+38. Their external representation is as 32-bit IEEE normalized single-precision floating point numbers. \fBreal\fP is accepted as a synonym for \fBfloat\fP in CDL declarations. .LP Doubles can hold values between about -1.7+308 and 1.7+308. Their external representation is as 64-bit IEEE standard normalized double-precision floating point numbers. .LP .SS "CDL Constants" .LP Constants assigned to attributes or variables may be of any of the basic MINC types. The syntax for constants is similar to C syntax, except that type suffixes must be appended to shorts and floats to distinguish them from longs and doubles. .LP A \fIbyte\fP constant is represented by a single character or multiple character escape sequence enclosed in single quotes. For example, .RS .nf 'a' // ASCII `a' '\\0' // a zero byte '\\n' // ASCII newline character '\\33' // ASCII escape character (33 octal) '\\x2b' // ASCII plus (2b hex) '\\377' // 377 octal = 255 decimal, non-ASCII .fi .RE .LP Character constants are enclosed in double quotes. A character array may be represented as a string enclosed in double quotes. The usual C string escape conventions are honored. For example .RS .nf "a" // ASCII `a' "Two\\nlines\\n" // a 10-character string with two embedded newlines "a bell:\\007" // a string containing an ASCII bell .fi .RE Note that the character array "a" would fit in a one-element variable, since no terminating NULL character is assumed. However, a zero byte in a character array is interpreted as the end of the significant characters by the \fBmincdump\fP program, following the C convention. Therefore, a NULL byte should not be embedded in a character string unless at the end: use the \fIbyte\fP data type instead for byte arrays that contain the zero byte. MINC and CDL have no string type, but only fixed-length character arrays, which may be multi-dimensional. .LP \fIshort\fP integer constants are intended for representing 16-bit signed quantities. The form of a \fIshort\fP constant is an integer constant with an `s' or `S' appended. If a \fIshort\fP constant begins with `0', it is interpreted as octal, except that if it begins with `0x', it is interpreted as a hexadecimal constant. For example: .RS .nf -2s // a short -2 0123s // octal 0x7ffs //hexadecimal .fi .RE .LP \fILong\fP integer constants are intended for representing 32-bit signed quantities. The form of a \fIlong\fP constant is an ordinary integer constant, although it is acceptable to append an optional `l' or `L'. If a \fIlong\fP constant begins with `0', it is interpreted as octal, except that if it begins with `0x', it is interpreted as a hexadecimal constant. Examples of valid \fIlong\fP constants include: .RS .nf -2 1234567890L 0123 // octal 0x7ff // hexadecimal .fi .RE .LP Floating point constants of type \fIfloat\fP are appropriate for representing floating point data with about seven significant digits of precision. The form of a \fIfloat\fP constant is the same as a C floating point constant with an `f' or `F' appended. For example the following are all acceptable \fIfloat\fP constants: .RS .nf -2.0f 3.14159265358979f // will be truncated to less precision 1.f .fi .RE .LP Floating point constants of type \fIdouble\fP are appropriate for representing floating point data with about sixteen significant digits of precision. The form of a \fIdouble\fP constant is the same as a C floating point constant. An optional `d' or `D' may be appended. For example the following are all acceptable \fIdouble\fP constants: .RS .nf -2.0 3.141592653589793 1.0e-20 1.d .fi .RE .SH AUTHOR Originally written by members of the Unidata Program at the University Corporation for Atmospheric Research. Modified by Bert Vincent (bert@bic.mni.mcgill.ca) for use with both netCDF and HDF5 files. .SH COPYRIGHTS Copyright \(co University Corporation for Atmospheric Research .SH "SEE ALSO" .LP .BR ncdump (1), .BR ncgen (1), .BR netcdf (3) .SH BUGS .LP The CDL syntax makes it easy to assign what looks like an array of variable-length strings to a variable, but the strings will simply be concatenated into a single array of characters, since MINC cannot represent an array of variable-length strings in one MINC variable. .LP MINC and CDL do not yet support a type corresponding to a 64-bit integer. minc-tools-2.3.00+dfsg/progs/mincgen/escapes.c0000644000175000000620000000410512574624760020163 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincgen/escapes.c,v 1.2 2008-01-12 19:08:15 stever Exp $ *********************************************************************/ #include #include #include "generic.h" #include "ncgen.h" #include "genlib.h" /* * "Expands" valid escape sequences in yystring (read by lex) into the * apropriate characters in termstring. For example, the two character * sequence "\t" in yystring would be converted into a single tab character * in termstring. On return, termstring is properly terminated. */ void expand_escapes( char *termstring, /* returned, with escapes expanded */ char *yytext, int yyleng) { char *s, *t, *endp; yytext[yyleng-1]='\0'; /* don't copy quotes */ /* expand "\" escapes, e.g. "\t" to tab character */ s = termstring; t = yytext+1; while(*t) { if (*t == '\\') { t++; switch (*t) { case 'a': *s++ = '\007'; t++; /* will use '\a' when STDC */ break; case 'b': *s++ = '\b'; t++; break; case 'f': *s++ = '\f'; t++; break; case 'n': *s++ = '\n'; t++; break; case 'r': *s++ = '\r'; t++; break; case 't': *s++ = '\t'; t++; break; case 'v': *s++ = '\v'; t++; break; case '\\': *s++ = '\\'; t++; break; case '?': *s++ = '\177'; t++; break; case '\'': *s++ = '\''; t++; break; case '\"': *s++ = '\"'; t++; break; case 'x': t++; /* now t points to one or more hex digits */ *s++ = (char) strtol(t, &endp, 16); t = endp; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* t now points to octal digits */ *s++ = (char) strtol(t, &endp, 8); t = endp; break; default: *s++ = *t++; break; } } else { *s++ = *t++; } } *s = '\0'; return; } minc-tools-2.3.00+dfsg/progs/mincgen/ncgen.h0000644000175000000620000000561212574624760017643 0ustar stevestaff#ifndef NC_NCGEN_H #define NC_NCGEN_H /********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincgen/ncgen.h,v 1.2 2007-02-02 18:49:36 baghdadi Exp $ *********************************************************************/ #define MAX_NC_ATTSIZE 500000 /* max size of attribute (for ncgen) */ #define MAXTRST 100000 /* max size of string value (for ncgen) */ /* True if string a equals string b*/ #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) #include "generic.h" extern int ncid; /* handle for netCDF */ extern int ndims; /* number of dimensions declared for netcdf */ extern int nvars; /* number of variables declared for netcdf */ extern int natts; /* number of attributes */ extern int nvdims; /* number of dimensions for variables */ extern int dimnum; /* dimension number index for variables */ extern int varnum; /* variable number index for attributes */ extern size_t valnum; /* number of values specified for variable */ extern int rec_dim; /* number of the unlimited dimension, if any */ extern size_t rec_len; /* number of elements for a record of data */ extern size_t var_len; /* variable length (product of dimensions) */ extern size_t var_size; /* size of each element of variable */ extern int is_hdf5; extern struct dims { size_t size; char *name; char *lname; /* with no "-" characters, for C and Fortran */ } *dims; /* table of dimensions */ extern struct vars { char *name; nc_type type; int ndims; int *dims; /* array of dimension ids */ union generic fill_value; /* set to value of _FillValue attribute */ int has_data; /* 1 if data specified, 0 otherwise */ size_t nrecs; /* for record variables, number of records * of data in CDL */ char *data_stmnt; /* for record variables, to avoid * two passes with -f option */ char *lname; /* with no "-" characters, for C and Fortran */ } *vars; /* table of variables */ extern struct atts { int var; /* number of variable for this attribute */ char *name; nc_type type; size_t len; void *val; char *lname; /* with no "-" characters, for C and Fortran */ } *atts; /* table of variable and global attributes */ typedef struct Symbol { /* symbol table entry */ char *name; struct Symbol *next; unsigned is_dim : 1; /* appears as netCDF dimension */ unsigned is_var : 1; /* appears as netCDF variable */ unsigned is_att : 1; /* appears as netCDF attribute */ int dnum; /* handle as a dimension */ int vnum; /* handle as a variable */ } *YYSTYPE1; #define YYSTYPE YYSTYPE1 extern int yylex(void); extern YYSTYPE lookup(char *sname); extern YYSTYPE install(char *sname); extern int yyerror(char *s); #endif /*!NC_NCGEN_H*/ minc-tools-2.3.00+dfsg/progs/mincgen/load.c0000644000175000000620000003012012574624760017453 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Id: load.c,v 1.3 2005-11-23 04:37:26 bert Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "generic.h" #include "ncgen.h" #include "genlib.h" extern int netcdf_flag; extern int c_flag; extern int fortran_flag; #define fpr (void) fprintf /* * Remove trailing zeros (after decimal point) but not trailing decimal * point from ss, a string representation of a floating-point number that * might include an exponent part. */ static void tztrim( char *ss /* returned string representing dd */ ) { char *cp, *ep; cp = ss; if (*cp == '-') cp++; while(isdigit((int)*cp) || *cp == '.') cp++; if (*--cp == '.') return; ep = cp+1; while (*cp == '0') cp--; cp++; if (cp == ep) return; while (*ep) *cp++ = *ep++; *cp = '\0'; return; } /* generate C to put netCDF record from in-memory data */ static void gen_load_c( void *rec_start ) { int idim; size_t ival; char *val_string; char *charvalp; short *shortvalp; int *intvalp; float *floatvalp; double *doublevalp; char stmnt[C_MAX_STMNT]; size_t stmnt_len; char s2[C_MAX_STMNT]; if (!vars[varnum].has_data) return; cline(""); sprintf(stmnt, " {\t\t\t/* store %s */", vars[varnum].name); cline(stmnt); if (vars[varnum].ndims > 0) { if (vars[varnum].dims[0] == rec_dim) { sprintf(stmnt, " static size_t %s_start[RANK_%s];", vars[varnum].lname, vars[varnum].lname); cline(stmnt); sprintf(stmnt, " static size_t %s_count[RANK_%s];", vars[varnum].lname, vars[varnum].lname); cline(stmnt); } /* load variable with data values using static initialization */ sprintf(stmnt, " static %s %s[] = {", ncctype(vars[varnum].type), vars[varnum].lname); stmnt_len = strlen(stmnt); switch (vars[varnum].type) { case NC_CHAR: val_string = cstrstr((char *) rec_start, var_len); sprintf(s2, "%s", val_string); strncat(stmnt, s2, C_MAX_STMNT - strlen(stmnt) ); free(val_string); break; default: switch (vars[varnum].type) { case NC_BYTE: charvalp = (char *) rec_start; break; case NC_SHORT: shortvalp = (short *) rec_start; break; case NC_INT: intvalp = (int *) rec_start; break; case NC_FLOAT: floatvalp = (float *) rec_start; break; case NC_DOUBLE: doublevalp = (double *) rec_start; break; } for (ival = 0; ival < var_len-1; ival++) { switch (vars[varnum].type) { case NC_BYTE: sprintf(s2, "%d, ", *charvalp++); break; case NC_SHORT: sprintf(s2, "%d, ", *shortvalp++); break; case NC_INT: sprintf(s2, "%ld, ", (long)*intvalp++); break; case NC_FLOAT: sprintf(s2, "%.8g, ", *floatvalp++); break; case NC_DOUBLE: sprintf(s2, "%#.16g", *doublevalp++); tztrim(s2); strcat(s2, ", "); break; } stmnt_len += strlen(s2); if (stmnt_len < C_MAX_STMNT) strcat(stmnt, s2); else { cline(stmnt); strcpy(stmnt,s2); stmnt_len = strlen(stmnt); } } for (;ival < var_len; ival++) { switch (vars[varnum].type) { case NC_BYTE: sprintf(s2, "%d", *charvalp); break; case NC_SHORT: sprintf(s2, "%d", *shortvalp); break; case NC_INT: sprintf(s2, "%ld", (long)*intvalp); break; case NC_FLOAT: sprintf(s2, "%.8g", *floatvalp); break; case NC_DOUBLE: sprintf(s2, "%#.16g", *doublevalp++); tztrim(s2); break; } stmnt_len += strlen(s2); if (stmnt_len < C_MAX_STMNT) strcat(stmnt, s2); else { cline(stmnt); strcpy(stmnt,s2); stmnt_len = strlen(stmnt); } } break; } strcat(stmnt,"};"); cline(stmnt); if (vars[varnum].dims[0] == rec_dim) { sprintf(stmnt, " %s_len = %lu; /* number of records of %s data */", dims[rec_dim].lname, (unsigned long)vars[varnum].nrecs, /* number of recs for this variable */ vars[varnum].name); cline(stmnt); for (idim = 0; idim < vars[varnum].ndims; idim++) { sprintf(stmnt, " %s_start[%d] = 0;", vars[varnum].lname, idim); cline(stmnt); } for (idim = 0; idim < vars[varnum].ndims; idim++) { sprintf(stmnt, " %s_count[%d] = %s_len;", vars[varnum].lname, idim, dims[vars[varnum].dims[idim]].lname); cline(stmnt); } } if (vars[varnum].dims[0] == rec_dim) { sprintf(stmnt, " stat = nc_put_vara_%s(ncid, %s_id, %s_start, %s_count, %s);", ncstype(vars[varnum].type), vars[varnum].lname, vars[varnum].lname, vars[varnum].lname, vars[varnum].lname); } else { /* non-record variables */ sprintf(stmnt, " stat = nc_put_var_%s(ncid, %s_id, %s);", ncstype(vars[varnum].type), vars[varnum].lname, vars[varnum].lname); } cline(stmnt); } else { /* scalar variables */ /* load variable with data values using static initialization */ sprintf(stmnt, " static %s %s = ", ncctype(vars[varnum].type), vars[varnum].lname); switch (vars[varnum].type) { case NC_CHAR: val_string = cstrstr((char *) rec_start, var_len); val_string[strlen(val_string)-1] = '\0'; sprintf(s2, "'%s'", &val_string[1]); free(val_string); break; case NC_BYTE: charvalp = (char *) rec_start; sprintf(s2, "%d", *charvalp); break; case NC_SHORT: shortvalp = (short *) rec_start; sprintf(s2, "%d", *shortvalp); break; case NC_INT: intvalp = (int *) rec_start; sprintf(s2, "%ld", (long)*intvalp); break; case NC_FLOAT: floatvalp = (float *) rec_start; sprintf(s2, "%.8g", *floatvalp); break; case NC_DOUBLE: doublevalp = (double *) rec_start; sprintf(s2, "%#.16g", *doublevalp++); tztrim(s2); break; } strncat(stmnt, s2, C_MAX_STMNT - strlen(stmnt) ); strcat(stmnt,";"); cline(stmnt); sprintf(stmnt, " stat = nc_put_var_%s(ncid, %s_id, &%s);", ncstype(vars[varnum].type), vars[varnum].lname, vars[varnum].lname); cline(stmnt); } cline(" check_err(stat,__LINE__,__FILE__);"); cline(" }"); } /* * Add to a partial Fortran statement, checking if it's too long. If it is too * long, output the first part of it as a single statement with continuation * characters and start a new (probably invalid) statement with the remainder. * This will cause a Fortran compiler error, but at least all the information * will be available. */ static void fstrcat( char *s, /* source string of stement being built */ char *t, /* string to be appended to source */ size_t *slenp /* pointer to length of source string */ ) { *slenp += strlen(t); if (*slenp >= FORT_MAX_STMNT) { derror("FORTRAN statement too long: %s",s); fline(s); strcpy(s, t); *slenp = strlen(s); } else { strcat(s, t); } } /* * Create Fortran data statement to initialize numeric variable with * values. */ static void f_var_init( int varnum, /* which variable */ void *rec_start /* start of data */ ) { char *val_string; char *charvalp; short *shortvalp; int *intvalp; float *floatvalp; double *doublevalp; char stmnt[FORT_MAX_STMNT]; size_t stmnt_len; char s2[FORT_MAX_STMNT]; size_t ival; /* load variable with data values */ sprintf(stmnt, "data %s /",vars[varnum].lname); stmnt_len = strlen(stmnt); switch (vars[varnum].type) { case NC_BYTE: charvalp = (char *) rec_start; for (ival = 0; ival < var_len-1; ival++) { val_string = fstring(NC_BYTE,(void *)charvalp++,0); sprintf(s2, "%s, ", val_string); fstrcat(stmnt, s2, &stmnt_len); free(val_string); } val_string = fstring(NC_BYTE,(void *)charvalp++,0); fstrcat(stmnt, val_string, &stmnt_len); free(val_string); break; case NC_SHORT: shortvalp = (short *) rec_start; for (ival = 0; ival < var_len-1; ival++) { sprintf(s2, "%d, ", *shortvalp++); fstrcat(stmnt, s2, &stmnt_len); } sprintf(s2, "%d", *shortvalp); fstrcat(stmnt, s2, &stmnt_len); break; case NC_INT: intvalp = (int *) rec_start; for (ival = 0; ival < var_len-1; ival++) { sprintf(s2, "%ld, ", (long)*intvalp++); fstrcat(stmnt, s2, &stmnt_len); } sprintf(s2, "%ld", (long)*intvalp); fstrcat(stmnt, s2, &stmnt_len); break; case NC_FLOAT: floatvalp = (float *) rec_start; for (ival = 0; ival < var_len-1; ival++) { sprintf(s2, "%.8g, ", *floatvalp++); fstrcat(stmnt, s2, &stmnt_len); } sprintf(s2, "%.8g", *floatvalp); fstrcat(stmnt, s2, &stmnt_len); break; case NC_DOUBLE: doublevalp = (double *) rec_start; for (ival = 0; ival < var_len-1; ival++) { sprintf(s2, "%#.16g", *doublevalp++); tztrim(s2); expe2d(s2); /* change 'e' to 'd' in exponent */ fstrcat(s2, ", ", &stmnt_len); fstrcat(stmnt, s2, &stmnt_len); } sprintf(s2, "%#.16g", *doublevalp++); tztrim(s2); expe2d(s2); fstrcat(stmnt, s2, &stmnt_len); break; default: derror("fstrstr: bad type"); break; } fstrcat(stmnt, "/", &stmnt_len); /* For record variables, store data statement for later use; otherwise, just print it. */ if (vars[varnum].ndims > 0 && vars[varnum].dims[0] == rec_dim) { char *dup_stmnt = emalloc(strlen(stmnt)+1); strcpy(dup_stmnt, stmnt); /* ULTRIX missing strdup */ vars[varnum].data_stmnt = dup_stmnt; } else { fline(stmnt); } } /* make Fortran to put record */ static void gen_load_fortran( void *rec_start ) { char stmnt[FORT_MAX_STMNT]; struct vars *v = &vars[varnum]; if (!v->has_data) return; if (v->ndims == 0 || v->dims[0] != rec_dim) { sprintf(stmnt, "* store %s", v->name); fline(stmnt); } /* generate code to initialize variable with values found in CDL input */ if (v->type != NC_CHAR) { f_var_init(varnum, rec_start); } else { v->data_stmnt = fstrstr(rec_start, valnum); } if (v->ndims >0 && v->dims[0] == rec_dim) { return; } if (v->type != NC_CHAR) { sprintf(stmnt, "iret = nf_put_var_%s(ncid, %s_id, %s)", nfftype(v->type), v->lname, v->lname); } else { char *char_expr = fstrstr(rec_start, valnum); sprintf(stmnt, "iret = nf_put_var_%s(ncid, %s_id, %s)", nfftype(v->type), v->lname, char_expr); free(char_expr); } fline(stmnt); fline("call check_err(iret)"); } /* invoke netcdf calls (or generate C or Fortran code) to load netcdf variable * from in-memory data. Assumes following global variables set from yacc * parser: * int varnum - number of variable to be loaded. * struct vars[varnum] - structure containing info on variable, specifically * name, type, ndims, dims, fill_value, has_data * int rec_dim - id of record dimension, or -1 if none * struct dims[] - structure containing name and size of dimensions. */ int put_variable( void *rec_start /* points to data to be loaded */ ) { if (netcdf_flag) load_netcdf(rec_start); /* put variable values */ if (c_flag) /* create C code to put values */ gen_load_c(rec_start); if (fortran_flag) /* create Fortran code to put values */ gen_load_fortran(rec_start); return 0; } /* write out variable's data from in-memory structure */ void load_netcdf( void *rec_start ) { int idim; int stat; long start[NC_MAX_VAR_DIMS]; long count[NC_MAX_VAR_DIMS]; /* load values into variable */ if (vars[varnum].ndims > 0) { /* initialize start to upper left corner (0,0,0,...) */ start[0] = 0; if (vars[varnum].dims[0] == rec_dim) { count[0] = vars[varnum].nrecs; } else { count[0] = dims[vars[varnum].dims[0]].size; } } for (idim = 1; idim < vars[varnum].ndims; idim++) { start[idim] = 0; count[idim] = dims[vars[varnum].dims[idim]].size; } stat = ncvarput(ncid, varnum, start, count, rec_start); check_err(stat); } minc-tools-2.3.00+dfsg/progs/mincgen/init.c0000644000175000000620000000323612574624760017507 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincgen/init.c,v 1.2 2006-05-19 00:35:58 bert Exp $ *********************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include "generic.h" #include "ncgen.h" #include "genlib.h" extern int netcdf_flag; extern int c_flag; extern int fortran_flag; struct dims *dims; /* table of netcdf dimensions */ int ncid; /* handle for netCDF */ int ndims; /* number of dimensions declared for netcdf */ int nvars; /* number of variables declared for netcdf */ int natts; /* number of attributes */ int nvdims; /* number of dimensions for variables */ int dimnum; /* dimension number index for variables */ int varnum; /* variable number index for attributes */ size_t valnum; /* value number index for attributes */ int rec_dim; /* number of the unlimited dimension, if any */ size_t var_len; /* variable length (product of dimensions) */ size_t rec_len; /* number of elements for a record of data */ size_t var_size; /* size of each element of variable */ struct vars *vars; /* a malloc'ed list */ struct atts *atts; /* table of variable and global attributes */ int is_hdf5; /* TRUE if HDF5 file */ void init_netcdf(int flag) { /* initialize global counts, flags */ clearout(); /* reset symbol table to empty */ ndims = 0; nvars = 0; rec_dim = -1; /* means no unlimited dimension (yet) */ is_hdf5 = flag; } minc-tools-2.3.00+dfsg/progs/mincgen/genlib.h0000644000175000000620000000556012574624760020013 0ustar stevestaff#ifndef NC_GENLIB_H #define NC_GENLIB_H /********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /private-cvsroot/minc/progs/mincgen/genlib.h,v 1.1 2004-06-15 20:14:40 bert Exp $ *********************************************************************/ #include #include extern const char *progname; /* for error messages */ extern const char *cdlname; /* for error messages */ #define FORT_MAX_LINES 20 /* max lines in FORTRAN statement */ #define FORT_MAX_STMNT 66*FORT_MAX_LINES /* max chars in FORTRAN statement */ #define C_MAX_STMNT FORT_MAX_STMNT /* until we fix to break up C lines */ #ifdef __cplusplus extern "C" { #endif extern void cline ( const char* stmnt ); extern void fline ( const char* stmnt ); extern char* nctype ( nc_type type ); extern char* ncctype ( nc_type type ); extern char* ncstype ( nc_type type ); extern char* ncatype ( nc_type type ); extern char* nfstype ( nc_type type ); extern char* nfftype ( nc_type type ); extern char* fstring ( nc_type type, void* valp, int num ); extern char* cstrstr ( const char* valp, size_t len ); extern char* fstrstr ( const char* str, size_t ilen ); extern size_t nctypesize( nc_type type ); extern void derror ( const char *fmt, ... ); extern void check_err ( int status ); extern void *emalloc ( size_t size ); extern void *ecalloc ( size_t size ); extern void *erealloc ( void *ptr, size_t size ); extern void expe2d ( char *ptr ); extern void grow_iarray ( int narray, int **array ); extern void grow_varray ( int narray, struct vars **array ); extern void grow_darray ( int narray, struct dims **array ); extern void grow_aarray ( int narray, struct atts **array ); extern char* decodify (const char *name); extern int put_variable ( void* rec_start ); /* initializes netcdf counts (e.g. nvars), defined in init.c */ extern void init_netcdf ( int ); /* generates all define mode stuff, defined in genlib.c */ extern void define_netcdf(char *netcdfname); /* generates variable puts, defined in load.c */ extern void load_netcdf ( void* rec_start ); /* generates close, defined in close.c */ extern void close_netcdf ( void ); /* defined in escapes.c */ extern void expand_escapes ( char* termstring, char* yytext, int yyleng ); /* to get fill value for various types, defined in getfill.c */ extern void nc_getfill ( nc_type type, union generic* gval ); /* to put fill value for various types, defined in getfill.c */ extern void nc_putfill ( nc_type type, void* val, union generic* gval ); /* fills a generic array with a value, defined in getfill.c */ extern void nc_fill ( nc_type type, size_t num, void* datp, union generic fill_val ); /* reset symbol table to empty, defined in ncgen.y */ extern void clearout(void); #ifdef __cplusplus } #endif #endif /*!NC_GENLIB_H*/ minc-tools-2.3.00+dfsg/progs/mincgen/getfill.c0000644000175000000620000000527612574624760020200 0ustar stevestaff/********************************************************************* * Copyright 1993, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header: /static-cvsroot/minc/progs/mincgen/getfill.c,v 1.2 2008-01-12 19:08:15 stever Exp $ *********************************************************************/ #include "netcdf.h" #include "generic.h" #include "ncgen.h" #include "genlib.h" /* * Given netCDF type, return a default fill_value appropriate for * that type. */ void nc_getfill( nc_type type, union generic *gval) { switch(type) { case NC_CHAR: gval->charv = NC_FILL_CHAR; return; case NC_BYTE: gval->charv = NC_FILL_BYTE; return; case NC_SHORT: gval->shortv = NC_FILL_SHORT; return; case NC_INT: gval->intv = NC_FILL_INT; return; case NC_FLOAT: gval->floatv = NC_FILL_FLOAT; return; case NC_DOUBLE: gval->doublev = NC_FILL_DOUBLE; return; default: derror("nc_getfill: unrecognized type"); } } void nc_fill( nc_type type, /* netcdf type code */ size_t num, /* number of values to fill */ void *datp, /* where to start filling */ union generic fill_val) /* value to use */ { char *char_valp; /* pointers used to accumulate data values */ short *short_valp; int *long_valp; float *float_valp; double *double_valp; switch (type) { case NC_CHAR: case NC_BYTE: char_valp = (char *) datp; break; case NC_SHORT: short_valp = (short *) datp; break; case NC_INT: long_valp = (int *) datp; break; case NC_FLOAT: float_valp = (float *) datp; break; case NC_DOUBLE: double_valp = (double *) datp; break; } while (num--) { switch (type) { case NC_CHAR: case NC_BYTE: *char_valp++ = fill_val.charv; break; case NC_SHORT: *short_valp++ = fill_val.shortv; break; case NC_INT: *long_valp++ = fill_val.intv; break; case NC_FLOAT: *float_valp++ = fill_val.floatv; break; case NC_DOUBLE: *double_valp++ = fill_val.doublev; break; } } } /* * Given netCDF type, put a value of that type into a fill_value */ void nc_putfill( nc_type type, void *val, /* value of type to be put */ union generic *gval) /* where the value is to be put */ { switch(type) { case NC_CHAR: case NC_BYTE: gval->charv = *(char *)val; return; case NC_SHORT: gval->shortv = *(short *)val; return; case NC_INT: gval->intv = *(int *)val; return; case NC_FLOAT: gval->floatv = *(float *)val; return; case NC_DOUBLE: gval->doublev = *(double *)val; return; default: derror("nc_putfill: unrecognized type"); } } minc-tools-2.3.00+dfsg/progs/coordinates/0002755000175000000620000000000012574624760017270 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/coordinates/worldtovoxel.c0000644000175000000620000001036212574624760022204 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : worldtovoxel @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to convert world coordinates to voxel coordinates @METHOD : @GLOBALS : @CALLS : @CREATED : June 13, 1994 (Peter Neelin) @MODIFIED : * $Log: worldtovoxel.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:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.6 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2004/02/02 18:27:51 bert * Call ParseArgv() so that version information can be output * * Revision 6.3 2001/04/24 13:38:41 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.2 2001/04/17 18:40:16 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:17 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:08 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:07 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:38 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:32:23 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:36 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:36:31 neelin * Pre-release * * Revision 1.2 94/09/26 10:05:26 neelin * Changed vx,vy,vz to v0, v1, v2. * * Revision 1.1 94/06/13 10:22:04 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Function to print to stderr */ void print_to_stderr(char *string) { (void) fprintf(stderr, "%s", string); return; } /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { VIO_Volume volume; volume_input_struct input_info; char *filename; double v0, v1, v2, wx, wy, wz; static char *dim_names[] = {ANY_SPATIAL_DIMENSION, ANY_SPATIAL_DIMENSION, ANY_SPATIAL_DIMENSION}; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || argc != 5) { (void) fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } filename = argv[1]; wx = atof(argv[2]); wy = atof(argv[3]); wz = atof(argv[4]); /* Open the image file */ set_print_function(print_to_stderr); if (start_volume_input(filename, 3, dim_names, MI_ORIGINAL_TYPE, TRUE, 0.0, 0.0, TRUE, &volume, NULL, &input_info) != VIO_OK) { (void) fprintf(stderr, "Error opening file %s for input.\n", filename); exit(EXIT_FAILURE); } /* Convert the voxel to world coordinates */ convert_3D_world_to_voxel(volume, wx, wy, wz, &v0, &v1, &v2); /* Write out the result */ (void) printf("%.20g %.20g %.20g\n", v0, v1, v2); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/coordinates/voxeltoworld.man10000644000175000000620000000156512574624760022623 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH VOXELTOWORLD 1 "$Date: 2004-05-20 21:52:07 $" "" "MINC User's Guide" .SH NAME voxeltoworld \- convert voxel coordinates to world coordinates worldtovoxel \- convert world coordinates to voxel coordinates .SH SYNOPSIS .B voxeltoworld filename v1 v2 v3 .B worldtovoxel filename x y z .SH DESCRIPTION Transform coordinates of a point between voxel and world coordinate systems. The voxel coordinate system is aligned with the sampling grid of the image volume. .SH OPTIONS .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES To get the start positions of all three spatial coordinate axes in world coordinates: .IP voxeltoworld file.mnc 0 0 0 .SH AUTHOR Peter Neelin .SH COPYRIGHT Copyright \(co 1993 by Peter Neelin minc-tools-2.3.00+dfsg/progs/coordinates/voxeltoworld.c0000644000175000000620000001052612574624760022206 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : voxeltoworld @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to convert voxel coordinates to world coordinates @METHOD : @GLOBALS : @CALLS : @CREATED : June 13, 1994 (Peter Neelin) @MODIFIED : * $Log: voxeltoworld.c,v $ * Revision 6.9 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.8 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.7 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.6 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.5 2004/04/27 15:38:47 bert * Added milog_init() * * Revision 6.4 2004/02/02 18:27:51 bert * Call ParseArgv() so that version information can be output * * Revision 6.3 2001/04/24 13:38:41 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.2 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.1 1999/10/19 14:45:16 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:08 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:07 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:38 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:32:23 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:35 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:36:30 neelin * Pre-release * * Revision 1.2 94/09/26 10:05:15 neelin * Changed vx,vy,vz to v0, v1, v2. * * Revision 1.1 94/06/13 10:21:47 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Function to print to stderr */ void print_to_stderr(char *string) { (void) fprintf(stderr, "%s", string); return; } /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { VIO_Volume volume; volume_input_struct input_info; char *filename; double v0, v1, v2, wx, wy, wz; static char *dim_names[] = {ANY_SPATIAL_DIMENSION, ANY_SPATIAL_DIMENSION, ANY_SPATIAL_DIMENSION}; milog_init(argv[0]); /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || argc != 5) { (void) fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } filename = argv[1]; v0 = atof(argv[2]); v1 = atof(argv[3]); v2 = atof(argv[4]); /* Open the image file */ set_print_function(print_to_stderr); if (start_volume_input(filename, 3, dim_names, MI_ORIGINAL_TYPE, TRUE, 0.0, 0.0, TRUE, &volume, NULL, &input_info) != VIO_OK) { (void) fprintf(stderr, "Error opening file %s for input.\n", filename); exit(EXIT_FAILURE); } /* Convert the voxel to world coordinates */ convert_3D_voxel_to_world(volume, v0, v1, v2, &wx, &wy, &wz); /* Write out the result */ (void) printf("%.20g %.20g %.20g\n", wx, wy, wz); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/mincresample/0002755000175000000620000000000012574624760017435 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincresample/mincresample.man10000644000175000000620000003205512574624760022675 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincresample/mincresample.man1,v 6.4 2005-07-13 21:34:25 bert Exp $ .\" .TH MINCRESAMPLE 1 "$Date: 2005-07-13 21:34:25 $" "" "MINC User's Guide" .SH NAME mincresample - resamples a minc file along new spatial dimensions .SH SYNOPSIS .B mincresample [] .SH DESCRIPTION \fIMincresample\fR will resample a minc file along new spatial dimensions with new voxel positions. Each volume in the input file (given by the spatial dimensions xspace, yspace and zspace) is resampled according to the command-line options. Non-spatial dimensions are preserved in their original order, but spatial dimensions can be re-ordered to give transverse, sagittal or coronal images. The new voxel values are calculated using tri-linear, tri-cubic or nearest-neighbour interpolation. .SH WORLD COORDINATES World coordinates refer to millimetric coordinates relative to some physical origin (either the scanner or some anatomical structure). Voxel coordinates are simply the indices into the image volume of a given voxel. In order to specify appropriate resampling options, it is necessary to understand how MINC coordinate conversions work. Each dimension of a MINC image volume is specified by name - the spatial dimensions are xspace, yspace and zspace. The convention is that positive xspace coordinates run from the patient's left side to right side, positive yspace coordinates run from patient posterior to anterior and positive zspace coordinates run from inferior to superior. For each of these spatial dimensions, the world coordinate conversion is specified by a pair of attributes: step and start. The xspace world coordinate, for example is calculated using x = v*step + start, where x is the x world coordinate and v is the voxel count (starting at zero). Thus the magnitude of the step attribute specifies the distance between voxels and the sign of the step attribute specifies the orientation of the axis. There is a further twist: MINC files are allowed to have non-orthogonal axes with the dimensions not perfectly aligned with the named axis. There can be a direction_cosine attribute that gives the true orientation of the axis. For example, normally the xspace dimension should line up with the world x axis, ie. direction cosine = (1,0,0); however, it is possible to have a direction cosine of (0.9, 0.43589, 0). These attributes (step, start and direction_cosines) provide a conversion from voxel coordinates to world coordinates. Combined with a number of elements or samples along an axis, they provide a complete description of where the output sampling should be. However, when we are resampling data, we are frequently interested in a change of world coordinates: from an MRI scanner's coordinate system to a PET scanner's coordinate system, for example, or from a volume in its acquisition space to coordinates in a standardized space. This change of world coordinates can be specified through the use of a transformation (.xfm) file. Thus, in general, the resampling involves three transformations: from the input file's voxel coordinates to its world coordinates (specified by the input file), from the input world coordinates to the output world coordinates (specified by the transformation file), and from the output file's world coordinates to its voxel coordinates (specified by command-line options). In general, direction cosines are rarely used - axis re-orientation is specified by a change of world coordinates (the transformation file). As well, resampling positions (output world to voxel conversion) are often specified relative to a model file (ie. resample this file so that it looks like that file). Although there are many options for a complete specification of the transformation, one does not usually need to specify more than a few of them. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create MINC 2.0 format output files. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB-verbose\fR Print out progress information for each slice computed (default). .TP \fB\-quiet\fR Do not print out progress information. .SH Resampling specification Options that give the output sampling (all of the following except \fB\-transformation\fR) are parsed in the order that they appear on the command line. Thus a command with \fB\-like file.mnc \-znelements 34 \-zstep 2\fR will give a sampling like that in file in \fIfile.mnc\fR but with 34 samples at 2 mm along the \fIzspace\fR axis. The default sampling is taken from the input file, transformed according to any transformation. .TP \fB\-transformation\fR\ \fIfile.xfm\fR Specify a file giving the world coordinate transformation (default is the identity transformation). .TP \fB\-invert_transformation\fR Invert the transformation before using it. .TP \fB\-noinvert_transformation\fR Do no invert the transformation (default). .TP \fB\-tfm_input_sampling\fR Transform the input sampling (using the transform specified by \fB\-transformation\fR) along with the data and use this as the default sampling (default). .TP \fB\-use_input_sampling\fR Use the input sampling as the default sampling, as is, without transformation, even though the data is being transformed (old behaviour). .TP \fB\-like\fR\ \fIfile.mnc\fR Specify a model file that gives the output world to voxel transformation and number of elements (ie. transform this file so that it looks like that one). .TP \fB\-standard_sampling\fR Set the sampling to standard values (start = 0, step = 1, direction cosines point along appropriate axes). .TP \fB\-spacetype\fR\ \fIstring\fR Set the name of the output space (usually \fBnative____\fR or \fBtalairach_\fR). .TP \fB\-talairach\fR Set the name of the output space to talairach_. .TP \fB\-units\fR\ \fIstring\fR Set the units of the output space. .TP \fB\-origin\fR\ \fIox\ oy\ oz\fR Specify the coordinate of the first voxel. This is not the same as the start value if the direction cosines are non-standard. As well, the start is not just a perpendicular projection of the origin onto the axis, it is a parallel projection (as in a multi-dimensional parallelogram projection). The conversion is handled properly by this option. .TP \fB\-nelements\fR\ \fInx\ ny\ nz\fR Number of elements along each of the world dimensions. .TP \fB\-xnelements\fR\ \fInx\fR Number of elements along the xspace dimension. .TP \fB\-ynelements\fR\ \fIny\fR Number of elements along the yspace dimension. .TP \fB\-znelements\fR\ \fInz\fR Number of elements along the zspace dimension. .TP \fB\-step\fR\ \fIxstep\ ystep\ zstep\fR Step between voxels along each of the world dimensions. .TP \fB\-xstep\fR\ \fIxstep\fR Step between voxels along the xspace dimension. .TP \fB\-ystep\fR\ \fIystep\fR Step between voxels along the yspace dimension. .TP \fB\-zstep\fR\ \fIzstep\fR Step between voxels along the zspace dimension. .TP \fB\-start\fR\ \fIxstart\ ystart\ zstart\fR Position of centre of first voxel along each of the world dimensions. .TP \fB\-xstart\fR\ \fIxstart\fR Position of centre of first voxel along the xspace dimension. .TP \fB\-ystart\fR\ \fIystart\fR Position of centre of first voxel along the yspace dimension. .TP \fB\-zstart\fR\ \fIzstart\fR Position of centre of first voxel along the zspace dimension. .TP \fB\-dircos\fR\ \fIx1\ x2\ x3\ y1\ y2\ y3\ z1\ z2\ z3\fR Direction cosines for each of the world axes. .TP \fB\-xdircos\fR\ \fIx1\ x2\ x3\fR Direction cosines for the xspace dimension. .TP \fB\-ydircos\fR\ \fIy1\ y2\ y3\fR Direction cosines for the yspace dimension. .TP \fB\-zdircos\fR\ \fIz1\ z2\ z3\fR Direction cosines for the zspace dimension. .SH Dimension ordering The default is to preserve the original dimension order. .TP \fB\-transverse\fR Write out transverse slices. .TP \fB\-sagittal\fR Write out sagittal slices. .TP \fB\-coronal\fR Write out coronal slices. .SH Output data type and range The default for type, sign and valid range is to use those of the input file. If type is specified, then both sign and valid range are set to the default for that type. If sign is specified, then valid range is set to the default for the type and sign. .TP \fB\-byte\fR Store output voxels in 8-bit integer format. .TP \fB\-short\fR Store output voxels in 16-bit integer format. .TP \fB\-int\fR Store output voxels in 32-bit integer format. .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB-float\fR Store output voxels in 32-bit floating point format. .TP \fB-double\fR Store output voxels in 64-bit floating point format. .TP \fB\-signed\fR Write out values as signed integers (default for short and long). Ignored for floating point types. .TP \fB\-unsigned\fR Write out values as unsigned integers (default for byte). Ignored for floating point types. .TP \fB\-range\fR \fImin max\fR specifies the valid range of output voxel values. Default is the full range for the type and sign. This option is ignored for floating point values. .TP \fB\-keep_real_range\fR Preserve the real minimum and maximum from the input volume, so that values are scaled in the same way on output. This is particularly useful for resampling label volumes where interpolating intensity values does not make sense. .TP \fB\-nokeep_real_range\fR Recompute the real minimum and maximum for each output slice. This is the default. .SH Handling of undefined (invalid) voxel values .TP \fB\-fill\fR Output voxels that fall outside of the input volume have undefined values. When the \fB-fill\fR option is used, these voxels are given a value that is outside of the valid range (less than the valid minimum, if the type, sign and valid range permit) so that they can be detected by other software. The values of these voxels are not included in the \fIimage-max\fR and \fIimage-min\fR variables. .TP \fB\-nofill\fR Use a real/physical value (not voxel value) of zero for points outside of the input volume. These points are included in the calculation of the \fIimage-max\fR and \fIimage-min\fR variables. This is the default. .TP \fB\-fillvalue\fR\ \fIfillvalue\fR Specifies a real/physical value (not voxel value) for points outside of the input volume. The points are not included in the calculation of the \fIimage-max\fR and \fIimage-min\fR variables. .SH Interpolation options .TP \fB\-trilinear\fR Do a tri-linear interpolation between voxels. The edges of the volume are at the centre of the first and last voxels of a dimension. This is the default. .TP \fB\-tricubic\fR Do a tri-cubic interpolation between voxels. The edges of the volume are at the centre of the first and last voxels of a dimension. .TP \fB\-nearest_neighbour\fR Do nearest neighbour interpolation between voxels (ie. find the voxel closest to the point and use its value). The edges of the volume are at the edge of the first and last voxels of a dimension (centre +/- half voxel separation). .TP \fB\-sinc\fR Do renormalized windowed-sinc interpolation between voxels, as described by Thacker et al. JMRI 10:582-588 (1999). .TP \fB\-width\fR \fIn\fR Specifies the half-width of the sinc interpolation kernel, in the range from 1 to 10. The full sinc kernel width is \fIn\fR * 2 + 1, and therefore varies from 3 to 21. The default value is 5 giving a full-width of 11. .TP \fB\-hanning\fR Use a Hanning window with the sinc interpolant. This is the default. .TP \fB\-hamming\fR Use a Hamming window with the sinc interpolant. .SH Generic options .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES Resample an individual's brain in a standardized space on a standard sampling grid: mincresample individual.mnc in_std_space.mnc \\ -transform transform_to_standard_space.xfm \\ -like standard_sampling.mnc Resample an MRI volume to be matched with a PET volume, but with finer resolution: mincresample mri.mnc mri_resampled.mnc \\ -transform mri_to_pet.xfm -like pet.mnc \\ -step 1 1 2 -xstart -0.5 -ystart -0.5 \\ -nelements 256 256 64 Turn a transverse volume into a sagittal volume: mincresample transverse.mnc sagittal.mnc \\ -sagittal -nearest Turn a 256x256x64 (1x1x2mm) transverse volume into 256x128x256 (1x1x1mm) sagittal volume: mincresample transverse.mnc sagittal.mnc -sagittal \\ -zstep 1 -znelem 128 Get a finer axial sampling on a PET volume: mincresample pet_15_slices.mnc pet_46_slices.mnc \\ -zstep 2 -znelements 46 .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin .SH "SEE ALSO" .LP .BR mincreshape (1) minc-tools-2.3.00+dfsg/progs/mincresample/resample_volumes.c0000644000175000000620000014256012574624760023171 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : resample_volumes.c @DESCRIPTION: File containing routines to resample minc volumes. @METHOD : @GLOBALS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : * $Log: resample_volumes.c,v $ * 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:49:00 stever * Add do_Ncubic_interpolation declaration, moved here from mincresample.h. * * Revision 6.10 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.9 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.8 2008/01/11 07:17:08 stever * Remove unused variables. * * Revision 6.7 2007/12/12 20:55:26 rotor * * added a bunch of bug fixes from Claude. * * Revision 6.6 2006/05/19 00:13:39 bert * Add config.h * * Revision 6.5 2005/07/13 21:34:25 bert * Add sinc interpolant (ported from 1.X branch) * * Revision 6.4 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.3 2001/08/16 13:32:39 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/02 14:58:10 neelin * Added -keep_real_range option to prevent rescaling of slices on output * * Revision 6.1 1999/10/19 14:45:28 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:21 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:22 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 19:59:42 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:30:57 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:32:50 neelin * Release of minc version 0.2 * * Revision 1.15 94/09/28 10:32:38 neelin * Pre-release * * Revision 1.14 94/03/15 10:58:47 neelin * Fixed tricubic interpolation (wasn't initializing variable for fillvalue * detection). * * Revision 1.13 94/03/01 14:33:48 neelin * Fixed error in calculating valid minimum for float volumes. * * Revision 1.12 93/11/02 11:23:52 neelin * Handle imagemax/min potentially varying over slices (for vector data, etc.) * * Revision 1.11 93/10/20 14:06:36 neelin * Modified tri-linear interpolation to allow volumes to extend epsilon * beyond the first voxel. * Added code to handle volume dimensions of size one (in tri-linear * interpolation). * * Revision 1.10 93/10/15 13:48:22 neelin * Removed include of recipes.h * * Revision 1.9 93/10/12 12:48:08 neelin * Use volume_io.h instead of def_mni.h * * Revision 1.8 93/08/11 14:29:53 neelin * Use volume->datatype in load_volume instead of file->datatype. * Loop for slice max/min is from 0, not 1 in load_volume. * * Revision 1.7 93/08/11 13:34:15 neelin * Converted to use Dave MacDonald's VIO_General_transform code. * Fixed bug in get_slice - for non-linear transformations coord was * transformed, then used again as a starting coordinate. * Handle files that have image-max/min that doesn't vary over slices. * Handle files that have image-max/min varying over row/cols. * Allow volume to extend to voxel edge for -nearest_neighbour interpolation. * Handle out-of-range values (-fill values from a previous mincresample, for * example). * Save transformation file as a string attribute to processing variable. * @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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "mincresample.h" static void load_volume(File_Info *file, long start[], long count[], Volume_Data *volume); static void get_slice(long slice_num, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation, double *minimum, double *maximum); static void renormalize_slices(Program_Flags *program_flags, VVolume *out_vol, double slice_min[], double slice_max[]); static int do_Ncubic_interpolation(Volume_Data *volume, long index[], int cur_dim, double frac[], double *result); /* ----------------------------- MNI Header ----------------------------------- @NAME : resample_volumes @INPUT : program_flags - data for program execution in_vol - description of input volume out_vol - description of output volume transformation - description of world transformation @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Resamples in_vol into file specified by out_vol using given world transformation. @METHOD : @GLOBALS : @CALLS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void resample_volumes(Program_Flags *program_flags, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation) { long in_start[MAX_VAR_DIMS], in_count[MAX_VAR_DIMS], in_end[MAX_VAR_DIMS]; long out_start[MAX_VAR_DIMS], out_count[MAX_VAR_DIMS]; long mm_start[MAX_VAR_DIMS]; /* VIO_Vector for min/max variables */ long nslice, islice, slice_count; int idim, index, slice_index; double maximum, minimum, valid_range[2]; double *slice_max, *slice_min; File_Info *ifp,*ofp; /* Set pointers to file information */ ifp = in_vol->file; ofp = out_vol->file; /* Allocate slice min/max arrays if needed */ if (ofp->do_slice_renormalization) { slice_min = malloc(ofp->images_per_file * ofp->slices_per_image * sizeof(double)); slice_max = malloc(ofp->images_per_file * ofp->slices_per_image * sizeof(double)); } /* Set input file start, count and end vectors for reading a volume at a time */ (void) miset_coords(ifp->ndims, (long) 0, in_start); (void) miset_coords(ifp->ndims, (long) 1, in_count); for (idim=0; idim < ifp->ndims; idim++) { in_end[idim] = ifp->nelements[idim]; } for (idim=0; idim < VOL_NDIMS; idim++) { index = ifp->indices[idim]; in_count[index] = ifp->nelements[index]; } /* Set output file count for writing a slice and get the number of output slices */ (void) miset_coords(ifp->ndims, (long) 1, out_count); for (idim=0; idim < VOL_NDIMS; idim++) { index = ofp->indices[idim]; if (idim==0) { slice_index = index; nslice = ofp->nelements[index]; } else { out_count[index] = ofp->nelements[index]; } } /* Initialize global max and min */ valid_range[0] = DBL_MAX; valid_range[1] = -DBL_MAX; /* Initialize file max/min slice count */ slice_count = 0; /* Print log message */ if (program_flags->verbose) { (void) fprintf(stderr, "Transforming slices:"); (void) fflush(stderr); } /* Loop over input volumes */ while (in_start[0] < in_end[0]) { /* Copy the start vector */ for (idim=0; idim < ifp->ndims; idim++) out_start[idim] = in_start[idim]; /* Read in the volume */ load_volume(ifp, in_start, in_count, in_vol->volume); /* Loop over slices */ for (islice=0; islice < nslice; islice++) { /* Print log message */ if (program_flags->verbose) { (void) fprintf(stderr, "."); (void) fflush(stderr); } /* Set slice number in out_start */ out_start[slice_index] = islice; /* Get the slice */ get_slice(islice, in_vol, out_vol, transformation, &minimum, &maximum); /* Check whether we are keep the input range */ if (ofp->keep_real_range) { minimum = in_vol->volume->real_range[0]; maximum = in_vol->volume->real_range[1]; } /* Update global max and min */ if (maximum > valid_range[1]) valid_range[1] = maximum; if (minimum < valid_range[0]) valid_range[0] = minimum; /* Write the max, min and slice */ (void) mivarput1(ofp->mincid, ofp->maxid, mitranslate_coords(ofp->mincid, ofp->imgid, out_start, ofp->maxid, mm_start), NC_DOUBLE, NULL, &maximum); (void) mivarput1(ofp->mincid, ofp->minid, mitranslate_coords(ofp->mincid, ofp->imgid, out_start, ofp->minid, mm_start), NC_DOUBLE, NULL, &minimum); (void) miicv_put(ofp->icvid, out_start, out_count, out_vol->slice->data); /* Save the max, min if needed */ if (ofp->do_slice_renormalization) { slice_max[slice_count] = maximum; slice_min[slice_count] = minimum; } /* Increment slice count */ slice_count++; } /* End loop over slices */ /* Increment in_start counter */ idim = ofp->ndims-1; in_start[idim] += in_count[idim]; while ( (idim>0) && (in_start[idim] >= in_end[idim])) { in_start[idim] = 0; idim--; in_start[idim] += in_count[idim]; } } /* End loop over volumes */ /* Print end of log message */ if (program_flags->verbose) { (void) fprintf(stderr, "Done\n"); (void) fflush(stderr); } /* If output volume is floating point, write out global max and min */ if ((ofp->datatype == NC_FLOAT) || (ofp->datatype == NC_DOUBLE)) { (void) miset_valid_range(ofp->mincid, ofp->imgid, valid_range); } /* Recompute slices and free vectors, if needed */ if (ofp->do_slice_renormalization) { renormalize_slices(program_flags, out_vol, slice_min, slice_max); free(slice_min); free(slice_max); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : load_volume @INPUT : file - description of input file start - index of start of volume in minc file count - vector size of volume in minc file volume - description of volume data @OUTPUT : volume - contains new volume data and scales and offsets @RETURNS : (none) @DESCRIPTION: Loads a volume from a minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void load_volume(File_Info *file, long start[], long count[], Volume_Data *volume) { long nread, islice, mm_start[MAX_VAR_DIMS], mm_count[MAX_VAR_DIMS]; int varid, ivar, idim, ndims; double *values, maximum, minimum, denom; /* Load the file */ if (file->using_icv) { (void) miicv_get(file->icvid, start, count, volume->data); } else { (void) ncvarget(file->mincid, file->imgid, start, count, volume->data); } /* Read the max and min from the file into the scale and offset variables (maxima into scale and minima into offset) if datatype is not floating point */ /* Don't do it ourselves if the icv is doing it for us */ if (file->using_icv) { for (islice=0; islice < volume->size[SLC_AXIS]; islice++) { (void) miicv_inqdbl(file->icvid, MI_ICV_NORM_MAX, &volume->scale[islice]); (void) miicv_inqdbl(file->icvid, MI_ICV_NORM_MIN, &volume->offset[islice]); } } /* If either max/min variable doesn't exist, or if type is floating point, then set max to 1 and min to 0 */ else if ((file->maxid == MI_ERROR) || (file->minid == MI_ERROR) || (volume->datatype == NC_FLOAT) || (volume->datatype == NC_DOUBLE)) { for (islice=0; islice < volume->size[SLC_AXIS]; islice++) { volume->scale[islice] = DEFAULT_MAX; volume->offset[islice] = DEFAULT_MIN; } } /* Otherwise the imagemax/min variables exist - use them for integer types */ else { for (ivar=0; ivar<2; ivar++) { /* Set up variables */ varid = (ivar == 0 ? file->maxid : file->minid); values = (ivar == 0 ? volume->scale : volume->offset); /* Read max or min */ (void) mivarget(file->mincid, varid, mitranslate_coords(file->mincid, file->imgid, start, varid, miset_coords(MAX_VAR_DIMS, 0L, mm_start)), mitranslate_coords(file->mincid, file->imgid, count, varid, miset_coords(MAX_VAR_DIMS, 1L, mm_count)), NC_DOUBLE, NULL, values); /* Check for number of values read */ (void) ncvarinq(file->mincid, varid, NULL, NULL, &ndims, NULL, NULL); for (nread=1, idim=0; idimsize[SLC_AXIS]; islice++) { values[islice] = values[0]; } } else if (nread != volume->size[SLC_AXIS]) { (void) fprintf(stderr, "Program bug while reading image max/min\n"); exit(EXIT_FAILURE); } } /* Loop over max/min variables */ } /* If max/min variables both exist */ /* Get the real range of this volume (offset is min, scale is max) */ if (volume->datatype == NC_FLOAT || volume->datatype == NC_DOUBLE) { volume->real_range[0] = file->vrange[0]; volume->real_range[1] = file->vrange[1]; } else { volume->real_range[0] = volume->offset[0]; volume->real_range[1] = volume->scale[0]; for (islice=1; islice < volume->size[SLC_AXIS]; islice++) { if (volume->offset[islice] < volume->real_range[0]) volume->real_range[0] = volume->offset[islice]; if (volume->scale[islice] > volume->real_range[1]) volume->real_range[1] = volume->scale[islice]; } } /* Calculate the scale and offset */ for (islice=0; islice < volume->size[SLC_AXIS]; islice++) { /* If the variables type is floating point, then don't scale */ if ((volume->datatype==NC_FLOAT) || (volume->datatype==NC_DOUBLE)) { volume->scale[islice] = 1.0; volume->offset[islice] = 0.0; } /* Otherwise, calculate scale and offset */ else { maximum = volume->scale[islice]; minimum = volume->offset[islice]; denom = file->vrange[1] - file->vrange[0]; if (denom != 0.0) { volume->scale[islice] = (maximum - minimum) / denom; } else { volume->scale[islice] = 0.0; } volume->offset[islice] = minimum - file->vrange[0] * volume->scale[islice]; } } /* End of loop through slices */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_slice @INPUT : in_vol - description of input volume out_vol - description of output volume transformation - description of world transformation @OUTPUT : out_vol - slice field contains new slice minimum - slice minimum (excluding data from outside volume) maximum - slice maximum (excluding data from outside volume) @RETURNS : (none) @DESCRIPTION: Resamples current volume of in_vol into slice in out_vol using given world transformation. @METHOD : @GLOBALS : @CALLS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void get_slice(long slice_num, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation, double *minimum, double *maximum) { Slice_Data *slice; Volume_Data *volume; double *dptr; long irow, icol; int all_linear; int idim; VIO_Real separations[WORLD_NDIMS]; int idim_in; char dimname[MAX_NC_NAME]; int dim[MAX_VAR_DIMS], dimid; int imgid; int ndims; /* Coordinate vectors for stepping through slice */ Coord_Vector zero = {0, 0, 0}; Coord_Vector column = {0, 0, 1}; Coord_Vector row = {0, 1, 0}; Coord_Vector start = {0, 0, 0}; /* start[SLICE] set later to slice_num */ Coord_Vector coord, transf_coord; /* Transformation stuff */ VIO_General_transform total_transf, temp_transf; /* Get slice and volume pointers */ volume = in_vol->volume; slice = out_vol->slice; /* Concatenate transforms */ concat_general_transforms(out_vol->voxel_to_world, transformation, &temp_transf); concat_general_transforms(&temp_transf, in_vol->world_to_voxel, &total_transf); delete_general_transform(&temp_transf); /* Check for complete linear transformation */ all_linear = (get_transform_type(&total_transf) == LINEAR); /* VIO_Transform vectors for linear transformation */ start[SLICE] = slice_num; if (all_linear) { DO_TRANSFORM(zero, &total_transf, zero); DO_TRANSFORM(column, &total_transf, column); DO_TRANSFORM(row, &total_transf, row); DO_TRANSFORM(start, &total_transf, start); } /* Make sure that row and column are vectors and not points */ VECTOR_DIFF(row, row, zero); VECTOR_DIFF(column, column, zero); /* Initialize maximum and minimum */ *maximum = -DBL_MAX; *minimum = DBL_MAX; imgid = ncvarid(in_vol->file->mincid, MIimage); ncvarinq(in_vol->file->mincid, imgid, NULL, NULL, &ndims, dim, NULL); /* Get the steps sizes (separations) of the input volume in order to get an appropriate error margin (ftol) for the function grid_inverse_transform_point */ for (idim_in=0; idim_in < in_vol->file->ndims; idim_in++) { /* Get size of dimension */ (void) ncdiminq(in_vol->file->mincid, dim[idim_in], dimname, &in_vol->file->nelements[idim_in]); /* Check for existence of variable */ dimid = ncvarid(in_vol->file->mincid, dimname); if (dimid == MI_ERROR) continue; /* Get attributes from variget_file_infoable */ (void) miattget1(in_vol->file->mincid, dimid, MIstep, NC_DOUBLE, &separations[in_vol->file->world_axes[idim_in]]); if (separations[in_vol->file->world_axes[idim_in]] == 0.0) separations[in_vol->file->world_axes[idim_in]] = 1.0; } /* Loop over rows of slice */ for (irow=0; irow < slice->size[SLICE_ROW]; irow++) { /* Set starting coordinate of row */ VECTOR_SCALAR_MULT(coord, row, irow); VECTOR_ADD(coord, coord, start); /* Loop over columns */ dptr = slice->data + irow*slice->size[SLICE_COL]; for (icol=0; icol < slice->size[SLICE_COL]; icol++) { /* If transformation is not completely linear, then transform voxel to world, world to world and world to voxel, as needed */ for (idim=0; idimuse_fill) { if (*dptr > *maximum) *maximum = *dptr; if (*dptr < *minimum) *minimum = *dptr; } /* Increment coordinate */ VECTOR_ADD(coord, coord, column); /* Increment slice pointer */ dptr++; } /* Loop over columns */ } /* Loop over rows */ if ((*maximum == -DBL_MAX) && (*minimum == DBL_MAX)) { *minimum = 0.0; *maximum = SMALL_VALUE; } else if (*maximum <= *minimum) { if (*minimum == 0.0) *maximum = SMALL_VALUE; else if (*minimum < 0.0) *maximum = 0.0; else *maximum = 2.0 * (*minimum); } /* Delete the transformation */ delete_general_transform(&total_transf); } /* ----------------------------- MNI Header ----------------------------------- @NAME : trilinear_interpolant @INPUT : volume - pointer to volume data coord - point at which volume should be interpolated in voxel units (with 0 being first point of the volume). @OUTPUT : result - interpolated value. @RETURNS : TRUE if coord is within the volume, FALSE otherwise. @DESCRIPTION: Routine to interpolate a volume at a point with tri-linear interpolation. @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int trilinear_interpolant(Volume_Data *volume, Coord_Vector coord, double *result) { long slcind, rowind, colind, slcmax, rowmax, colmax; long slcnext, rownext, colnext; static double f0, f1, f2, r0, r1, r2, r1r2, r1f2, f1r2, f1f2; static double v000, v001, v010, v011, v100, v101, v110, v111; /* Check that the coordinate is inside the volume */ slcmax = volume->size[SLC_AXIS] - 1; rowmax = volume->size[ROW_AXIS] - 1; colmax = volume->size[COL_AXIS] - 1; if ((coord[SLICE] < -VOXEL_COORD_EPS) || (coord[SLICE] > slcmax+VOXEL_COORD_EPS) || (coord[ROW] < -VOXEL_COORD_EPS) || (coord[ROW] > rowmax+VOXEL_COORD_EPS) || (coord[COLUMN] < -VOXEL_COORD_EPS) || (coord[COLUMN] > colmax+VOXEL_COORD_EPS)) { *result = volume->fillvalue; return FALSE; } /* Get the whole part of the coordinate */ slcind = (long) coord[SLICE]; rowind = (long) coord[ROW]; colind = (long) coord[COLUMN]; if (slcind >= slcmax-1) slcind = slcmax-1; if (rowind >= rowmax-1) rowind = rowmax-1; if (colind >= colmax-1) colind = colmax-1; /* Get the next voxel up */ slcnext = slcind+1; rownext = rowind+1; colnext = colind+1; /* Check for case of dimension of length one */ if (slcmax == 0) { slcind = 0; slcnext = 0; } if (rowmax == 0) { rowind = 0; rownext = 0; } if (colmax == 0) { colind = 0; colnext = 0; } /* Get the relevant voxels */ VOLUME_VALUE(volume, slcind , rowind , colind , v000); VOLUME_VALUE(volume, slcind , rowind , colnext, v001); VOLUME_VALUE(volume, slcind , rownext, colind , v010); VOLUME_VALUE(volume, slcind , rownext, colnext, v011); VOLUME_VALUE(volume, slcnext, rowind , colind , v100); VOLUME_VALUE(volume, slcnext, rowind , colnext, v101); VOLUME_VALUE(volume, slcnext, rownext, colind , v110); VOLUME_VALUE(volume, slcnext, rownext, colnext, v111); /* Check that the values are not fill values */ if ((v000 < volume->vrange[0]) || (v000 > volume->vrange[1]) || (v001 < volume->vrange[0]) || (v001 > volume->vrange[1]) || (v010 < volume->vrange[0]) || (v010 > volume->vrange[1]) || (v011 < volume->vrange[0]) || (v011 > volume->vrange[1]) || (v100 < volume->vrange[0]) || (v100 > volume->vrange[1]) || (v101 < volume->vrange[0]) || (v101 > volume->vrange[1]) || (v110 < volume->vrange[0]) || (v110 > volume->vrange[1]) || (v111 < volume->vrange[0]) || (v111 > volume->vrange[1])) { *result = volume->fillvalue; return FALSE; } /* Get the fraction parts */ f0 = coord[SLICE] - slcind; f1 = coord[ROW] - rowind; f2 = coord[COLUMN] - colind; r0 = 1.0 - f0; r1 = 1.0 - f1; r2 = 1.0 - f2; /* Do the interpolation */ r1r2 = r1 * r2; r1f2 = r1 * f2; f1r2 = f1 * r2; f1f2 = f1 * f2; *result = r0 * (volume->scale[slcind] * (r1r2 * v000 + r1f2 * v001 + f1r2 * v010 + f1f2 * v011) + volume->offset[slcind]); *result += f0 * (volume->scale[slcind+1] * (r1r2 * v100 + r1f2 * v101 + f1r2 * v110 + f1f2 * v111) + volume->offset[slcind+1]); return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : tricubic_interpolant @INPUT : volume - pointer to volume data coord - point at which volume should be interpolated in voxel units (with 0 being first point of the volume). @OUTPUT : result - interpolated value. @RETURNS : TRUE if coord is within the volume, FALSE otherwise. @DESCRIPTION: Routine to interpolate a volume at a point with tri-cubic interpolation. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int tricubic_interpolant(Volume_Data *volume, Coord_Vector coord, double *result) { long slcind, rowind, colind, slcmax, rowmax, colmax, index[VOL_NDIMS]; double frac[VOL_NDIMS]; /* Check that the coordinate is inside the volume */ slcmax = volume->size[SLC_AXIS] - 1; rowmax = volume->size[ROW_AXIS] - 1; colmax = volume->size[COL_AXIS] - 1; /* Identify a slice or a volume. Slice must be in x-y. */ if( slcmax == 0 ) { /* 2-d slice */ if( (coord[ROW] < 0) || (coord[ROW] > rowmax) || (coord[COLUMN] < 0) || (coord[COLUMN] > colmax) ) { *result = volume->fillvalue; return FALSE; } } else { /* 3-d volume */ if( (coord[SLICE] < 0) || (coord[SLICE] > slcmax) || (coord[ROW] < 0) || (coord[ROW] > rowmax) || (coord[COLUMN] < 0) || (coord[COLUMN] > colmax) ) { *result = volume->fillvalue; return FALSE; } } /* Get the whole and fractional part of the coordinate */ slcind = (long)coord[SLICE]; rowind = (long)coord[ROW]; colind = (long)coord[COLUMN]; frac[0] = coord[SLICE] - slcind; frac[1] = coord[ROW] - rowind; frac[2] = coord[COLUMN] - colind; slcind--; rowind--; colind--; /* Check for dimension of image. */ /* Check for edges in the 2-d plane - do linear interpolation at edges */ /* Note: Spline stencil is right-sided, so ok to start at 0. */ if( slcmax == 0 && rowmax > 0 && colmax > 0 ) { if( (rowind > rowmax-3) || (rowind < 0) || (colind > colmax-3) || (colind < 0)) { return trilinear_interpolant(volume, coord, result); } else { slcind = 0; /* there is only slice 0 */ index[0]=slcind; index[1]=rowind; index[2]=colind; if( do_Ncubic_interpolation(volume, index, 1, frac, result) ) { /* scaling not done for a slice in do_Ncubic_interpolation, */ /* only done for a volume */ *result = (*result) * volume->scale[slcind] + volume->offset[slcind]; return TRUE; } else { return FALSE; } } } /* Check for edges in the 3-d volume - do linear interpolation at edges */ /* Note: Spline stencil is right-sided, so ok to start at 0. */ if ((slcind > slcmax-3) || (slcind < 0) || (rowind > rowmax-3) || (rowind < 0) || (colind > colmax-3) || (colind < 0)) { return trilinear_interpolant(volume, coord, result); } else { index[0]=slcind; index[1]=rowind; index[2]=colind; /* Do the interpolation and return its value */ return do_Ncubic_interpolation(volume, index, 0, frac, result); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_Ncubic_interpolation @INPUT : volume - pointer to volume data index - indices to start point in volume (bottom of 4x4x4 cube for interpolation) cur_dim - dimension to be interpolated (0 = volume, 1 = slice, 2 = line) @OUTPUT : result - interpolated value. @RETURNS : TRUE if coord is within the volume, FALSE otherwise. @DESCRIPTION: Routine to interpolate a volume, slice or line (specified by cur_dim). @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int do_Ncubic_interpolation(Volume_Data *volume, long index[], int cur_dim, double frac[], double *result) { long base_index; double v0, v1, v2, v3, u; int found_fillvalue; /* Save index that we will change */ base_index = index[cur_dim]; /* If last dimension, then just get the values */ found_fillvalue = FALSE; if (cur_dim == VOL_NDIMS-1) { VOLUME_VALUE(volume, index[0] ,index[1], index[2], v0); index[cur_dim]++; VOLUME_VALUE(volume, index[0] ,index[1], index[2], v1); index[cur_dim]++; VOLUME_VALUE(volume, index[0] ,index[1], index[2], v2); index[cur_dim]++; VOLUME_VALUE(volume, index[0] ,index[1], index[2], v3); /* Check for fillvalues */ if ((v0 < volume->vrange[0]) || (v0 > volume->vrange[1]) || (v1 < volume->vrange[0]) || (v1 > volume->vrange[1]) || (v2 < volume->vrange[0]) || (v2 > volume->vrange[1]) || (v3 < volume->vrange[0]) || (v3 > volume->vrange[1])) { found_fillvalue = TRUE; } } /* Otherwise, recurse */ else { if (!do_Ncubic_interpolation(volume, index, cur_dim+1, frac, &v0)) { found_fillvalue = TRUE; } index[cur_dim]++; if (!do_Ncubic_interpolation(volume, index, cur_dim+1, frac, &v1)) { found_fillvalue = TRUE; } index[cur_dim]++; if (!do_Ncubic_interpolation(volume, index, cur_dim+1, frac, &v2)) { found_fillvalue = TRUE; } index[cur_dim]++; if (!do_Ncubic_interpolation(volume, index, cur_dim+1, frac, &v3)) { found_fillvalue = TRUE; } } /* Restore index */ index[cur_dim] = base_index; /* Check for fill value found */ if (found_fillvalue) { *result = volume->fillvalue; return FALSE; } /* Scale values for slices */ if (cur_dim==0) { v0 = v0 * volume->scale[base_index ] + volume->offset[base_index ]; v1 = v1 * volume->scale[base_index+1] + volume->offset[base_index+1]; v2 = v2 * volume->scale[base_index+2] + volume->offset[base_index+2]; v3 = v3 * volume->scale[base_index+3] + volume->offset[base_index+3]; } /* Get fraction */ u = frac[cur_dim]; /* Do tricubic interpolation (code from Dave MacDonald). Gives v1 and v2 at u = 0 and 1 and gives continuity of intensity and first derivative. */ *result = ( (v1) + (u) * ( 0.5 * ((v2)-(v0)) + (u) * ( (v0) - 2.5 * (v1) + 2.0 * (v2) - 0.5 * (v3) + (u) * ( -0.5 * (v0) + 1.5 * (v1) - 1.5 * (v2) + 0.5 * (v3) ) ) ) ); return TRUE; } /************************************************************************ * Windowed Sinc Interpolant * * The technique used is borrowed from Neil Thacker et al., "Improved * Quality of Re-sliced MR Images Using Re-normalized Sinc * Interpolation", Journal of Magnetic Resonance Imaging 10:582-588 * (1999). * * Any bugs are of course my own fault! * * -bert */ int sinc_half_width = SINC_HALF_WIDTH_MAX / 2; enum sinc_interpolant_window_t sinc_window_type = SINC_WINDOW_HANNING; /* basic windowed sinc function */ static double windowed_sinc(double delta) { double phase; double sinc; double window; /* Calculate the sinc function. */ phase = delta * M_PI; if (phase == 0.0) { sinc = 1.0; } else { sinc = sin(phase) / phase; } switch (sinc_window_type) { case SINC_WINDOW_HANNING: /* Calculate the Hanning window. */ window = 0.50 + 0.50 * cos(phase / (1.0 + sinc_half_width)); break; case SINC_WINDOW_HAMMING: /* Calculate the Hamming window. */ window = 0.54 + 0.46 * cos(phase / (1.0 + sinc_half_width)); break; default: window = 1.0; /* No window */ break; } return (sinc * window); } /* Floating-point (unscaled) multiply/accumulate operations */ #define SINC_FRND result += *pix_ptr++ * *win_ptr++ /* Unroll those loops!! */ #define SINC_FMAC \ result = 0.0; \ switch (sinc_half_width) { \ case 10: SINC_FRND; SINC_FRND; \ case 9: SINC_FRND; SINC_FRND; \ case 8: SINC_FRND; SINC_FRND; \ case 7: SINC_FRND; SINC_FRND; \ case 6: SINC_FRND; SINC_FRND; \ case 5: SINC_FRND; SINC_FRND; \ case 4: SINC_FRND; SINC_FRND; \ case 3: SINC_FRND; SINC_FRND; \ case 2: SINC_FRND; SINC_FRND; \ case 1: SINC_FRND; SINC_FRND; \ SINC_FRND; /* Do the leftover */ \ } /* Integer (scaled) multiply/accumulate operations */ #define SINC_IRND result += ((slope * *pix_ptr++) + intercept) * *win_ptr++ /* Unroll the loops!! */ #define SINC_IMAC \ result = 0.0; \ switch (sinc_half_width) { \ case 10: SINC_IRND; SINC_IRND; \ case 9: SINC_IRND; SINC_IRND; \ case 8: SINC_IRND; SINC_IRND; \ case 7: SINC_IRND; SINC_IRND; \ case 6: SINC_IRND; SINC_IRND; \ case 5: SINC_IRND; SINC_IRND; \ case 4: SINC_IRND; SINC_IRND; \ case 3: SINC_IRND; SINC_IRND; \ case 2: SINC_IRND; SINC_IRND; \ case 1: SINC_IRND; SINC_IRND; \ SINC_IRND; /* Do the leftover */ \ } static double sinc_mac_d(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; long offset; double *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (double *) volume->data + offset; SINC_FMAC; return (result); } static double sinc_mac_f(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; long offset; float *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (float *) volume->data + offset; SINC_FMAC; return (result); } static double sinc_mac_uc(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; unsigned char *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (unsigned char *) volume->data + offset; SINC_IMAC; return (result); } static double sinc_mac_sc(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; char *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (char *) volume->data + offset; SINC_IMAC; return (result); } static double sinc_mac_us(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; unsigned short *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (unsigned short *) volume->data + offset; SINC_IMAC; return (result); } static double sinc_mac_ss(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; short *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (short *) volume->data + offset; SINC_IMAC; return (result); } static double sinc_mac_ui(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; unsigned int *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (unsigned int *) volume->data + offset; SINC_IMAC; return (result); } static double sinc_mac_si(Volume_Data *volume, int z, int y, int x, double *win_ptr) { double result; double slope = volume->scale[z]; double intercept = volume->offset[z]; long offset; int *pix_ptr; offset = (z * volume->size[ROW_AXIS] + y) * volume->size[COL_AXIS] + x; pix_ptr = (int *) volume->data + offset; SINC_IMAC; return (result); } /* ----------------------------- MNI Header ----------------------------------- @NAME : windowed_sinc_interpolant @INPUT : volume - pointer to volume data coord - point at which volume should be interpolated in voxel units (with 0 being first point of the volume). @OUTPUT : result - interpolated value. @RETURNS : TRUE if coord is within the volume, FALSE otherwise. @DESCRIPTION: Routine to interpolate a volume at a point with windowed sinc interpolation. @METHOD : @GLOBALS : @CALLS : @CREATED : July 11 2005 (Robert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ int windowed_sinc_interpolant(Volume_Data *volume, Coord_Vector coord, double *result) { double zt, yt, xt; double zf, yf, xf; int zi, yi, xi; int i, j; double new; double tmp; long slcmax, rowmax, colmax; double zw[SINC_HALF_WIDTH_MAX * 2 + 1]; double yw[SINC_HALF_WIDTH_MAX * 2 + 1]; double xw[SINC_HALF_WIDTH_MAX * 2 + 1]; slcmax = volume->size[SLC_AXIS] - 1; rowmax = volume->size[ROW_AXIS] - 1; colmax = volume->size[COL_AXIS] - 1; if ((coord[SLICE] < 0) || (coord[SLICE] > slcmax) || (coord[ROW] < 0) || (coord[ROW] > rowmax) || (coord[COLUMN] < 0) || (coord[COLUMN] > colmax)) { *result = volume->fillvalue; return FALSE; } zi = (int) coord[SLICE]; yi = (int) coord[ROW]; xi = (int) coord[COLUMN]; /* Check for edges - do linear interpolation at edges */ if ((zi > slcmax-sinc_half_width) || (zi < sinc_half_width) || (yi > rowmax-sinc_half_width) || (yi < sinc_half_width) || (xi > colmax-sinc_half_width) || (xi < sinc_half_width)) { return trilinear_interpolant(volume, coord, result); } /* Calculate fractional part of the coordinate. */ zf = coord[SLICE] - zi; yf = coord[ROW] - yi; xf = coord[COLUMN] - xi; /* Initialize the totals. */ zt = 0.0; yt = 0.0; xt = 0.0; /* Generate the three windowed sinc functions. */ for (i = -sinc_half_width; i <= sinc_half_width; i++) { tmp = windowed_sinc(zf - i); zw[i + sinc_half_width] = tmp; zt += tmp; tmp = windowed_sinc(yf - i); yw[i + sinc_half_width] = tmp; yt += tmp; tmp = windowed_sinc(xf - i); xw[i + sinc_half_width] = tmp; xt += tmp; } /* Now calculate the new value. */ new = 0.0; for (i = -sinc_half_width; i <= sinc_half_width; i++) { for (j = -sinc_half_width; j <= sinc_half_width; j++) { switch (volume->datatype) { case NC_BYTE: if (volume->is_signed) { tmp = sinc_mac_sc(volume, zi + i, yi + j, xi - sinc_half_width, xw); } else { tmp = sinc_mac_uc(volume, zi + i, yi + j, xi - sinc_half_width, xw); } break; case NC_SHORT: if (volume->is_signed) { tmp = sinc_mac_ss(volume, zi + i, yi + j, xi - sinc_half_width, xw); } else { tmp = sinc_mac_us(volume, zi + i, yi + j, xi - sinc_half_width, xw); } break; case NC_INT: if (volume->is_signed) { tmp = sinc_mac_si(volume, zi + i, yi + j, xi - sinc_half_width, xw); } else { tmp = sinc_mac_ui(volume, zi + i, yi + j, xi - sinc_half_width, xw); } break; case NC_FLOAT: tmp = sinc_mac_f(volume, zi + i, yi + j, xi - sinc_half_width, xw); break; case NC_DOUBLE: tmp = sinc_mac_d(volume, zi + i, yi + j, xi - sinc_half_width, xw); break; default: fprintf(stderr, "UNHANDLED TYPE!!!\n"); break; } new += zw[i + sinc_half_width] * yw[j + sinc_half_width] * tmp; } } *result = (new / (zt * yt * xt)); return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : nearest_neighbour_interpolant @INPUT : volume - pointer to volume data coord - point at which volume should be interpolated in voxel units (with 0 being first point of the volume). @OUTPUT : result - interpolated value. @RETURNS : TRUE if coord is within the volume, FALSE otherwise. @DESCRIPTION: Routine to interpolate a volume at a point with nearest neighbour interpolation. Allows the coord to be outside the volume by up to 1/2 a pixel. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int nearest_neighbour_interpolant(Volume_Data *volume, Coord_Vector coord, double *result) { long slcind, rowind, colind, slcmax, rowmax, colmax; /* Check that the coordinate is inside the volume */ slcmax = volume->size[SLC_AXIS] - 1; rowmax = volume->size[ROW_AXIS] - 1; colmax = volume->size[COL_AXIS] - 1; slcind = VIO_ROUND(coord[SLICE]); rowind = VIO_ROUND(coord[ROW]); colind = VIO_ROUND(coord[COLUMN]); if ((slcind < 0) || (slcind > slcmax) || (rowind < 0) || (rowind > rowmax) || (colind < 0) || (colind > colmax)) { *result = volume->fillvalue; return FALSE; } /* Get the value */ VOLUME_VALUE(volume, slcind , rowind , colind , *result); /* Check for fillvalue on input */ if ((*result < volume->vrange[0]) || (*result > volume->vrange[1])) { *result = volume->fillvalue; return FALSE; } *result = volume->scale[slcind] * (*result) + volume->offset[slcind]; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : renormalize_slices @INPUT : ofp - output file pointer slice_min - array of slice minima slice_max - array of slice maxima @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to loop through the output file and renormalize the slices. @METHOD : @GLOBALS : @CALLS : @CREATED : October 29, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void renormalize_slices(Program_Flags *program_flags, VVolume *out_vol, double slice_min[], double slice_max[]) { File_Info *ofp; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS], end[MAX_VAR_DIMS]; long mm_start[MAX_VAR_DIMS]; long nslice, image, islice, ivolume, image_slice, slice_count; int idim, slice_index, index; double *image_maximum, *image_minimum; /* Set pointer to file information */ ofp = out_vol->file; /* Set output file start, count and end vectors for stepping a slice at a time */ (void) miset_coords(ofp->ndims, (long) 0, start); (void) miset_coords(ofp->ndims, (long) 1, count); for (idim=0; idim < ofp->ndims; idim++) { end[idim] = ofp->nelements[idim]; } for (idim=0; idim < VOL_NDIMS; idim++) { index = ofp->indices[idim]; if (idim==0) { slice_index = index; nslice = ofp->nelements[index]; count[slice_index] = 1; } else { count[index] = ofp->nelements[index]; } } /* Find the max/min for each image */ image_maximum = malloc( sizeof(double) * ofp->images_per_file); image_minimum = malloc( sizeof(double) * ofp->images_per_file); for (image=0; image < ofp->images_per_file; image++) { image_maximum[image] = -DBL_MAX; image_minimum[image] = DBL_MAX; } slice_count = 0; for (ivolume=0; ivolume < ofp->images_per_file / nslice; ivolume++) { for (image_slice=0; image_sliceslices_per_image; image_slice++) { for (islice=0; islice < nslice; islice++) { image = ivolume * nslice + islice; image_maximum[image] = MAX(image_maximum[image], slice_max[slice_count]); image_minimum[image] = MIN(image_minimum[image], slice_min[slice_count]); slice_count++; } } } /* Initialize file max/min slice count */ slice_count = 0; /* Print log message */ if (program_flags->verbose) { (void) fprintf(stderr, "Renormalizing slices:"); (void) fflush(stderr); } /* Loop over output volumes */ while (start[0] < end[0]) { /* Loop over slices */ for (islice=0; islice < nslice; islice++) { /* Print log message */ if (program_flags->verbose) { (void) fprintf(stderr, "."); (void) fflush(stderr); } /* Set slice number in out_start */ start[slice_index] = islice; /* Figure out which image, slice, volume, etc we are dealing with */ islice = slice_count % nslice; ivolume = slice_count / nslice / ofp->slices_per_image; image = ivolume * nslice + islice; /* Get the slice min/max start coordinate */ (void) mitranslate_coords(ofp->mincid, ofp->imgid, start, ofp->maxid, mm_start), /* Write out the slice max and min */ (void) mivarput1(ofp->mincid, ofp->maxid, mm_start, NC_DOUBLE, NULL, &slice_max[slice_count]); (void) mivarput1(ofp->mincid, ofp->minid, mm_start, NC_DOUBLE, NULL, &slice_min[slice_count]); /* Read in the slice */ (void) miicv_get(ofp->icvid, start, count, out_vol->slice->data); /* Write the image max, min and slice */ (void) mivarput1(ofp->mincid, ofp->maxid, mm_start, NC_DOUBLE, NULL, &image_maximum[image]); (void) mivarput1(ofp->mincid, ofp->minid, mm_start, NC_DOUBLE, NULL, &image_minimum[image]); (void) miicv_put(ofp->icvid, start, count, out_vol->slice->data); /* Increment slice count */ slice_count++; } /* End loop over slices */ /* Set slice counter to ensure that we move to the next volume */ start[slice_index] = end[slice_index] - 1; /* Increment in_start counter */ idim = ofp->ndims-1; start[idim] += count[idim]; while ( (idim>0) && (start[idim] >= end[idim])) { start[idim] = 0; idim--; start[idim] += count[idim]; } } /* End loop over volumes */ /* Print end of log message */ if (program_flags->verbose) { (void) fprintf(stderr, "Done\n"); (void) fflush(stderr); } /* Free the image max/min arrays */ free(image_maximum); free(image_minimum); return; } minc-tools-2.3.00+dfsg/progs/mincresample/mincresample.c0000644000175000000620000025226412574624760022271 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincresample @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Program to resample a minc file along different spatial coordinate axes. @METHOD : @GLOBALS : @CALLS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : * $Log: mincresample.c,v $ * Revision 6.23 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.22 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.21 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.20 2006/07/28 18:19:46 baghdadi * *** empty log message *** * * Revision 6.19 2006/07/28 17:54:57 baghdadi * added vector dimension to list of excluded files (minc2. * * Revision 6.18 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.17 2005/07/13 21:34:24 bert * Add sinc interpolant (ported from 1.X branch) * * Revision 6.16 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.15 2004/04/30 19:53:40 bert * Remove unused variable * * Revision 6.14 2004/04/30 18:52:49 bert * Remove some unused variables * * Revision 6.13 2004/04/27 15:31:20 bert * Added -2 option * * Revision 6.12 2003/09/18 15:01:33 bert * Removed unnecessary brackets from initializer * * Revision 6.11 2002/11/06 13:32:23 jason * Fixes to mincresample: setting the interpolation type is now done * through an enum rather than function pointers. * * Revision 6.10 2002/10/30 13:53:02 jason * Added a ARGV_LONG argument type to ParseArgv. Used that type for the nelements variable in mincresample * * Revision 6.9 2001/08/24 19:12:50 neelin * Re-ordered variables so that image variable is last. This fixes a problem * with an uninitialized processing variable that shows up when mincresample * is linked with netcdf 3.5.0 and an older program linked with 2.3.2 dies * when reading the mincresample output file. * It also allows use of large volumes with 64-bit minc: all variables * must have 32-bit offset, but one (the image) can extend beyond the * 32-bit limit. * * Revision 6.8 2001/08/16 13:32:39 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.7 2001/04/24 13:38:45 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.6 2001/04/17 18:40:22 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.5 2001/04/10 12:44:34 neelin * Re-introduced fix for fillvalues that was lost with last version * * Revision 6.4 2001/04/02 14:58:09 neelin * Added -keep_real_range option to prevent rescaling of slices on output * * Revision 6.2 1999/10/19 14:45:27 neelin * Fixed Log subsitutions for CVS * * Revision 6.1 1998/02/19 15:04:24 neelin * Minor bug fixes. * * Revision 6.0 1997/09/12 13:23:21 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:22 neelin * Release of minc version 0.5 * * Revision 4.1 1997/08/13 15:41:12 neelin * Fixed initialization problem that caused crashing under Linux. * * Revision 4.0 1997/05/07 19:59:42 neelin * Release of minc version 0.4 * * Revision 3.5 1996/01/31 15:22:02 neelin * Fixed bug in transformation of input sampling. * * Revision 3.4 1996/01/30 14:10:24 neelin * Added check for -transformation without -like, -tfm_input_sampling or * -use_input_sampling because of change in behaviour. * * Revision 3.3 1995/12/12 19:15:35 neelin * Added -spacetype, -talairach and -units options. * * Revision 3.2 1995/11/21 14:13:20 neelin * Transform input sampling with transformation and use this as default. * Added -tfm_input_sampling to specify above option. * Added -use_input_sampling to get old behaviour (no longer the default). * Added -origin option (to specify coordinate instead of start values). * Added -standard_sampling option (to set standard values of start, step * and direction cosines). * Added -invert_transformation option. * * Revision 3.1 1995/11/07 15:04:02 neelin * Modified argument parsing so that only one pass is done. * * Revision 3.0 1995/05/15 19:30:57 neelin * Release of minc version 0.3 * * Revision 2.3 1995/05/05 19:11:05 neelin * Modified call to input_transform. * * Revision 2.2 1995/02/09 14:05:51 neelin * Mods to make irix 5 lint happy. * * Revision 2.1 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.0 1994/09/28 10:32:46 neelin * Release of minc version 0.2 * * Revision 1.16 94/09/28 10:32:33 neelin * Pre-release * * Revision 1.15 94/03/17 14:12:09 neelin * Exit with failure if no argument given for -transformation or -like. * ., * * Revision 1.14 94/03/15 16:44:21 neelin * Changed default from -clobber to -noclobber. * * Revision 1.13 93/11/04 15:13:13 neelin * Added support for irregularly spaced dimensions. * * Revision 1.12 93/11/03 14:32:44 neelin * Turn off fill for output file. * * Revision 1.11 93/11/03 12:32:17 neelin * Change ncopen, nccreate and ncclose to miopen, micreate and miclose. * * Revision 1.10 93/11/02 11:23:06 neelin * Handle imagemax/min potentially varying over slices (for vector data, etc.) * * Revision 1.9 93/10/12 12:47:50 neelin * Use volume_io.h instead of def_mni.h * * Revision 1.8 93/09/16 09:56:36 neelin * Added use of open_file_with_default_suffix in get_transformation to * append appropriate suffix for xfm files. * * Revision 1.7 93/08/11 14:28:19 neelin * Modified get_arginfo and check_imageminmax to modify type of volume (not * file) so that output volume gets the input volume type by default when * an icv is used on input. * * Revision 1.6 93/08/11 13:27:59 neelin * Converted to use Dave MacDonald's VIO_General_transform code. * Fixed bug in get_slice - for non-linear transformations coord was * transformed, then used again as a starting coordinate. * Handle files that have image-max/min that doesn't vary over slices. * Handle files that have image-max/min varying over row/cols. * Allow volume to extend to voxel edge for -nearest_neighbour interpolation. * Handle out-of-range values (-fill values from a previous mincresample, for * example). * Save transformation file as a string attribute to processing variable. * @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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "mincresample.h" /* Kludge to catch use of -transformation without -like and without -tfm_input_sampling or -use_input_sampling */ #define TRANSFORM_CHANGE_KLUDGE #ifdef TRANSFORM_CHANGE_KLUDGE int Specified_like = FALSE; int Specified_transform = FALSE; #define SAMPLING_ACTION_NOT_SET (-1) #endif static void get_arginfo(int argc, char *argv[], Program_Flags *program_flags, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation); static void check_imageminmax(File_Info *fp, Volume_Data *volume); static void get_file_info(char *filename, int initialized_volume_def, Volume_Definition *volume_def, File_Info *file_info); static void get_args_volume_def(Volume_Definition *input_volume_def, Volume_Definition *args_volume_def); static void transform_volume_def(Transform_Info *transform_info, Volume_Definition *input_volume_def, Volume_Definition *transformed_volume_def); static int is_zero_vector(double vector[]); static void normalize_vector(double vector[]); static void create_output_file(char *filename, int clobber, Volume_Definition *volume_def, File_Info *in_file, File_Info *out_file, char *tm_stamp, Transform_Info *transform_info); static void get_voxel_to_world_transf(Volume_Definition *volume_def, VIO_General_transform *voxel_to_world); static void irregular_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); static void irregular_inverse_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); static double get_default_range(char *what, nc_type datatype, int is_signed); static void finish_up(VVolume *in_vol, VVolume *out_vol); static int get_transformation(char *dst, char *key, char *nextArg); static int get_model_file(char *dst, char *key, char *nextArg); static int set_standard_sampling(char *dst, char *key, char *nextArg); static int set_spacetype(char *dst, char *key, char *nextArg); static int set_units(char *dst, char *key, char *nextArg); static int get_axis_order(char *dst, char *key, char *nextArg); static int get_fillvalue(char *dst, char *key, char *nextArg); /* Main program */ int main(int argc, char *argv[]) { VVolume in_vol_struct, out_vol_struct; VVolume *in_vol = &in_vol_struct, *out_vol = &out_vol_struct; VIO_General_transform transformation; Program_Flags program_flags; /* Get argument information */ get_arginfo(argc, argv, &program_flags, in_vol, out_vol, &transformation); /* Do the resampling */ resample_volumes(&program_flags, in_vol, out_vol, &transformation); /* Finish up */ finish_up(in_vol, out_vol); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_arginfo @INPUT : argc - number of command-line arguments argv - command-line arguments @OUTPUT : program_flags - data for program execution in_vol - description of input volume. out_vol - description of output volume. transformation - description of world transformation @RETURNS : (nothing) @DESCRIPTION: Routine to get information from arguments about input and output files and transfomation. Sets up all structures completely (including allocating space for data). @METHOD : @GLOBALS : @CALLS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_arginfo(int argc, char *argv[], Program_Flags *program_flags, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation) { /* Argument parsing information */ #ifdef TRANSFORM_CHANGE_KLUDGE static int transform_input_sampling = SAMPLING_ACTION_NOT_SET; #else static int transform_input_sampling = TRUE; #endif static Arg_Data args={ FALSE, /* Clobber */ FALSE, /* Keep scale */ MI_ORIGINAL_TYPE, /* Flag that type not set */ INT_MIN, /* Flag that is_signed has not been set */ {-DBL_MAX, -DBL_MAX}, /* Flag that range not set */ FILL_DEFAULT, /* Flag indicating that fillvalue not set */ {NO_VALUE, NO_VALUE, NO_VALUE}, /* Flag indicating that origin not set */ {TRUE}, /* Verbose */ TRILINEAR, /* use trilinear interpolation by default */ {FALSE, NULL, NULL, 0, NULL}, /* Transformation info is empty at start. Transformation must be set before invoking argument parsing */ { /* Use flags to indicate that values not set */ {NO_AXIS, NO_AXIS, NO_AXIS}, /* Axis order */ {0, 0, 0}, /* nelements */ {0.0, 0.0, 0.0}, /* step */ {NO_VALUE, NO_VALUE, NO_VALUE}, /* start */ {{NO_VALUE, NO_VALUE, NO_VALUE}, /* Default direction cosines */ {NO_VALUE, NO_VALUE, NO_VALUE}, {NO_VALUE, NO_VALUE, NO_VALUE}}, {NULL, NULL, NULL}, /* Pointers to coordinate arrays */ {"", "", ""}, /* units */ {"", "", ""} /* spacetype */ }, FALSE /* MINC 2.0 format? */ }; static ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &args.v2format, "Produce a MINC 2.0 format output file."}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &args.clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &args.clobber, "Do not overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &args.flags.verbose, "Print out log messages as processing is being done (default).\n"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &args.flags.verbose, "Do not print out any log messages.\n"}, {"-transformation", ARGV_FUNC, (char *) get_transformation, (char *) &args.transform_info, "File giving world transformation. (Default = identity)."}, {"-invert_transformation", ARGV_CONSTANT, (char *) TRUE, (char *) &args.transform_info.invert_transform, "Invert the transformation before using it.\n"}, {"-noinvert_transformation", ARGV_CONSTANT, (char *) FALSE, (char *) &args.transform_info.invert_transform, "Do not invert the transformation (default).\n"}, {"-tfm_input_sampling", ARGV_CONSTANT, (char *) TRUE, (char *) &transform_input_sampling, "Transform the input sampling with the transform (default).\n"}, {"-use_input_sampling", ARGV_CONSTANT, (char *) FALSE, (char *) &transform_input_sampling, "Use the input sampling without transforming (old behaviour).\n"}, {"-like", ARGV_FUNC, (char *) get_model_file, (char *) &args.volume_def, "Specifies a model file for the resampling."}, {"-standard_sampling", ARGV_FUNC, (char *) set_standard_sampling, (char *) &args.volume_def, "Set the sampling to standard values (step, start and dircos)."}, {"-spacetype", ARGV_FUNC, (char *) set_spacetype, (char *) &args.volume_def, "Set the spacetype attribute to a specified string."}, {"-talairach", ARGV_FUNC, (char *) set_spacetype, (char *) &args.volume_def, "Output is in Talairach space."}, {"-units", ARGV_FUNC, (char *) set_units, (char *) &args.volume_def, "Specify the units of the output sampling."}, {"-nelements", ARGV_LONG, (char *) 3, (char *) args.volume_def.nelements, "Number of elements along each dimension (X, Y, Z)"}, {"-xnelements", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_X], "Number of elements along the X dimension"}, {"-ynelements", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_Y], "Number of elements along the Y dimension"}, {"-znelements", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_Z], "Number of elements along the Z dimension"}, {"-size", ARGV_LONG, (char *) 3, (char *) args.volume_def.nelements, "synonym for -nelements)"}, {"-xsize", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_X], "synonym for -xnelements"}, {"-ysize", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_Y], "synonym for -ynelements"}, {"-zsize", ARGV_LONG, (char *) 0, (char *) &args.volume_def.nelements[VIO_Z], "synonym for -ynelements"}, {"-step", ARGV_FLOAT, (char *) 3, (char *) args.volume_def.step, "Step size along each dimension (X, Y, Z)"}, {"-xstep", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.step[VIO_X], "Step size along the X dimension"}, {"-ystep", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.step[VIO_Y], "Step size along the Y dimension"}, {"-zstep", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.step[VIO_Z], "Step size along the Z dimension"}, {"-start", ARGV_FLOAT, (char *) 3, (char *) args.volume_def.start, "Start point along each dimension (X, Y, Z)"}, {"-xstart", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.start[VIO_X], "Start point along the X dimension"}, {"-ystart", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.start[VIO_Y], "Start point along the Y dimension"}, {"-zstart", ARGV_FLOAT, (char *) 0, (char *) &args.volume_def.start[VIO_Z], "Start point along the Z dimension"}, {"-dircos", ARGV_FLOAT, (char *) 9, (char *) args.volume_def.dircos, "Direction cosines along each dimension (X, Y, Z)"}, {"-xdircos", ARGV_FLOAT, (char *) 3, (char *) args.volume_def.dircos[VIO_X], "Direction cosines along the X dimension"}, {"-ydircos", ARGV_FLOAT, (char *) 3, (char *) args.volume_def.dircos[VIO_Y], "Direction cosines along the Y dimension"}, {"-zdircos", ARGV_FLOAT, (char *) 3, (char *) args.volume_def.dircos[VIO_Z], "Direction cosines along the Z dimension"}, {"-origin", ARGV_FLOAT, (char *) 3, (char *) args.origin, "Origin of first pixel in 3D space"}, {"-transverse", ARGV_FUNC, (char *) get_axis_order, (char *) &args.volume_def, "Write out transverse slices"}, {"-sagittal", ARGV_FUNC, (char *) get_axis_order, (char *) &args.volume_def, "Write out sagittal slices"}, {"-coronal", ARGV_FUNC, (char *) get_axis_order, (char *) &args.volume_def, "Write out coronal slices"}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &args.datatype, "Write out byte data"}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &args.datatype, "Write out short integer data"}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &args.datatype, "Write out 32-bit integer data"}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &args.datatype, "Superseded by -int"}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &args.datatype, "Write out single-precision floating-point data"}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &args.datatype, "Write out double-precision floating-point data"}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &args.is_signed, "Write signed integer data"}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &args.is_signed, "Write unsigned integer data"}, {"-range", ARGV_FLOAT, (char *) 2, (char *) args.vrange, "Valid range for output data"}, {"-keep_real_range", ARGV_CONSTANT, (char *) TRUE, (char *) &args.keep_real_range, "Keep the real scale of the input volume"}, {"-nokeep_real_range", ARGV_CONSTANT, (char *) FALSE, (char *) &args.keep_real_range, "Do not keep the real scale of the data (default)"}, {"-nofill", ARGV_FUNC, (char *) get_fillvalue, (char *) &args.fillvalue, "Use value zero for points outside of input volume"}, {"-fill", ARGV_FUNC, (char *) get_fillvalue, (char *) &args.fillvalue, "Use a fill value for points outside of input volume"}, {"-fillvalue", ARGV_FLOAT, (char *) 0, (char *) &args.fillvalue, "Specify a fill value for points outside of input volume"}, {"-trilinear", ARGV_CONSTANT, (char *) TRILINEAR, (char *) &args.interpolant_type, "Do trilinear interpolation"}, {"-tricubic", ARGV_CONSTANT, (char *) TRICUBIC, (char *) &args.interpolant_type, "Do tricubic interpolation"}, {"-nearest_neighbour", ARGV_CONSTANT, (char *) N_NEIGHBOUR, (char *) &args.interpolant_type, "Do nearest neighbour interpolation"}, {"-sinc", ARGV_CONSTANT, (char *) WINDOWED_SINC, (char *) &args.interpolant_type, "Do windowed sinc interpolation"}, {"-width", ARGV_INT, (char *) 1, (char *) &sinc_half_width, "Set half-width of sinc window (1-10)" }, {"-hanning", ARGV_CONSTANT, (char *) SINC_WINDOW_HANNING, (char *) &sinc_window_type, "Set sinc window type to Hanning"}, {"-hamming", ARGV_CONSTANT, (char *) SINC_WINDOW_HAMMING, (char *) &sinc_window_type, "Set sinc window type to Hamming"}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Other variables */ int idim, index; int out_vindex; /* Volume indices (0, 1 or 2) */ int out_findex; /* File indices (0 to ndims-1) */ long size, total_size; char *infile, *outfile; File_Info *fp; char *tm_stamp, *pname; Volume_Definition input_volume_def, transformed_volume_def; VIO_General_transform input_transformation; int cflags; /* Initialize the transformation to identity */ create_linear_transform(&input_transformation, NULL); args.transform_info.transformation = &input_transformation; /* Get the time stamp */ tm_stamp = time_stamp(argc, argv); /* Save the program name */ pname=argv[0]; /* Call ParseArgv */ if (ParseArgv(&argc, argv, argTable, 0) || (argc!=3)) { (void) fprintf(stderr, "\nUsage: %s [] \n", pname); (void) fprintf(stderr, " %s [-help]\n\n", pname); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; #ifdef TRANSFORM_CHANGE_KLUDGE if (Specified_transform && !Specified_like && (transform_input_sampling == SAMPLING_ACTION_NOT_SET)) { (void) fprintf(stderr, "Use -like, -tfm_input_sampling or " "-use_input_sampling with -transformation\n"); exit(EXIT_FAILURE); } if (transform_input_sampling == SAMPLING_ACTION_NOT_SET) { transform_input_sampling = TRUE; } #endif /* Check for an inverted transform. This looks backwards because we normally invert the transform. */ if (args.transform_info.invert_transform) { copy_general_transform(args.transform_info.transformation, transformation); } else { create_inverse_general_transform(args.transform_info.transformation, transformation); } args.transform_info.transformation = transformation; /* Get rid of the input transformation */ delete_general_transform(&input_transformation); /* Check input file for default argument information */ in_vol->file = malloc(sizeof(File_Info)); get_file_info(infile, FALSE, &input_volume_def, in_vol->file); transform_volume_def((transform_input_sampling ? &args.transform_info : NULL), &input_volume_def, &transformed_volume_def); get_args_volume_def(&transformed_volume_def, &args.volume_def); /* Check that direction cosines are normalized and look for origin option */ for (idim=0; idim < WORLD_NDIMS; idim++) { normalize_vector(args.volume_def.dircos[idim]); if (is_zero_vector(args.volume_def.dircos[idim])) { (void) fprintf(stderr, "Bad direction cosines.\n"); exit(EXIT_FAILURE); } } if (args.origin[0] != NO_VALUE) { if (convert_origin_to_start(args.origin, args.volume_def.dircos[XCOORD], args.volume_def.dircos[YCOORD], args.volume_def.dircos[ZCOORD], args.volume_def.start) != 0) { (void) fprintf(stderr, "Error converting origin to start value: "); (void) fprintf(stderr, "Bad direction cosines.\n"); exit(EXIT_FAILURE); } } /* Save the voxel_to_world transformation information */ in_vol->voxel_to_world = malloc(sizeof(VIO_General_transform)); in_vol->world_to_voxel = malloc(sizeof(VIO_General_transform)); get_voxel_to_world_transf(&input_volume_def, in_vol->voxel_to_world); create_inverse_general_transform(in_vol->voxel_to_world, in_vol->world_to_voxel); /* Get input volume data information */ in_vol->slice = NULL; in_vol->volume = malloc(sizeof(Volume_Data)); in_vol->volume->datatype = in_vol->file->datatype; in_vol->volume->is_signed = in_vol->file->is_signed; in_vol->volume->vrange[0] = in_vol->file->vrange[0]; in_vol->volume->vrange[1] = in_vol->file->vrange[1]; if (args.fillvalue == FILL_DEFAULT) { in_vol->volume->fillvalue = 0.0; in_vol->volume->use_fill = TRUE; } else { in_vol->volume->fillvalue = args.fillvalue; in_vol->volume->use_fill = (args.fillvalue != -DBL_MAX); } /* set the function pointer defining the type of interpolation */ switch (args.interpolant_type ) { case TRICUBIC: in_vol->volume->interpolant = tricubic_interpolant; break; case TRILINEAR: in_vol->volume->interpolant = trilinear_interpolant; break; case N_NEIGHBOUR: in_vol->volume->interpolant = nearest_neighbour_interpolant; break; case WINDOWED_SINC: in_vol->volume->interpolant = windowed_sinc_interpolant; if (sinc_half_width < SINC_HALF_WIDTH_MIN || sinc_half_width > SINC_HALF_WIDTH_MAX) { fprintf(stderr, "Invalid sinc half-window size %d\n", sinc_half_width); exit(EXIT_FAILURE); } break; default: (void) fprintf(stderr, "Error determining interpolation type\n"); exit(EXIT_FAILURE); } /* Check min/max variables */ fp = in_vol->file; fp->using_icv=FALSE; if ((fp->maxid != MI_ERROR) && (fp->minid != MI_ERROR) && (fp->datatype!=NC_FLOAT) && (fp->datatype!=NC_DOUBLE)) { check_imageminmax(fp, in_vol->volume); } /* Get space for volume data */ total_size = 1; for (idim=0; idim < WORLD_NDIMS; idim++) { index = input_volume_def.axes[idim]; size = input_volume_def.nelements[idim]; total_size *= size; in_vol->volume->size[index] = size; } in_vol->volume->data = malloc((size_t) total_size * nctypelen(in_vol->volume->datatype)); /* Get space for slice scale and offset */ in_vol->volume->scale = malloc(sizeof(double) * in_vol->volume->size[SLC_AXIS]); in_vol->volume->offset = malloc(sizeof(double) * in_vol->volume->size[SLC_AXIS]); /* Save the program flags */ *program_flags = args.flags; /* Set the default output file datatype */ if (args.datatype == MI_ORIGINAL_TYPE) args.datatype = in_vol->file->datatype; /* Explicitly force output files to have regular spacing */ for (idim=0; idim < WORLD_NDIMS; idim++) { if (args.volume_def.coords[idim] != NULL) { free(args.volume_def.coords[idim]); args.volume_def.coords[idim] = NULL; } } /* Check to see if sign and range have been explicitly set. If not set them now */ if (args.is_signed == INT_MIN) { if (args.datatype == in_vol->file->datatype) args.is_signed = in_vol->file->is_signed; else args.is_signed = (args.datatype != NC_BYTE); } if (args.vrange[0] == -DBL_MAX) { if ((args.datatype == in_vol->file->datatype) && (args.is_signed == in_vol->file->is_signed)) { args.vrange[0] = in_vol->file->vrange[0]; args.vrange[1] = in_vol->file->vrange[1]; } else { args.vrange[0] = get_default_range(MIvalid_min, args.datatype, args.is_signed); args.vrange[1] = get_default_range(MIvalid_max, args.datatype, args.is_signed); } } /* Set up the file description for the output file */ out_vol->file = malloc(sizeof(File_Info)); out_vol->file->ndims = in_vol->file->ndims; out_vol->file->datatype = args.datatype; out_vol->file->is_signed = args.is_signed; out_vol->file->vrange[0] = args.vrange[0]; out_vol->file->vrange[1] = args.vrange[1]; for (idim=0; idim < out_vol->file->ndims; idim++) { out_vol->file->nelements[idim] = in_vol->file->nelements[idim]; out_vol->file->world_axes[idim] = in_vol->file->world_axes[idim]; } out_vol->file->keep_real_range = args.keep_real_range; /* Get space for output slice */ out_vol->volume = NULL; out_vol->slice = malloc(sizeof(Slice_Data)); /* Loop through list of axes, getting size of volume and slice */ total_size = 1; for (idim=0; idim < WORLD_NDIMS; idim++) { /* Get the index for input and output volumes */ out_vindex = args.volume_def.axes[idim]; /* 0, 1 or 2 */ out_findex = in_vol->file->indices[out_vindex]; /* 0 to ndims-1 */ size = args.volume_def.nelements[idim]; /* Update output axes and indices and nelements */ out_vol->file->nelements[out_findex] = size; out_vol->file->world_axes[out_findex] = idim; out_vol->file->axes[idim] = out_vindex; out_vol->file->indices[out_vindex] = out_findex; /* Update slice size */ if (out_vindex != 0) { out_vol->slice->size[out_vindex-1] = size; total_size *= size; } } out_vol->slice->data = malloc((size_t) total_size * sizeof(double)); /* Create the output file */ if (args.clobber) { cflags = NC_CLOBBER; } else { cflags = NC_NOCLOBBER; } #if MINC2 if (args.v2format) { cflags |= MI2_CREATE_V2; } #endif /* MINC2 */ create_output_file(outfile, cflags, &args.volume_def, in_vol->file, out_vol->file, tm_stamp, &args.transform_info); /* Save the voxel_to_world transformation information */ out_vol->voxel_to_world = malloc(sizeof(VIO_General_transform)); out_vol->world_to_voxel = malloc(sizeof(VIO_General_transform)); get_voxel_to_world_transf(&args.volume_def, out_vol->voxel_to_world); create_inverse_general_transform(out_vol->voxel_to_world, out_vol->world_to_voxel); /* Free the time stamp */ free(tm_stamp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_imageminmax @INPUT : fp - pointer to file description volume - pointer to volume description @OUTPUT : @RETURNS : (nothing) @DESCRIPTION: Routine to check that MIimagemax and MIimagemin do not vary over volume rows and columns. If they do, set up an icv to handle it. @METHOD : @GLOBALS : @CALLS : @CREATED : August 5, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void check_imageminmax(File_Info *fp, Volume_Data *volume) { int ndims, idim, dim[MAX_VAR_DIMS], imgdim[MAX_VAR_DIMS]; int ivar, varid; /* Get MIimage dimensions */ (void) ncvarinq(fp->mincid, fp->imgid, NULL, NULL, &ndims, imgdim, NULL); /* Check MIimagemax/min dimensions */ for (ivar=0; ivar<2; ivar++) { varid = (ivar==0 ? fp->maxid : fp->minid); (void) ncvarinq(fp->mincid, varid, NULL, NULL, &ndims, dim, NULL); for (idim=0; idim < ndims; idim++) { if ((dim[idim] == imgdim[fp->indices[ROW_AXIS]]) || (dim[idim] == imgdim[fp->indices[COL_AXIS]])) { fp->using_icv = TRUE; } } /* End loop over MIimagemax/min dimensions */ } /* End loop over variables MIimagemax/min */ /* Set up an icv if needed to handle values varying over slice dims. */ if (fp->using_icv) { /* Change type to floating point so that there is no loss of precision (except possibly for long values). */ if (volume->datatype != NC_DOUBLE) volume->datatype = NC_FLOAT; volume->is_signed = TRUE; /* Create the icv */ fp->icvid = miicv_create(); (void) miicv_setint(fp->icvid, MI_ICV_TYPE, volume->datatype); (void) miicv_setstr(fp->icvid, MI_ICV_SIGN, (volume->is_signed ? MI_SIGNED : MI_UNSIGNED)); (void) miicv_setint(fp->icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(fp->icvid, MI_ICV_DO_FILLVALUE, TRUE); (void) miicv_attach(fp->icvid, fp->mincid, fp->imgid); /* Get max and min for doing valid range checking */ (void) miicv_inqdbl(fp->icvid, MI_ICV_NORM_MIN, &volume->vrange[0]); (void) miicv_inqdbl(fp->icvid, MI_ICV_NORM_MAX, &volume->vrange[1]); } } /** * Scans through an array of values from an irregular dimension and * verifies that they seem reasonable. * * \todo Should this sort the values or otherwise perform a more robust * check for repeated values? We could probably afford the O(N^2) check * of searching the list for every element, but it is probably overkill. * * \param coord_ptr Pointer to the array of coordinate values. * \param length The length of the coord_ptr array. * \returns TRUE if the array seems correct. */ int check_irregular_dimension(double *coord_ptr, int length) { int i; double first_delta; if (length <= 1) { /* No further checking is possible. */ return TRUE; } if (length == 2) { /* Just verify that the values are distinct. */ return coord_ptr[0] != coord_ptr[1]; } /* Else check the beginning of the array. */ first_delta = coord_ptr[1] - coord_ptr[0]; if (first_delta == 0.0) { return FALSE; } /* Then check the rest. */ for (i = 2; i < length; i++) { double temp_delta = coord_ptr[i] - coord_ptr[i - 1]; /* If the delta is zero, we have a repeated value, which is * bad. Alternatively, if the sign changes, we have a non-monotonic * irregular dimension. This is also probably bad. */ if (temp_delta == 0.0 || (temp_delta < 0.0 && first_delta > 0.0) || (temp_delta > 0.0 && first_delta < 0.0)) { return FALSE; } } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_info @INPUT : filename - name of file to read initialized_volume_def - if TRUE, then volume_def is taken as being properly initialized and arrays are freed if non-NULL. Otherwise arrays are not freed. @OUTPUT : volume_def - description of volume file_info - description of file @RETURNS : (nothing) @DESCRIPTION: Routine to get information about the volume definition of a minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : February 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_file_info(char *filename, int initialized_volume_def, Volume_Definition *volume_def, File_Info *file_info) { int dim[MAX_VAR_DIMS], dimid; int axis_counter, idim, jdim, cur_axis; int varndims, vardim[MAX_VAR_DIMS]; long varstart, varcount, dimlength; char attstr[MI_MAX_ATTSTR_LEN]; char dimname[MAX_NC_NAME]; enum {UNKNOWN, REGULAR, IRREGULAR} coord_spacing; /* Open the minc file */ file_info->mincid = miopen(filename, NC_NOWRITE); file_info->name = filename; /* Get variable identifiers */ file_info->imgid = ncvarid(file_info->mincid, MIimage); ncopts = 0; file_info->maxid = ncvarid(file_info->mincid, MIimagemax); file_info->minid = ncvarid(file_info->mincid, MIimagemin); ncopts = NC_VERBOSE | NC_FATAL; /* Get information about datatype dimensions of variable */ (void) miget_datatype(file_info->mincid, file_info->imgid, &file_info->datatype, &file_info->is_signed); /* Get valid max and min */ (void) miget_valid_range(file_info->mincid, file_info->imgid, file_info->vrange); /* Get information about dimensions */ (void) ncvarinq(file_info->mincid, file_info->imgid, NULL, NULL, &file_info->ndims, dim, NULL); /* Set variables for keeping track of spatial dimensions */ axis_counter = 0; /* Keeps track of values for axes */ /* Initialize volume definition variables */ for (idim=0; idim < WORLD_NDIMS; idim++) { volume_def->axes[idim] = NO_AXIS; volume_def->step[idim] = 1.0; volume_def->start[idim] = 0.0; for (jdim=0; jdim < WORLD_NDIMS; jdim++) { if (jdim==idim) volume_def->dircos[idim][jdim] = 1.0; else volume_def->dircos[idim][jdim] = 0.0; } if (initialized_volume_def && (volume_def->coords[idim] != NULL)) { free(volume_def->coords[idim]); } volume_def->coords[idim] = NULL; (void) strcpy(volume_def->units[idim], "mm"); (void) strcpy(volume_def->spacetype[idim], MI_NATIVE); } /* Loop through dimensions, getting dimension information */ for (idim=0; idim < file_info->ndims; idim++) { /* Get size of dimension */ (void) ncdiminq(file_info->mincid, dim[idim], dimname, &file_info->nelements[idim]); /* Check variable name */ cur_axis = NO_AXIS; if (strcmp(dimname, MIxspace)==0) cur_axis = XAXIS; else if (strcmp(dimname, MIyspace)==0) cur_axis = YAXIS; else if (strcmp(dimname, MIzspace)==0) cur_axis = ZAXIS; /* Save world axis info */ file_info->world_axes[idim] = cur_axis; /* Check for spatial dimension */ if (cur_axis == NO_AXIS) continue; /* Set axis */ if (volume_def->axes[cur_axis] != NO_AXIS) { (void) fprintf(stderr, "Repeated spatial dimension %s in file %s.\n", dimname, filename); exit(EXIT_FAILURE); } volume_def->axes[cur_axis] = axis_counter++; /* Save spatial axis specific info */ file_info->axes[cur_axis] = volume_def->axes[cur_axis]; file_info->indices[volume_def->axes[cur_axis]] = idim; volume_def->nelements[cur_axis] = file_info->nelements[idim]; /* Check for existence of variable */ ncopts = 0; dimid = ncvarid(file_info->mincid, dimname); ncopts = NC_VERBOSE | NC_FATAL; if (dimid == MI_ERROR) continue; /* Get attributes from variable */ ncopts = 0; (void) miattget1(file_info->mincid, dimid, MIstep, NC_DOUBLE, &volume_def->step[cur_axis]); if (volume_def->step[cur_axis] == 0.0) volume_def->step[cur_axis] = 1.0; (void) miattget1(file_info->mincid, dimid, MIstart, NC_DOUBLE, &volume_def->start[cur_axis]); (void) miattget(file_info->mincid, dimid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, volume_def->dircos[cur_axis], NULL); (void) miattgetstr(file_info->mincid, dimid, MIunits, MI_MAX_ATTSTR_LEN, volume_def->units[cur_axis]); (void) miattgetstr(file_info->mincid, dimid, MIspacetype, MI_MAX_ATTSTR_LEN, volume_def->spacetype[cur_axis]); ncopts = NC_VERBOSE | NC_FATAL; /* Normalize the direction cosine */ normalize_vector(volume_def->dircos[cur_axis]); /* Look for irregular coordinates for dimension variable */ ncopts = 0; coord_spacing = UNKNOWN; dimlength = volume_def->nelements[cur_axis]; if (miattgetstr(file_info->mincid, dimid, MIspacing, MI_MAX_ATTSTR_LEN, attstr) != NULL) { if (strcmp(attstr, MI_IRREGULAR) == 0) coord_spacing = IRREGULAR; else if (strcmp(attstr, MI_REGULAR) == 0) coord_spacing = REGULAR; } if (ncvarinq(file_info->mincid, dimid, NULL, NULL, &varndims, vardim, NULL) == MI_ERROR) { ncopts = NC_VERBOSE | NC_FATAL; continue; } if ((coord_spacing != REGULAR) && (varndims == 1) && (vardim[0] == dim[idim])) { coord_spacing = IRREGULAR; } if ((coord_spacing == UNKNOWN) || (dimlength <= 1)) { coord_spacing = REGULAR; } if (coord_spacing == IRREGULAR) { volume_def->coords[cur_axis] = malloc(sizeof(double) * dimlength); varstart = 0; varcount = dimlength; if (mivarget(file_info->mincid, dimid, &varstart, &varcount, NC_DOUBLE, MI_SIGNED, volume_def->coords[cur_axis]) == MI_ERROR) { ncopts = NC_VERBOSE | NC_FATAL; free(volume_def->coords[cur_axis]); volume_def->coords[cur_axis] = NULL; continue; } if (!check_irregular_dimension(volume_def->coords[cur_axis], dimlength)) { (void) fprintf(stderr, "WARN: Irregular dimension in file %s contains non-monotonic or repeated values.\n", filename); } volume_def->start[cur_axis] = volume_def->coords[cur_axis][0]; if (dimlength > 1) { volume_def->step[cur_axis] = (volume_def->coords[cur_axis][dimlength-1] - volume_def->coords[cur_axis][0]) / (dimlength - 1); if (volume_def->step[cur_axis] == 0.0) { (void) fprintf(stderr, "WARN: Something is wrong with the irregular dimension in file %s.\n", filename); (void) fprintf(stderr, " Substituting a step size of 1.0.\n"); volume_def->step[cur_axis] = 1.0; } } } ncopts = NC_VERBOSE | NC_FATAL; } /* End of loop over dimensions */ /* Check that we have the correct number of spatial dimensions */ if (axis_counter != WORLD_NDIMS) { (void) fprintf(stderr, "Incorrect number of spatial dimensions in file %s.\n", filename); exit(EXIT_FAILURE); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_args_volume_def @INPUT : input_volume_def - description of input volume @OUTPUT : args_volume_def - description of new output volume @RETURNS : (nothing) @DESCRIPTION: Routine to copy appropriate information from input volume definition to output volume definition, overriding values not set on the command line. @METHOD : @GLOBALS : @CALLS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_args_volume_def(Volume_Definition *input_volume_def, Volume_Definition *args_volume_def) { int idim, jdim; /* Loop over dimensions */ for (idim=0; idim < WORLD_NDIMS; idim++) { /* Copy values, as needed */ if (args_volume_def->axes[idim] == NO_AXIS) args_volume_def->axes[idim] = input_volume_def->axes[idim]; if (args_volume_def->nelements[idim] == 0) args_volume_def->nelements[idim] = input_volume_def->nelements[idim]; if (args_volume_def->step[idim] == 0.0) args_volume_def->step[idim] = input_volume_def->step[idim]; if (args_volume_def->start[idim] == NO_VALUE) args_volume_def->start[idim] = input_volume_def->start[idim]; if (args_volume_def->dircos[idim][0] == NO_VALUE) { for (jdim=0; jdim < WORLD_NDIMS; jdim++) args_volume_def->dircos[idim][jdim] = input_volume_def->dircos[idim][jdim]; } if (strlen(args_volume_def->units[idim]) == 0) (void) strcpy(args_volume_def->units[idim], input_volume_def->units[idim]); if (strlen(args_volume_def->spacetype[idim]) == 0) (void) strcpy(args_volume_def->spacetype[idim], input_volume_def->spacetype[idim]); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : transform_volume_def @INPUT : transform_info - description of output to input transform (if NULL, then don't transform). input_volume_def - description of input volume @OUTPUT : transformed_volume_def - description of new output volume @RETURNS : (nothing) @DESCRIPTION: Routine to copy appropriate information from input volume definition to a new volume definition, after transformation from with the output to input transform. @METHOD : @GLOBALS : @CALLS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void transform_volume_def(Transform_Info *transform_info, Volume_Definition *input_volume_def, Volume_Definition *transformed_volume_def) { int idim, jdim; Coord_Vector origin = {0, 0, 0}; Coord_Vector transformed_origin, vector; double length, nelements; /* Copy the volume definition */ *transformed_volume_def = *input_volume_def; /* Modify things if the transform has changed */ if ((transform_info != NULL) && (transform_info->file_name != NULL)) { /* Set up origin vector */ for (idim=0; idim < WORLD_NDIMS; idim++) for (jdim=0; jdim < WORLD_NDIMS; jdim++) origin[idim] += input_volume_def->start[jdim] * input_volume_def->dircos[jdim][idim]; /* Transform origin vector */ DO_INVERSE_TRANSFORM_WITH_INPUT_STEPS(transformed_origin, transform_info->transformation, origin, input_volume_def->step); /* Loop over dimensions */ for (idim=0; idim < WORLD_NDIMS; idim++) { /* Get number of elements */ nelements = input_volume_def->nelements[idim] - 1; if (nelements < 1) nelements = 1.0; /* Transform whole axis */ VECTOR_SCALAR_MULT(vector, input_volume_def->dircos[idim], nelements * input_volume_def->step[idim]); VECTOR_ADD(vector, vector, origin); DO_INVERSE_TRANSFORM_WITH_INPUT_STEPS(vector, transform_info->transformation, vector, input_volume_def->step); VECTOR_DIFF(vector, vector, transformed_origin); /* Calculate length of transformed axis - new step value */ length = sqrt(vector[XCOORD]*vector[XCOORD] + vector[YCOORD]*vector[YCOORD] + vector[ZCOORD]*vector[ZCOORD]); transformed_volume_def->step[idim] = length / nelements; /* Calculate new direction cosines */ for (jdim=0; jdim < WORLD_NDIMS; jdim++) { transformed_volume_def->dircos[idim][jdim] = (length > 0.0 ? vector[jdim] / length : (jdim == idim ? 1.0 : 0.0)); } /* Make sure that direction cosines point the right way */ if (transformed_volume_def->dircos[idim][idim] < 0.0) { VECTOR_SCALAR_MULT(transformed_volume_def->dircos[idim], transformed_volume_def->dircos[idim], -1.0); transformed_volume_def->step[idim] *= -1.0; } } /* Calculate the start along each axis - check for bad dircos. */ if (convert_origin_to_start(transformed_origin, transformed_volume_def->dircos[XCOORD], transformed_volume_def->dircos[YCOORD], transformed_volume_def->dircos[ZCOORD], transformed_volume_def->start) != 0) { /* If dircos are bad, set them to default */ for (idim=0; idim < WORLD_NDIMS; idim++) { for (jdim=0; jdim < WORLD_NDIMS; jdim++) { transformed_volume_def->dircos[idim][jdim] = ((idim==jdim) ? 1.0 : 0.0); } } /* Try to convert point again */ if (convert_origin_to_start(transformed_origin, transformed_volume_def->dircos[XCOORD], transformed_volume_def->dircos[YCOORD], transformed_volume_def->dircos[ZCOORD], transformed_volume_def->start) != 0) { (void) fprintf(stderr, "Serious problem converting origin to start!\n"); exit(EXIT_FAILURE); } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : is_zero_vector @INPUT : vector - 3D vector @OUTPUT : (none) @RETURNS : 1 if vector has zero length, 0 otherwise @DESCRIPTION: Routine to check for a zero length vector. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int is_zero_vector(double vector[]) { return ((vector[0] == 0.0) && (vector[1] == 0.0) && (vector[2] == 0.0)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : normalize_vector @INPUT : vector - 3D vector @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to normalize a vector @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void normalize_vector(double vector[]) { int idim; double magnitude; /* Normalize the direction cosine */ 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; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_output_file @INPUT : filename - name of file to create cflags - flag creation flags volume_def - description of volume in_file - description of input file out_file - description of output file tm_stamp - string describing program invocation transformation - transformation to be applied to data @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to create an minc output file and set up its parameters properly. @METHOD : @GLOBALS : @CALLS : @CREATED : February 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void create_output_file(char *filename, int cflags, Volume_Definition *volume_def, File_Info *in_file, File_Info *out_file, char *tm_stamp, Transform_Info *transform_info) { int ndims, in_dims[MAX_VAR_DIMS], out_dims[MAX_VAR_DIMS]; int out_maxmin_dims[MAX_VAR_DIMS]; char dimname[MAX_NC_NAME]; int nmaxmin_dims, nimage_dims, axis, idim, dimid, varid, itrans; int att_length; nc_type datatype; int nexcluded, excluded_vars[10]; char *string; int dim_exists, is_volume_dimension; int in_index, out_index; /* Save the file name */ out_file->name = filename; /* Create the list of excluded variables */ nexcluded = 0; ncopts = 0; if ((varid=ncvarid(in_file->mincid, MIxspace)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(in_file->mincid, MIyspace)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(in_file->mincid, MIzspace)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(in_file->mincid, MIimage)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(in_file->mincid, MIimagemax)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(in_file->mincid, MIimagemin)) != MI_ERROR) excluded_vars[nexcluded++] = varid; #if MINC2 if ((varid=ncvarid(in_file->mincid, MIvector_dimension)) != MI_ERROR) excluded_vars[nexcluded++] = varid; #endif /* MINC2 */ ncopts = NC_VERBOSE | NC_FATAL; /* Create the file */ out_file->mincid = micreate(filename, cflags); /* Copy all other variable definitions */ (void) micopy_all_var_defs(in_file->mincid, out_file->mincid, nexcluded, excluded_vars); /* Add the time stamp */ ncopts=0; if ((ncattinq(out_file->mincid, NC_GLOBAL, MIhistory, &datatype, &att_length) == MI_ERROR) || (datatype != NC_CHAR)) att_length = 0; att_length += strlen(tm_stamp) + 1; string = malloc(att_length); string[0] = '\0'; (void) miattgetstr(out_file->mincid, NC_GLOBAL, MIhistory, att_length, string); ncopts=NC_VERBOSE | NC_FATAL; (void) strcat(string, tm_stamp); (void) miattputstr(out_file->mincid, NC_GLOBAL, MIhistory, string); free(string); /* Get the dimension ids from the input file */ (void) ncvarinq(in_file->mincid, in_file->imgid, NULL, NULL, &ndims, in_dims, NULL); /* Check for screw-up on number of dimensions */ if (ndims != out_file->ndims) { (void) fprintf(stderr, "Error in number of dimensions for output file.\n"); exit(EXIT_FAILURE); } /* Create the dimensions for the image variable */ for (out_index=0; out_indexworld_axes[out_index] != NO_AXIS); /* Get the input index */ if (!is_volume_dimension) in_index = out_index; else { axis = out_file->world_axes[out_index]; if ((axis<0) || (axis>=WORLD_NDIMS)) { (void) fprintf(stderr, "Error creating dimensions for output file.\n"); exit(EXIT_FAILURE); } in_index = in_file->indices[in_file->axes[axis]]; } /* Get the dimension name from the input file */ (void) ncdiminq(in_file->mincid, in_dims[in_index], dimname, NULL); /* Check to see if the dimension already exists */ ncopts = 0; out_dims[out_index] = ncdimid(out_file->mincid, dimname); ncopts = NC_VERBOSE | NC_FATAL; dim_exists = (out_dims[out_index] != MI_ERROR); /* If we have a volume dimension and it exists already with the wrong size, then we must rename it */ if (is_volume_dimension && dim_exists && (out_file->nelements[out_index] != in_file->nelements[in_index])) { string = malloc(MAX_NC_NAME); ncopts = 0; idim = 0; do { (void) sprintf(string, "%s%d", dimname, idim); idim++; } while (ncdimid(out_file->mincid, string) != MI_ERROR); ncopts = NC_VERBOSE | NC_FATAL; (void) ncdimrename(out_file->mincid, out_dims[out_index], string); free(string); out_dims[out_index] = ncdimdef(out_file->mincid, dimname, out_file->nelements[out_index]); } else if (!dim_exists) out_dims[out_index] = ncdimdef(out_file->mincid, dimname, out_file->nelements[out_index]); /* If this is a volume dimension, create a variable */ if (is_volume_dimension) { /* Create the variable */ dimid = micreate_group_variable(out_file->mincid, dimname); (void) miattputdbl(out_file->mincid, dimid, MIstep, volume_def->step[axis]); (void) miattputdbl(out_file->mincid, dimid, MIstart, volume_def->start[axis]); (void) ncattput(out_file->mincid, dimid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, volume_def->dircos[axis]); (void) miattputstr(out_file->mincid, dimid, MIunits, volume_def->units[axis]); (void) miattputstr(out_file->mincid, dimid, MIspacetype, volume_def->spacetype[axis]); } /* If volume dimension */ } /* Loop over dimensions */ /* Create the image max and min variables. These do not vary over the volume rows and columns even if they are not image dimensions, so we have to copy down the elements of the array (excluding image dimensions). Compute number of slices (row,columns) in an image (minc file def'n) and number of images in the file. */ nimage_dims = 2; ncdiminq(out_file->mincid, out_dims[ndims-1], dimname, NULL); if (strcmp(dimname, MIvector_dimension)==0) nimage_dims++; nmaxmin_dims = 0; out_file->slices_per_image = 1; out_file->images_per_file = 1; for (idim=0; idimindices[COL_AXIS]) && (idim != out_file->indices[ROW_AXIS])) { if (idim < ndims-nimage_dims) { out_maxmin_dims[nmaxmin_dims] = out_dims[idim]; nmaxmin_dims++; out_file->images_per_file *= out_file->nelements[idim]; } else { out_file->slices_per_image *= out_file->nelements[idim]; } } } out_file->do_slice_renormalization = ((out_file->datatype != NC_FLOAT) && (out_file->datatype != NC_DOUBLE) && (out_file->slices_per_image > 1)); /* Create the variables */ out_file->maxid = micreate_std_variable(out_file->mincid, MIimagemax, NC_DOUBLE, nmaxmin_dims, out_maxmin_dims); if (in_file->maxid != MI_ERROR) (void) micopy_all_atts(in_file->mincid, in_file->maxid, out_file->mincid, out_file->maxid); out_file->minid = micreate_std_variable(out_file->mincid, MIimagemin, NC_DOUBLE, nmaxmin_dims, out_maxmin_dims); if (in_file->minid != MI_ERROR) (void) micopy_all_atts(in_file->mincid, in_file->minid, out_file->mincid, out_file->minid); /* Add transformation information to image processing variable if a transformation is given on the command line */ if (transform_info->file_name != NULL) { ncopts = 0; /* Get id of processing variable (create it if needed) */ varid = ncvarid(out_file->mincid, PROCESSING_VAR); if (varid == MI_ERROR) { varid = ncvardef(out_file->mincid, PROCESSING_VAR, NC_INT, 0, NULL); (void) miadd_child(out_file->mincid, ncvarid(out_file->mincid, MIrootvariable), varid); } /* Look for an unused transformation attribute */ string = malloc(MI_MAX_ATTSTR_LEN); itrans = 0; do { (void) sprintf(string, "transformation%d-filename", itrans); itrans++; } while (ncattinq(out_file->mincid, varid, string, NULL, NULL) != MI_ERROR); itrans--; /* Reset error handling */ ncopts = NC_VERBOSE | NC_FATAL; /* Add the attributes describing the transformation */ (void) miattputstr(out_file->mincid, varid, string, transform_info->file_name); (void) sprintf(string, "transformation%d-filedata", itrans); (void) miattputstr(out_file->mincid, varid, string, transform_info->file_contents); if (transform_info->invert_transform) { (void) sprintf(string, "transformation%d-inverted", itrans); (void) miattputstr(out_file->mincid, varid, string, MI_TRUE); } free(string); } /* If transform specified on command line */ /* Create the image variable */ out_file->imgid = micreate_std_variable(out_file->mincid, MIimage, out_file->datatype, ndims, out_dims); (void) micopy_all_atts(in_file->mincid, in_file->imgid, out_file->mincid, out_file->imgid); (void) miattputstr(out_file->mincid, out_file->imgid, MIcomplete, MI_FALSE); (void) miset_valid_range(out_file->mincid, out_file->imgid, out_file->vrange); if (out_file->is_signed) (void) miattputstr(out_file->mincid, out_file->imgid, MIsigntype, MI_SIGNED); else (void) miattputstr(out_file->mincid, out_file->imgid, MIsigntype, MI_UNSIGNED); /* Get into data mode */ (void) ncsetfill(out_file->mincid, NC_NOFILL); (void) ncendef(out_file->mincid); /* Copy all the other data */ (void) micopy_all_var_values(in_file->mincid, out_file->mincid, nexcluded, excluded_vars); /* Create and attach an icv */ out_file->using_icv = TRUE; out_file->icvid = miicv_create(); (void) miicv_setint(out_file->icvid, MI_ICV_TYPE, NC_DOUBLE); (void) miicv_setint(out_file->icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(out_file->icvid, MI_ICV_USER_NORM, TRUE); (void) miicv_attach(out_file->icvid, out_file->mincid, out_file->imgid); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_voxel_to_world_transf @INPUT : volume_def - description of volume @OUTPUT : voxel_to_world - transformation @RETURNS : (nothing) @DESCRIPTION: Routine to convert a Volume definition specification of sampling to a voxel-to-world transformation @METHOD : @GLOBALS : @CALLS : @CREATED : February 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_voxel_to_world_transf(Volume_Definition *volume_def, VIO_General_transform *voxel_to_world) { int idim, jdim, cur_dim, vol_axis; int irregular; long ielement, dimlength; VIO_Transform matrix; Irregular_Transform_Data *irreg_transf_data; VIO_General_transform linear_transf, irreg_transf; /* Make an identity matrix */ make_identity_transform(&matrix); /* Loop over rows of matrix */ for (idim=0; idimaxes[jdim]; /* Get rotation/scale components of matrix */ Transform_elem(matrix, idim, cur_dim) = volume_def->step[jdim] * volume_def->dircos[jdim][idim]; /* Get translation components */ Transform_elem(matrix, idim, VOL_NDIMS) += volume_def->start[jdim] * volume_def->dircos[jdim][idim]; } } /* Save the general transform */ create_linear_transform(voxel_to_world, &matrix); /* Check for an irregularly spaced dimension */ irregular = FALSE; for (idim=0; idim < WORLD_NDIMS; idim++) { if (volume_def->coords[idim] != NULL) irregular = TRUE; } /* If we have an irregularly spaced dimension, then create the appropriate transform */ if (irregular) { irreg_transf_data = malloc(sizeof(*irreg_transf_data)); /* Loop through the axes */ for (idim=0; idim < WORLD_NDIMS; idim++) { vol_axis = volume_def->axes[idim]; irreg_transf_data->last_index[vol_axis] = 0; /* Check whether the axis is irregularly spaced or not */ if (volume_def->coords[idim] == NULL) { irreg_transf_data->nelements[vol_axis] = 0; irreg_transf_data->coords[vol_axis] = NULL; } else { /* If irregular then get the number of elements and allocate space */ dimlength = volume_def->nelements[idim]; irreg_transf_data->nelements[vol_axis] = dimlength; irreg_transf_data->coords[vol_axis] = malloc(sizeof(double) * dimlength); /* Normalize the coordinates to give first coord 0 and last coord n-1 (so that we can concat with the linear transform already created */ for (ielement=0; ielement < dimlength; ielement++) { irreg_transf_data->coords[vol_axis][ielement] = (volume_def->coords[idim][ielement] - volume_def->start[idim]) / volume_def->step[idim]; } } } /* Create an irregular transform and free the data (we only free the Irregular_Transform_Data structure, not the coord pointers, since these arrays are not copied) */ create_user_transform(&irreg_transf, (void *) irreg_transf_data, sizeof(*irreg_transf_data), irregular_transform_function, irregular_inverse_transform_function); free(irreg_transf_data); /* Concatenate the linear transform with the irregular transform */ copy_general_transform(voxel_to_world, &linear_transf); delete_general_transform(voxel_to_world); concat_general_transforms(&irreg_transf, &linear_transf, voxel_to_world); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : irregular_transform_function @INPUT : user_data - pointer to user data x, y, z - coordinate to transform @OUTPUT : x_trans, y_trans, z_trans - resulting coordinate @RETURNS : (nothin) @DESCRIPTION: Routine to transform irregularly spaced coordinate to a regular spacing. @METHOD : @GLOBALS : @CALLS : @CREATED : November 4, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void irregular_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) { Irregular_Transform_Data *irreg_transf_data; int idim; long dimlength, index; double coord[VOL_NDIMS], coord_transf[VOL_NDIMS]; double frac; /* Get the pointer to the data */ irreg_transf_data = (Irregular_Transform_Data *) user_data; /* Get the coordinate */ coord[0] = x; coord[1] = y; coord[2] = z; /* Loop through axes, renormalizing coordinate */ for (idim=0; idim < VOL_NDIMS; idim++) { dimlength = irreg_transf_data->nelements[idim]; if (dimlength <= 1) { coord_transf[idim] = coord[idim]; } else { /* For irregular dimension, do linear interpolation between coords */ index = (long) coord[idim]; if (index < 0) index = 0; if (index > dimlength-2) index = dimlength-2; frac = coord[idim] - index; coord_transf[idim] = (1.0 - frac) * irreg_transf_data->coords[idim][index] + frac * irreg_transf_data->coords[idim][index + 1]; } } /* Save the coordinates */ *x_trans = coord_transf[0]; *y_trans = coord_transf[1]; *z_trans = coord_transf[2]; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : irregular_inverse_transform_function @INPUT : user_data - pointer to user data x, y, z - coordinate to transform @OUTPUT : x_trans, y_trans, z_trans - resulting coordinate @RETURNS : (nothin) @DESCRIPTION: Routine to transform irregularly spaced coordinate to a regular spacing. @METHOD : @GLOBALS : @CALLS : @CREATED : November 4, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void irregular_inverse_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) { Irregular_Transform_Data *irreg_transf_data; int idim, not_found; long dimlength, index; double coord[VOL_NDIMS], coord_transf[VOL_NDIMS]; double first, next, step, frac; /* Get the pointer to the data */ irreg_transf_data = (Irregular_Transform_Data *) user_data; /* Get the coordinate */ coord[0] = x; coord[1] = y; coord[2] = z; /* Loop through axes, renormalizing coordinate */ for (idim=0; idim < VOL_NDIMS; idim++) { dimlength = irreg_transf_data->nelements[idim]; if (dimlength <= 1) { coord_transf[idim] = coord[idim]; } else { /* Search for the closest index (checking for out-of-range values) */ index = irreg_transf_data->last_index[idim]; if (index < 0) index = 0; if (index > dimlength-2) index = dimlength-2; not_found = TRUE; while (not_found) { if (coord[idim] < irreg_transf_data->coords[idim][index]) { if (index > 0) index--; else { index = 0; not_found = FALSE; } } else if (coord[idim] > irreg_transf_data->coords[idim][index+1]) { if (index < dimlength-2) index++; else { index = dimlength-2; not_found = FALSE; } } else { not_found = FALSE; } } irreg_transf_data->last_index[idim] = index; /* Do linear interpolation */ first = irreg_transf_data->coords[idim][index]; next = irreg_transf_data->coords[idim][index + 1]; step = next - first; if (step == 0.0) frac = 0.0; else frac = (coord[idim] - first) / step; coord_transf[idim] = (1.0 - frac) * index + frac * (index + 1); } } /* Save the coordinates */ *x_trans = coord_transf[0]; *y_trans = coord_transf[1]; *z_trans = coord_transf[2]; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_range @INPUT : what - MIvalid_min means get default min, MIvalid_min means get default min datatype - type of variable is_signed - TRUE if variable is signed @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 : @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : February 10, 1993 (Peter Neelin) - ripped off from MINC code ---------------------------------------------------------------------------- */ static double get_default_range(char *what, nc_type datatype, int is_signed) { double limit; if (strcmp(what, MIvalid_max)==0) { switch (datatype) { case NC_INT: limit = (is_signed) ? INT_MAX : UINT_MAX; break; case NC_SHORT: limit = (is_signed) ? SHRT_MAX : USHRT_MAX; break; case NC_BYTE: limit = (is_signed) ? SCHAR_MAX : UCHAR_MAX; break; default: limit = DEFAULT_MAX; break; } } else if (strcmp(what, MIvalid_min)==0) { switch (datatype) { case NC_INT: limit = (is_signed) ? INT_MIN : 0; break; case NC_SHORT: limit = (is_signed) ? SHRT_MIN : 0; break; case NC_BYTE: limit = (is_signed) ? SCHAR_MIN : 0; break; default: limit = DEFAULT_MIN; break; } } else { limit = 0.0; } return limit; } /* ----------------------------- MNI Header ----------------------------------- @NAME : finish_up @INPUT : in_vol - input volume out_vol - output volume @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to finish up at end of program, closing files, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void finish_up(VVolume *in_vol, VVolume *out_vol) { File_Info *in_file, *out_file; /* Get file info pointers */ in_file = in_vol->file; out_file = out_vol->file; /* Close the output file */ (void) miattputstr(out_file->mincid, out_file->imgid, MIcomplete, MI_TRUE); if (out_file->using_icv) { (void) miicv_free(out_file->icvid); } (void) miclose(out_file->mincid); /* Close the input file */ if (in_file->using_icv) { (void) miicv_free(in_file->icvid); } (void) miclose(in_file->mincid); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transformation @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE so that ParseArgv will discard nextArg, unless there is no following argument. @DESCRIPTION: Routine called by ParseArgv to read in a transformation file @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_transformation(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Transform_Info *transform_info; VIO_General_transform *transformation; FILE *fp; int ch, index; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Get pointer to transform info structure */ transform_info = (Transform_Info *) dst; /* Save file name */ transform_info->file_name = nextArg; transformation = transform_info->transformation; /* Open the file */ if (strcmp(nextArg, "-") == 0) { /* Create a temporary for standard input */ fp=tmpfile(); if (fp==NULL) { (void) fprintf(stderr, "Error opening temporary file.\n"); exit(EXIT_FAILURE); } while ((ch=getc(stdin))!=EOF) (void) putc(ch, fp); rewind(fp); } else { if (open_file_with_default_suffix(nextArg, get_default_transform_file_suffix(), READ_FILE, ASCII_FORMAT, &fp) != VIO_OK) { (void) fprintf(stderr, "Error opening transformation file %s.\n", nextArg); exit(EXIT_FAILURE); } } /* Read in the file for later use */ if (transform_info->file_contents == NULL) { transform_info->file_contents = malloc(TRANSFORM_BUFFER_INCREMENT); transform_info->buffer_length = TRANSFORM_BUFFER_INCREMENT; } for (index = 0; (ch=getc(fp)) != EOF; index++) { if (index >= transform_info->buffer_length-1) { transform_info->buffer_length += TRANSFORM_BUFFER_INCREMENT; transform_info->file_contents = realloc(transform_info->file_contents, transform_info->buffer_length); } transform_info->file_contents[index] = (char) ch; } transform_info->file_contents[index] = '\0'; rewind(fp); /* Get rid of the old transformation */ delete_general_transform(transformation); /* Read the file */ if (input_transform(fp, transform_info->file_name, transformation)!=VIO_OK) { (void) fprintf(stderr, "Error reading transformation file.\n"); exit(EXIT_FAILURE); } (void) close_file(fp); #ifdef TRANSFORM_CHANGE_KLUDGE Specified_transform = TRUE; #endif return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_model_file @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE so that ParseArgv will discard nextArg unless there is no following argument. @DESCRIPTION: Routine called by ParseArgv to read in a model file (-like) @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_model_file(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; File_Info file; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Get pointer to volume definition structure */ volume_def = (Volume_Definition *) dst; /* Get file information */ get_file_info(nextArg, TRUE, volume_def, &file); /* Close the file */ (void) miclose(file.mincid); #ifdef TRANSFORM_CHANGE_KLUDGE Specified_like = TRUE; #endif return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_standard_sampling @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : FALSE so that ParseArgv will not discard nextArg. @DESCRIPTION: Routine called by ParseArgv to set the sampling to standard values (sets only step, start and direction cosines). @METHOD : @GLOBALS : @CALLS : @CREATED : November 14, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int set_standard_sampling(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; int idim, jdim; /* Get pointer to volume definition structure */ volume_def = (Volume_Definition *) dst; /* Set sensible values */ for (idim=0; idim < WORLD_NDIMS; idim++) { volume_def->step[idim] = 1.0; volume_def->start[idim] = 0.0; for (jdim=0; jdim < WORLD_NDIMS; jdim++) { volume_def->dircos[idim][jdim] = (idim == jdim ? 1.0 : 0.0); } } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_spacetype @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE if nextArg should be discarded, FALSE otherwise @DESCRIPTION: Routine called by ParseArgv to set the space type of the output sampling. @METHOD : @GLOBALS : @CALLS : @CREATED : December 12, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int set_spacetype(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; char *spacetype; int idim; int return_value; /* Get pointer to client data */ volume_def = (Volume_Definition *) dst; /* Check key for spacetype */ return_value = FALSE; if (strcmp(key, "-talairach") == 0) { spacetype = MI_TALAIRACH; } else { /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } spacetype = nextArg; return_value = TRUE; } /* Copy the strings */ for (idim=0; idim < WORLD_NDIMS; idim++) { (void) strncpy(volume_def->spacetype[idim], spacetype, MI_MAX_ATTSTR_LEN); volume_def->spacetype[idim][MI_MAX_ATTSTR_LEN-1] = '\0'; } return return_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_units @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE if nextArg should be discarded, FALSE otherwise @DESCRIPTION: Routine called by ParseArgv to set the units of the output sampling. @METHOD : @GLOBALS : @CALLS : @CREATED : December 12, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int set_units(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; char *units; int idim; /* Get pointer to client data */ volume_def = (Volume_Definition *) dst; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } units = nextArg; /* Copy the strings */ for (idim=0; idim < WORLD_NDIMS; idim++) { (void) strncpy(volume_def->units[idim], units, MI_MAX_ATTSTR_LEN); volume_def->units[idim][MI_MAX_ATTSTR_LEN-1] = '\0'; } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_axis_order @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : FALSE so that ParseArgv will not discard nextArg @DESCRIPTION: Routine called by ParseArgv to get the axis order @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_axis_order(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Volume_Definition *volume_def; /* Get pointer to client data */ volume_def = (Volume_Definition *) dst; /* Check key for order */ if (strcmp(key, "-transverse") == 0) { volume_def->axes[VIO_Z] = 0; volume_def->axes[VIO_Y] = 1; volume_def->axes[VIO_X] = 2; } else if (strcmp(key, "-sagittal") == 0) { volume_def->axes[VIO_X] = 0; volume_def->axes[VIO_Z] = 1; volume_def->axes[VIO_Y] = 2; } else if (strcmp(key, "-coronal") == 0) { volume_def->axes[VIO_Y] = 0; volume_def->axes[VIO_Z] = 1; volume_def->axes[VIO_X] = 2; } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_fillvalue @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : FALSE so that ParseArgv will not discard nextArg @DESCRIPTION: Routine called by ParseArgv to set the fill value @METHOD : @GLOBALS : @CALLS : @CREATED : February 15, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_fillvalue(char *dst, char *key, char *nextArg) /* ARGSUSED */ { double *dptr; /* Get pointer to client data */ dptr = (double *) dst; /* Check key for fill value to set */ if (strcmp(key, "-fill") == 0) { *dptr = -DBL_MAX; } else if (strcmp(key, "-nofill") == 0) { *dptr = FILL_DEFAULT; } return FALSE; } minc-tools-2.3.00+dfsg/progs/mincresample/mincresample.h0000644000175000000620000003407112574624760022270 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincresample.h @DESCRIPTION: Header file for mincresample.c @METHOD : @GLOBALS : @CALLS : @CREATED : February 8, 1993 (Peter Neelin) @MODIFIED : * $Log: mincresample.h,v $ * Revision 6.8 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.7 2005/07/13 21:34:25 bert * Add sinc interpolant (ported from 1.X branch) * * Revision 6.6 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.5 2004/04/27 15:31:20 bert * Added -2 option * * Revision 6.4 2002/11/06 13:32:23 jason * Fixes to mincresample: setting the interpolation type is now done * through an enum rather than function pointers. * * Revision 6.3 2001/04/17 18:40:23 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/02 14:58:09 neelin * Added -keep_real_range option to prevent rescaling of slices on output * * Revision 6.1 1999/10/19 14:45:27 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:21 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:22 neelin * Release of minc version 0.5 * * Revision 4.1 1997/08/13 15:41:12 neelin * Fixed initialization problem that caused crashing under Linux. * * Revision 4.0 1997/05/07 19:59:42 neelin * Release of minc version 0.4 * * Revision 3.4 1996/01/31 15:22:02 neelin * Fixed bug in transformation of input sampling. * * Revision 3.3 1995/12/12 19:15:35 neelin * Added -spacetype, -talairach and -units options. * * Revision 3.2 1995/11/21 14:13:20 neelin * Transform input sampling with transformation and use this as default. * Added -tfm_input_sampling to specify above option. * Added -use_input_sampling to get old behaviour (no longer the default). * Added -origin option (to specify coordinate instead of start values). * Added -standard_sampling option (to set standard values of start, step * and direction cosines). * Added -invert_transformation option. * * Revision 3.1 1995/11/07 15:04:02 neelin * Modified argument parsing so that only one pass is done. * * Revision 3.0 1995/05/15 19:30:57 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:32:48 neelin * Release of minc version 0.2 * * Revision 1.11 94/09/28 10:32:40 neelin * Pre-release * * Revision 1.10 93/11/04 15:13:40 neelin * Added support for irregularly spaced dimensions. * * Revision 1.9 93/11/02 11:23:56 neelin * Handle imagemax/min potentially varying over slices (for vector data, etc.) * * Revision 1.8 93/10/20 14:05:42 neelin * Added VOXEL_COORD_EPS - an epsilon for doing voxel coordinate comparisons. * * Revision 1.7 93/08/11 14:31:50 neelin * Changed prototype for check_imageminmax. * * Revision 1.6 93/08/11 13:34:20 neelin * Converted to use Dave MacDonald's VIO_General_transform code. * Fixed bug in get_slice - for non-linear transformations coord was * transformed, then used again as a starting coordinate. * Handle files that have image-max/min that doesn't vary over slices. * Handle files that have image-max/min varying over row/cols. * Allow volume to extend to voxel edge for -nearest_neighbour interpolation. * Handle out-of-range values (-fill values from a previous mincresample, for * example). * Save transformation file as a string attribute to processing variable. * @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. ---------------------------------------------------------------------------- */ /* Constants used in program */ /* Number of dimensions for various things */ #define VOL_NDIMS 3 /* Number of volume dimensions */ #define WORLD_NDIMS 3 /* Number of world spatial dimensions */ #define SLICE_NDIMS 2 /* Number of slice dimensions */ #define MAT_NDIMS WORLD_NDIMS+1 /* Number of dims for homogenous matrix */ /* For referring to world axes in arrays subscripted by WORLD_NDIMS */ #define NO_AXIS -1 #define XAXIS 0 #define YAXIS 1 #define ZAXIS 2 /* For referring to volume axes in arrays subscripted by VOL_NDIMS */ #define SLC_AXIS 0 #define ROW_AXIS 1 #define COL_AXIS 2 /* For referring to slice axes in arrays subscripted by SLICE_NDIMS */ #define SLICE_ROW 0 #define SLICE_COL 1 /* For referring to world coordinates in Coord_Vector */ #define XCOORD 0 #define YCOORD 1 #define ZCOORD 2 /* For referring to volume coordinates in Coord_Vector */ #define SLICE 0 #define ROW 1 #define COLUMN 2 /* Various constants */ #define NO_VALUE DBL_MAX /* Constant to flag fact that value not set */ #define DEFAULT_MAX 1.0 #define DEFAULT_MIN 0.0 #define FILL_DEFAULT DBL_MAX /* Fillvalue indicating -nofill */ #define SMALL_VALUE (100.0*FLT_MIN) /* A small floating-point value */ #define VOXEL_COORD_EPS (100.0*FLT_EPSILON) /* Epsilon for voxel coords */ #define TRANSFORM_BUFFER_INCREMENT 256 #define PROCESSING_VAR "processing" #define TEMP_IMAGE_VAR "mincresample-temporary-image" #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Types used in program */ enum Interpolant_type { TRILINEAR, TRICUBIC, N_NEIGHBOUR, WINDOWED_SINC }; typedef double Coord_Vector[WORLD_NDIMS]; typedef struct { char *name; int mincid; int imgid; int maxid; int minid; int ndims; nc_type datatype; int is_signed; double vrange[2]; /* [0]=min, [1]=max */ long nelements[MAX_VAR_DIMS]; /* Size of each dimension */ int world_axes[MAX_VAR_DIMS]; /* Relates variable index to X, Y, Z or NO_AXIS */ int indices[VOL_NDIMS]; /* Indices of volume dimenions (subscripted from slowest to fastest) */ int axes[WORLD_NDIMS]; /* Relates world X,Y,Z (index) to dimension order (value=0,1,2; 0=slowest varying) */ int using_icv; /* True if we are using an icv to read data */ int icvid; /* Id of icv (if used) */ long slices_per_image; /* Number of volume slices (row, column) per minc file image */ long images_per_file; /* Number of minc file images in the file */ int do_slice_renormalization; /* Flag indicating that we need to loop through the data a second time, recomputing the slices to normalize images properly */ int keep_real_range; /* Flag indicating whether we should keep the real range of the input data or not */ } File_Info; typedef struct { long size[SLICE_NDIMS]; /* Size of each dimension */ double *data; /* Pointer to slice data */ } Slice_Data; typedef struct Volume_Data_Struct Volume_Data; typedef int (*Interpolating_Function) (Volume_Data *volume, Coord_Vector coord, double *result); struct Volume_Data_Struct { nc_type datatype; /* Type of data in volume */ int is_signed; /* Sign of data (TRUE if signed) */ int use_fill; /* TRUE if fill values should be used in calculation of output image max/min */ double fillvalue; /* Value to return when out of bounds */ double vrange[2]; /* [0]=min, [1]=max */ double real_range[2]; /* Real min and max for current volume */ int size[VOL_NDIMS]; /* Size of each dimension */ void *data; /* Pointer to volume data */ double *scale; /* Pointer to array of scales for slices */ double *offset; /* Pointer to array of offsets for slices */ Interpolating_Function interpolant; /* Function Pointer */ }; typedef struct { File_Info *file; /* Information about associated file */ Volume_Data *volume; /* Volume data for (input volume) */ Slice_Data *slice; /* Slice data for (output volume) */ VIO_General_transform *voxel_to_world; VIO_General_transform *world_to_voxel; } VVolume; typedef struct { int axes[WORLD_NDIMS]; /* Relates world X,Y,Z (index) to dimension order (value=0,1,2; 0=slowest varying) */ long nelements[WORLD_NDIMS]; /* These are subscripted by X, Y and Z */ double step[WORLD_NDIMS]; double start[WORLD_NDIMS]; double dircos[WORLD_NDIMS][WORLD_NDIMS]; double *coords[WORLD_NDIMS]; char units[WORLD_NDIMS][MI_MAX_ATTSTR_LEN]; char spacetype[WORLD_NDIMS][MI_MAX_ATTSTR_LEN]; } Volume_Definition; typedef struct { int verbose; } Program_Flags; typedef struct { int invert_transform; char *file_name; char *file_contents; long buffer_length; VIO_General_transform *transformation; } Transform_Info; typedef struct { int clobber; int keep_real_range; nc_type datatype; int is_signed; double vrange[2]; double fillvalue; double origin[3]; Program_Flags flags; enum Interpolant_type interpolant_type; /* Type of interpolation */ Transform_Info transform_info; Volume_Definition volume_def; int v2format; /* If non-zero, create a MINC 2.0 output */ } Arg_Data; typedef struct { long last_index[VOL_NDIMS]; long nelements[VOL_NDIMS]; double *coords[VOL_NDIMS]; } Irregular_Transform_Data; /* Macros used in program */ #define DO_TRANSFORM(result, transformation, coord) \ general_transform_point(transformation, \ coord[XCOORD], coord[YCOORD], coord[ZCOORD], \ &result[XCOORD], &result[YCOORD], &result[ZCOORD]) #define DO_TRANSFORM_WITH_INPUT_STEPS(result, transformation, coord, input_volume_steps) \ general_transform_point_with_input_steps(transformation, \ coord[XCOORD], coord[YCOORD], coord[ZCOORD], input_volume_steps, \ &result[XCOORD], &result[YCOORD], &result[ZCOORD]) #define DO_INVERSE_TRANSFORM(result, transformation, coord) \ general_inverse_transform_point(transformation, \ coord[XCOORD], coord[YCOORD], coord[ZCOORD], \ &result[XCOORD], &result[YCOORD], &result[ZCOORD]) #define DO_INVERSE_TRANSFORM_WITH_INPUT_STEPS(result, transformation, coord, input_volume_steps) \ general_inverse_transform_point_with_input_steps(transformation, \ coord[XCOORD], coord[YCOORD], coord[ZCOORD], input_volume_steps, \ &result[XCOORD], &result[YCOORD], &result[ZCOORD]) #define IS_LINEAR(transformation) \ (get_transform_type(transformation)==LINEAR) #define VECTOR_COPY(result, first) { \ result[XCOORD] = first[XCOORD]; \ result[YCOORD] = first[YCOORD]; \ result[ZCOORD] = first[ZCOORD]; \ } #define VECTOR_DIFF(result, first, second) { \ result[XCOORD] = first[XCOORD] - second[XCOORD]; \ result[YCOORD] = first[YCOORD] - second[YCOORD]; \ result[ZCOORD] = first[ZCOORD] - second[ZCOORD]; \ } #define VECTOR_ADD(result, first, second) { \ result[XCOORD] = first[XCOORD] + second[XCOORD]; \ result[YCOORD] = first[YCOORD] + second[YCOORD]; \ result[ZCOORD] = first[ZCOORD] + second[ZCOORD]; \ } #define VECTOR_SCALAR_MULT(result, vector, scalar) { \ result[XCOORD] = vector[XCOORD] * (scalar); \ result[YCOORD] = vector[YCOORD] * (scalar); \ result[ZCOORD] = vector[ZCOORD] * (scalar); \ } #ifdef INTERPOLATE # undef INTERPOLATE #endif #define INTERPOLATE(volume, coord, result) \ (*volume->interpolant) (volume, coord, result) #define VOLUME_VALUE(volume, slcind, rowind, colind, value) \ { \ long offset; \ \ offset = ((slcind)*volume->size[ROW_AXIS] + \ (rowind))*volume->size[COL_AXIS] + (colind); \ switch (volume->datatype) { \ case NC_BYTE: \ if (volume->is_signed) \ value = *((signed char *) volume->data + offset); \ else \ value = *((unsigned char *) volume->data + offset); \ break; \ case NC_SHORT: \ if (volume->is_signed) \ value = *((signed short *) volume->data + offset); \ else \ value = *((unsigned short *) volume->data + offset); \ break; \ case NC_INT: \ if (volume->is_signed) \ value = *((signed int *) volume->data + offset); \ else \ value = *((unsigned int *) volume->data + offset); \ break; \ case NC_FLOAT: \ value = *((float *) volume->data + offset); \ break; \ case NC_DOUBLE: \ value = *((double *) volume->data + offset); \ break; \ } \ } /* Function prototypes */ extern void resample_volumes(Program_Flags *program_flags, VVolume *in_vol, VVolume *out_vol, VIO_General_transform *transformation); extern int trilinear_interpolant(Volume_Data *volume, Coord_Vector coord, double *result); extern int tricubic_interpolant(Volume_Data *volume, Coord_Vector coord, double *result); extern int nearest_neighbour_interpolant(Volume_Data *volume, Coord_Vector coord, double *result); extern int windowed_sinc_interpolant(Volume_Data *volume, Coord_Vector coord, double *result); #define SINC_HALF_WIDTH_MAX 10 #define SINC_HALF_WIDTH_MIN 1 enum sinc_interpolant_window_t { SINC_WINDOW_NONE, SINC_WINDOW_HANNING, SINC_WINDOW_HAMMING }; extern enum sinc_interpolant_window_t sinc_window_type; extern int sinc_half_width; minc-tools-2.3.00+dfsg/progs/minctoraw/0002755000175000000620000000000012574624760016761 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minctoraw/minctoraw.man10000644000175000000620000000267312574624760021550 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCTORAW 1 "$Date: 2004-05-20 21:52:09 $" "" "MINC User's Guide" .SH NAME minctoraw \- copy data from a MINC file. .SH SYNOPSIS .B minctoraw .BI [options] .BI mincfile .SH DESCRIPTION \fIminctoraw\fR dumps a chunk of MINC file data to standard output in the format of your choice. .P This program is largely superceded by \fImincextract\fR. .SH OPTIONS .TP \fB\-byte\fR Write out data as 8-bit integers .TP \fB\-short\fR Write out data as 16-bit integers .TP \fB\-int\fR Write out data as 32-bit integers .TP \fB\-long\fR Superseded by \fB\-int\fR .TP \fB\-float\fR Write out data as single precision floating-point values .TP \fB\-double\fR Write out data as double precision floating-point values .TP \fB\-signed\fR Write out signed data (applies only to integer types) .TP \fB\-unsigned\fR Write out unsigned data (applies only to integer types) .TP \fB\-range\fR \fIlow\ high\fR Specify the range of output values .TP \fB\-normalize\fR Normalize integer pixel values to file max and min (Default) .TP \fB\-nonormalize\fR Turn off pixel normalization .TP \fB\-big-endian\fR Request big-endian (most significant byte first) format. .TP \fB\-little-endian\fR Request little-endian (least significant byte first) format. .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH "SEE ALSO" .LP .BR mincextract (1) minc-tools-2.3.00+dfsg/progs/minctoraw/minctoraw.c0000644000175000000620000002352412574624760021134 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minctoraw @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to dump minc file data @METHOD : @GLOBALS : @CALLS : @CREATED : February 11, 1993 (Peter Neelin) @MODIFIED : * $Log: minctoraw.c,v $ * Revision 6.12 2008-01-17 02:33:06 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/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.10 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.9 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.8 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.7 2003/10/28 20:32:09 bert * Get rid of a few compiler warnings * * Revision 6.6 2001/08/16 16:41:36 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 16:18:47 neelin * Fixed typo for compile * * Revision 6.4 2001/08/16 16:17:22 neelin * Added hint about normalization in error message of previous change. * * Revision 6.3 2001/08/16 16:10:50 neelin * Force user to specify either -normalize or -nonormalize. * * Revision 6.2 2001/04/17 18:40:25 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:30 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:26 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:27 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:01 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:01 neelin * Release of minc version 0.3 * * Revision 2.2 1995/01/23 09:05:31 neelin * changed ncclose to miclose * * Revision 2.1 95/01/23 09:03:19 neelin * Changed ncopen to miopen. * * Revision 2.0 94/09/28 10:33:06 neelin * Release of minc version 0.2 * * Revision 1.8 94/09/28 10:33:03 neelin * Pre-release * * Revision 1.7 93/08/11 15:23:15 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define VIO_BOOL_DEFAULT -1 /* Variables used for argument parsing */ static int output_datatype = INT_MAX; static int output_signed = INT_MAX; static int output_endian = MINC_NATIVE_ENDIAN; static double valid_range[2] = {DBL_MAX, DBL_MAX}; static int normalize_output = VIO_BOOL_DEFAULT; /* Argument table */ static ArgvInfo argTable[] = { {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &output_datatype, "Write out data as bytes"}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &output_datatype, "Write out data as short integers"}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &output_datatype, "Write out data as 32-bit integers"}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &output_datatype, "Superseded by -int"}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &output_datatype, "Write out data as single precision floating-point values"}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &output_datatype, "Write out data as double precision floating-point values"}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &output_signed, "Write out signed data"}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &output_signed, "Write out unsigned data"}, {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Specify the range of output values"}, {"-normalize", ARGV_CONSTANT, (char *) TRUE, (char *) &normalize_output, "Normalize integer pixel values to file max and min"}, {"-nonormalize", ARGV_CONSTANT, (char *) FALSE, (char *) &normalize_output, "Turn off pixel normalization"}, {"-big-endian", ARGV_CONSTANT, (char *) MINC_BIG_ENDIAN, (char *) &output_endian, "Force big-endian output." }, {"-little-endian", ARGV_CONSTANT, (char *) MINC_LITTLE_ENDIAN, (char *) &output_endian, "Force little-endian output." }, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *filename; int mincid, imgid, icvid, ndims, dims[MAX_VAR_DIMS]; nc_type datatype; int is_signed; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS], end[MAX_VAR_DIMS]; size_t size; int idim; void *data; double temp; minc_swap_fn_t swap_fn = NULL; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 2)) { (void) fprintf(stderr, "\nUsage: %s [] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } filename = argv[1]; /* Check that a normalization option was specified */ if (normalize_output == VIO_BOOL_DEFAULT) { (void) fprintf(stderr, "Please specify either -normalize or -nonormalize\n"); (void) fprintf(stderr, "Usually -normalize is most appropriate\n"); exit(EXIT_FAILURE); } /* Open the file */ mincid = miopen(filename, NC_NOWRITE); /* Inquire about the image variable */ imgid = ncvarid(mincid, MIimage); (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dims, NULL); (void)miget_datatype(mincid, imgid, &datatype, &is_signed); /* Check if arguments set */ /* Get output data type */ if (output_datatype == INT_MAX) output_datatype = datatype; /* Get output sign */ if (output_signed == INT_MAX) { if (output_datatype == datatype) output_signed = is_signed; else output_signed = (output_datatype != NC_BYTE); } /* Get output range */ if (valid_range[0] == DBL_MAX) { if ((output_datatype == datatype) && (output_signed == is_signed)) { (void) miget_valid_range(mincid, imgid, valid_range); } else { (void) miget_default_range(output_datatype, output_signed, valid_range); } } if (valid_range[0] > valid_range[1]) { temp = valid_range[0]; valid_range[0] = valid_range[1]; valid_range[1] = temp; } /* Set up image conversion */ icvid = miicv_create(); (void) miicv_setint(icvid, MI_ICV_TYPE, output_datatype); (void) miicv_setstr(icvid, MI_ICV_SIGN, (output_signed ? MI_SIGNED : MI_UNSIGNED)); (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, valid_range[0]); (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, valid_range[1]); if ((output_datatype == NC_FLOAT) || (output_datatype == NC_DOUBLE)) { (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); } else if (normalize_output) { (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); } (void) miicv_attach(icvid, mincid, imgid); /* Set input file start, count and end vectors for reading a slice at a time */ for (idim=0; idim < ndims; idim++) { (void) ncdiminq(mincid, dims[idim], NULL, &end[idim]); } (void) miset_coords(ndims, (long) 0, start); (void) miset_coords(ndims, (long) 1, count); size = nctypelen(output_datatype); for (idim=ndims-2; idim < ndims; idim++) { count[idim] = end[idim]; size *= count[idim]; } /* Allocate space */ data = malloc(size); /* Figure out if we have to swap bytes. */ swap_fn = minc_get_swap_function(output_endian, nctypelen(output_datatype)); /* Loop over input slices */ while (start[0] < end[0]) { /* Read in the slice */ (void) miicv_get(icvid, start, count, data); if (swap_fn != NULL) { (*swap_fn)(data, size); } /* Write out the slice */ if (fwrite(data, sizeof(char), (size_t) size, stdout) != size) { (void) fprintf(stderr, "Error writing data.\n"); exit(EXIT_FAILURE); } /* Increment start counter */ idim = ndims-1; start[idim] += count[idim]; while ( (idim>0) && (start[idim] >= end[idim])) { start[idim] = 0; idim--; start[idim] += count[idim]; } } /* End loop over slices */ /* Clean up */ (void) miclose(mincid); (void) miicv_free(icvid); free(data); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/minccalc/0002755000175000000620000000000012574624760016527 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minccalc/vector.c0000644000175000000620000000164712574624760020203 0ustar stevestaff/* Copyright David Leonard and Andrew Janke, 2000. All rights reserved. */ #include #include #include "node.h" #define INIT_SIZE 20 vector_t new_vector(){ vector_t v; v = malloc(sizeof *v); v->maxlen = INIT_SIZE; v->el = malloc(INIT_SIZE * sizeof v->el[0]); v->len = 0; v->refcnt = 1; return v; } void vector_incr_ref(vector_t v) { v->refcnt++; } void vector_append(vector_t v, scalar_t s){ if (v->len + 1 == v->maxlen) { v->maxlen *= 2; v->el = realloc(v->el, v->maxlen * sizeof v->el[0]); } v->el[v->len++] = s; scalar_incr_ref(s); } void vector_free(vector_t v){ int i; if (v->refcnt <= 0) { (void) fprintf(stderr, "Internal error: vector freed too often\n"); exit(1); } if (--v->refcnt == 0) { for (i=0; i < v->len; i++) { scalar_free(v->el[i]); } free(v->el); v->el = NULL; free(v); } } minc-tools-2.3.00+dfsg/progs/minccalc/node.h0000644000175000000620000000530112574624760017622 0ustar stevestaff#include "errx.h" struct node; struct scalar; struct vector; struct sym; typedef int ident_t; typedef struct node *node_t; typedef struct scalar *scalar_t; typedef struct vector *vector_t; typedef struct sym *sym_t; #define SCALAR_ROUND(s) (floor(s + 0.5)) struct scalar { int width; double *vals; int refcnt; }; struct vector { int len; scalar_t *el; int maxlen; int refcnt; }; enum nodetype { NODETYPE_ADD, NODETYPE_SUB, NODETYPE_MUL, NODETYPE_DIV, NODETYPE_POW, NODETYPE_INDEX, NODETYPE_SUM, NODETYPE_PROD, NODETYPE_AVG, NODETYPE_LEN, NODETYPE_MAX, NODETYPE_MIN, NODETYPE_IMAX, NODETYPE_IMIN, NODETYPE_IDENT, NODETYPE_REAL, NODETYPE_LET, NODETYPE_VEC1, NODETYPE_VEC2, NODETYPE_GEN, NODETYPE_RANGE, NODETYPE_LT, NODETYPE_LE, NODETYPE_GT, NODETYPE_GE, NODETYPE_EQ, NODETYPE_NE, NODETYPE_NOT, NODETYPE_AND, NODETYPE_OR, NODETYPE_ISNAN, NODETYPE_SQRT, NODETYPE_ABS, NODETYPE_EXP, NODETYPE_LOG, NODETYPE_SIN, NODETYPE_COS, NODETYPE_TAN, NODETYPE_ASIN, NODETYPE_ACOS, NODETYPE_ATAN, NODETYPE_CLAMP, NODETYPE_SEGMENT, NODETYPE_EXPRLIST, NODETYPE_ASSIGN, NODETYPE_IFELSE, NODETYPE_FOR }; #define RANGE_EXACT_UPPER 1 #define RANGE_EXACT_LOWER 2 #define ALLARGS_SCALAR 4 #define NODE_IS_SCALAR 8 struct node { enum nodetype type; node_t expr[3]; ident_t ident; int flags; double real; int pos; int numargs; }; ident_t new_ident(const char *); const char *ident_str(ident_t); int ident_is_scalar(ident_t); ident_t ident_lookup(char *string); node_t new_node(int, int); node_t new_scalar_node(int); node_t new_vector_node(int); const char * node_name(node_t); int node_is_scalar(node_t); node_t optimize(node_t); vector_t new_vector(void); void vector_append(vector_t, scalar_t); void vector_free(vector_t); void vector_incr_ref(vector_t); scalar_t new_scalar(int); void scalar_free(scalar_t); void scalar_incr_ref(scalar_t); sym_t sym_enter_scope(sym_t sym); void sym_leave_scope(sym_t sym); void sym_declare_ident(ident_t id, sym_t sym); void sym_set_scalar(int, int *, scalar_t, ident_t, sym_t); void sym_set_vector(int, int *, vector_t, ident_t, sym_t); scalar_t sym_lookup_scalar(ident_t id, sym_t sym); vector_t sym_lookup_vector(ident_t id, sym_t sym); void lex_init(const char *); void lex_finalize(void); scalar_t eval_scalar(int, int *, node_t, sym_t); void show_error(int, const char *); int yyparse(void); int yylex(void); extern node_t root; minc-tools-2.3.00+dfsg/progs/minccalc/minccalc.c0000644000175000000620000005542612574624760020456 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- minccalc.c A expression parser that works voxel-by-voxel for minc files Andrew Janke - a.janke@gmail.com Center for Magnetic Resonance University of Queensland Original Grammar and parser by David Leonard - leonard@csee.uq.edu.au Department of Computer Science University of Queensland Modifications by Peter Neelin - neelin@bic.mni.mcgill.ca McConnell Brain Imaging Centre Montreal Neurological Institute McGill University This is predominately a rehash of mincmath by Peter Neelin * $Log: minccalc.c,v $ * Revision 1.19 2010-03-27 15:24:03 rotor * * added outfile checks (with clobber) in minccalc * * Revision 1.18 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 1.17 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 1.16 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.15 2008/01/11 04:24:16 rotor * * updated all my email addresses * * removed a (very) outdated TODO file * * Revision 1.14 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 1.13 2005/08/26 21:07:16 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 1.12 2004/12/14 23:52:23 bert * Get rid of compilation warnings w/c99 * * Revision 1.11 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 1.10 2004/06/11 20:55:37 bert * Fix for nasty bug which causes lots of bogus zero values to be inserted when minccalc is used with a file with a vector_dimension * * Revision 1.9 2004/04/27 15:37:52 bert * Added -2 option * * Revision 1.8 2001/05/24 15:08:40 neelin * Added support for comments so that minccalc scripts can be created. * * Revision 1.7 2001/05/04 15:40:33 neelin * Added -outfile option. * Changed syntax of for to use curlies around first part. * Changed syntax of for and if to evaluate an expression in the body, rather * than an expression list in curlies. * * Revision 1.6 2001/05/02 16:27:19 neelin * Fixed handling of invalid values. Added NaN constant. Force copy of * data when assigning to a symbol so that s1 = s2 = expr does the right * thing (s1 and s2 should be separate copies of the data). Updated man * page. * * Revision 1.5 2001/05/02 01:38:15 neelin * Major changes to allow parallel evaluations. Created scalar_t type * along the lines of vector_t (reference counting, etc.). Compiles * and runs on basic standard deviation calculation (with test for invalid * data). Gives over 3 times speedup compared to old version (on linux box). * SD calculation is slightly under half the speed of mincaverage. * Changes are significant enough and testing is little enough that there * are probably lots of bugs left. * * Revision 1.4 2001/04/30 19:16:43 neelin * Added assignment operator, made symbol table global, added expression lists, * for loops, if operators and changed range operator to colon. * * Revision 1.3 2001/04/26 19:12:39 neelin * Finished up addition of operators and handling of invalid values. * This version seems to work. * * Revision 1.2 2001/04/24 18:17:09 neelin * Added CVS logging. * Thu Dec 21 17:26:46 EST 2000 - Added use of voxel_loop Thu Oct 5 17:09:12 EST 2000 - First alpha version Mon May 28 01:00:01 EST 2000 - First minc version - Andrew Janke Mon May 21 01:01:01 EST 2000 - Original version "imgcalc" by David Leonard ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "node.h" /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Data values for invalid data and for uninitialized data */ #define INVALID_DATA -DBL_MAX #define UNINITIALIZED_DATA DBL_MAX /* Values for representing default case for command-line options */ #define DEFAULT_DBL DBL_MAX #define DEFAULT_BOOL -1 /* Function prototypes */ static void do_math(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static char *read_expression_file(char *filename); static int get_list_option(char *dst, char *key, int argc, char **argv); /* Argument variables */ static int Output_list_size = 0; static int Output_list_alloc = 0; struct { char *symbol; char *file; } *Output_list = NULL; static int clobber = FALSE; static int verbose = TRUE; int debug = FALSE; static int is_signed = FALSE; int propagate_nan = TRUE; static int check_dim_info = TRUE; static int copy_all_header = DEFAULT_BOOL; static int use_nan_for_illegal_values = TRUE; static int max_buffer_size_in_kb = 0; static double valid_range[2] = {0.0, 0.0}; double value_for_illegal_operations = DEFAULT_DBL; static nc_type datatype = MI_ORIGINAL_TYPE; static char *filelist = NULL; static char *expr_file = NULL; char *expression = NULL; static int eval_width = 200; #if MINC2 static int minc2_format = FALSE; #endif /* MINC2 */ /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "General options:"}, #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format, "Produce a MINC 2.0 format output file"}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-no_clobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Synonym for -noclobber."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-debug", ARGV_CONSTANT, (char *) TRUE, (char *) &debug, "Print out debugging messages."}, {"-filelist", ARGV_STRING, (char *) 1, (char *) &filelist, "Specify the name of a file containing input file names (- for stdin)."}, {"-copy_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all of the header from the first file."}, {"-nocopy_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Do not copy all of the header from the first file."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {"-max_buffer_size_in_kb", ARGV_INT, (char *) 1, (char *) &max_buffer_size_in_kb, "Specify the maximum size of the internal buffers (in kbytes)."}, {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE, (char *) &check_dim_info, "Check that files have matching dimensions (default)."}, {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE, (char *) &check_dim_info, "Do not check that files have matching dimensions."}, {"-ignore_nan", ARGV_CONSTANT, (char *) FALSE, (char *) &propagate_nan, "Ignore invalid data (NaN) for accumulations."}, {"-propagate_nan", ARGV_CONSTANT, (char *) TRUE, (char *) &propagate_nan, "Invalid data in any file at a voxel produces a NaN (default)."}, {"-nan", ARGV_CONSTANT, (char *) TRUE, (char *) &use_nan_for_illegal_values, "Output NaN when an illegal operation is done (default)."}, {"-zero", ARGV_CONSTANT, (char *) FALSE, (char *) &use_nan_for_illegal_values, "Output zero when an illegal operation is done."}, {"-illegal_value", ARGV_FLOAT, (char *) 1, (char *) &value_for_illegal_operations, "Value to write out when an illegal operation is done."}, {"-expression", ARGV_STRING, (char*)1, (char*) &expression, "Expression to use in calculations."}, {"-expfile", ARGV_STRING, (char*)1, (char*) &expr_file, "Name of file containing expression."}, {"-outfile", ARGV_GENFUNC, (char*)get_list_option, (char*) &Output_list, "Symbol to save in an output file (2 args)."}, {"-eval_width", ARGV_INT, (char*)1, (char*) &eval_width, "Number of voxels to evaluate simultaneously."}, {NULL, ARGV_END, NULL, NULL, NULL} }; extern int yydebug; sym_t rootsym; vector_t A; scalar_t *Output_values; /* Main program */ int main(int argc, char *argv[]){ char **infiles, **outfiles; int nfiles, nout; char *arg_string; Loop_Options *loop_options; char *pname; int i; ident_t ident; scalar_t scalar; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { (void) fprintf(stderr, "\nUsage: %s [options] [ ...] \n", pname); (void) fprintf(stderr, " %s -help\n\n", pname); exit(EXIT_FAILURE); } /* Get output file names */ nout = (Output_list == NULL ? 1 : Output_list_size); outfiles = malloc(nout * sizeof(*outfiles)); if (Output_list == NULL) { outfiles[0] = argv[argc-1]; } else { for (i=0; i < Output_list_size; i++) { outfiles[i] = Output_list[i].file; } } /* check for output files */ for (i=0; i < nout; i++){ if(access(outfiles[i], F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists, use -clobber to overwrite\n\n", argv[0], outfiles[i]); exit(EXIT_FAILURE); } } /* Get the list of input files either from the command line or from a file, or report an error if both are specified. Note that if -outfile is given then there is no output file name left on argv after option parsing. */ nfiles = argc - 2; if (Output_list != NULL) nfiles++; if (filelist == NULL) { infiles = &argv[1]; } else if (nfiles <= 0) { infiles = read_file_names(filelist, &nfiles); if (infiles == NULL) { (void) fprintf(stderr, "Error reading in file names from file \"%s\"\n", filelist); exit(EXIT_FAILURE); } } else { (void) fprintf(stderr, "Do not specify both -filelist and input file names\n"); exit(EXIT_FAILURE); } /* Make sure that we have something to process */ if (nfiles == 0) { (void) fprintf(stderr, "No input files specified\n"); exit(EXIT_FAILURE); } /* Get the expression from the file if needed */ if ((expression == NULL) && (expr_file == NULL)) { (void) fprintf(stderr, "An expression must be specified on the command line\n"); exit(EXIT_FAILURE); } else if (expression == NULL) { expression = read_expression_file(expr_file); } /* Parse expression argument */ if (debug) fprintf(stderr, "Feeding in expression %s\n", expression); lex_init(expression); if (debug) yydebug = 1; else yydebug = 0; yyparse(); lex_finalize(); /* Optimize the expression tree */ root = optimize(root); /* Setup the input vector from the input files */ A = new_vector(); for (i=0; i total_values) nvox = total_values - ivox; /* Copy the data into the A vector */ for (ivalue=0; ivalue < nvox; ivalue++) { for (ibuff=0; ibuff < input_num_buffers; ibuff++){ A->el[ibuff]->vals[ivalue] = input_data[ibuff][ivox+ivalue]; } } /* Some debugging */ if (debug) { (void) fprintf(stderr, "\n===New voxel===\n"); } /* Evaluate the expression */ scalar = eval_scalar((int) nvox, NULL, root, rootsym); /* Get the list of scalar values to write out */ if (Output_values == NULL) { num_output = 1; output_scalars = &scalar; } else { num_output = Output_list_size; output_scalars = Output_values; } /* Copy the scalar values into the right buffers */ for (iout=0; iout < num_output; iout++) { for (ivalue=0; ivalue < nvox; ivalue++) { output_data[iout][ivox+ivalue] = output_scalars[iout]->vals[ivalue]; } } /* Free things up */ scalar_free(scalar); if (debug) { (void) printf("Voxel result = %g\n", output_data[0][ivox]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_expression_file @INPUT : filename - Name of file from which to read expression @OUTPUT : (none) @RETURNS : String containing expression - must be freed by caller. @DESCRIPTION: Reads in an expression from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : May 3, 2001 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static char *read_expression_file(char *filename) { struct stat statbuf; size_t size; FILE *fp; char *expression; size_t ichar; int beginning_of_line, in_comment; int ch; #define ALLOC_SIZE 1024 /* Set the default allocation size - zero means allocate as we go */ size = 0; /* Check for reading from stdin */ if (strcmp(filename, "-") == 0) { fp = stdin; } /* Otherwise read from file. Get allocation size from file size. */ else { /* Get the file size */ if (stat(filename, &statbuf) >= 0) { size = statbuf.st_size + 1; } /* Open the file */ if ((fp=fopen(filename, "r")) == NULL) { (void) fprintf(stderr, "Unable to open expression file \"%s\"\n", filename); exit(EXIT_FAILURE); } } /* Make sure that we are going to allocate something */ if (size == 0) size = ALLOC_SIZE; /* Get space */ expression = malloc(size * sizeof(*expression)); /* Read the expression */ ichar = 0; beginning_of_line = TRUE; in_comment = FALSE; while ((ch = getc(fp)) != EOF) { /* Check for newline to end comments */ if (ch == '\n') { beginning_of_line = TRUE; in_comment = FALSE; } /* Check for comment character as first non-whitespace char of line */ else if (beginning_of_line && (ch == '#')) { in_comment = TRUE; beginning_of_line = FALSE; } /* Check for first non-whitespace char of line */ else if (!isspace(ch)) { beginning_of_line = FALSE; } /* If not in a comment, then save the character */ if (!in_comment) { /* Check whether we need more space */ if (ichar >= size-1) { size += ALLOC_SIZE; expression = realloc(expression, size * sizeof(expression)); } /* Save the character */ expression[ichar] = (char) ch; ichar++; } } expression[ichar] = '\0'; /* Close the file */ if (fp != stdin) { (void) fclose(fp); } /* Return the expression */ return expression; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_list_option @INPUT : dst - client data passed by ParseArgv key - matching key in argv argc - number of arguments passed in argv - argument list @OUTPUT : (none) @RETURNS : Number of arguments left in argv list. @DESCRIPTION: Gets arguments from the command line and appends them to a list, chosen based on key. @METHOD : @GLOBALS : @CALLS : @CREATED : May 3, 2001 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_list_option(char *dst, char *key, int argc, char **argv) /* ARGSUSED */ { enum {OPT_OUTPUT_SYMBOL} option_type; void **list; size_t entry_size; int *list_size, *list_alloc, index; int num_args, iarg; /* Check the key */ list = (void **) dst; if (strcmp(key, "-outfile") == 0) { option_type = OPT_OUTPUT_SYMBOL; list_size = &Output_list_size; list_alloc = &Output_list_alloc; entry_size = sizeof(Output_list[0]); num_args = 2; } else { (void) fprintf(stderr, "Internal error - unrecognized key in get_list_option\n"); exit(EXIT_FAILURE); } /* Check for following arguments */ if (argc < num_args) { (void) fprintf(stderr, "\"%s\" option requires %d additional arguments\n", key, num_args); exit(EXIT_FAILURE); } /* Get more space */ (*list_size)++; if (*list_size > *list_alloc) { *list_alloc += 10; if (*list == NULL) { *list = malloc(*list_alloc * entry_size); } else { *list = realloc(*list, *list_alloc * entry_size); } } index = *list_size - 1; /* Save the values */ if (option_type == OPT_OUTPUT_SYMBOL) { Output_list[index].symbol = argv[0]; Output_list[index].file = argv[1]; } /* Modify the argument list */ if (num_args > 0) { for (iarg=0; iarg < (argc - num_args); iarg++) { argv[iarg] = argv[iarg + num_args]; } } return argc - num_args; } minc-tools-2.3.00+dfsg/progs/minccalc/sym.c0000644000175000000620000001243712574624760017510 0ustar stevestaff/* Copyright David Leonard and Andrew Janke, 2000. All rights reserved. */ #include #include #include "node.h" #define NEW_SCOPE (-1) typedef enum {SYM_UNKNOWN, SYM_SCALAR, SYM_VECTOR} sym_type_t; struct sym { ident_t ident; sym_type_t type; scalar_t scalar; vector_t vector; sym_t next; }; static sym_t new_sym(ident_t id, sym_t sym){ sym_t newsym; newsym = malloc(sizeof *newsym); newsym->ident = id; newsym->type = SYM_UNKNOWN; /* We put new scope nodes at the head of the list. For regular symbols, we add the symbol to the head of the list only in the case where the head of the current symbol table is not a new scope. */ if ((id == NEW_SCOPE) || (sym==NULL) || (sym->ident != NEW_SCOPE)) { newsym->next = sym; } /* Usually for regular symbols, we add the symbol to an existing scope, so we put it as the second element of the list and return the head of the list. */ else { newsym->next = sym->next; sym->next = newsym; } return newsym; } sym_t sym_enter_scope(sym_t sym) { sym_t newsym = new_sym(NEW_SCOPE, sym); return newsym; } void sym_leave_scope(sym_t sym) { sym_t osym; if (sym==NULL) return; do { osym = sym->next; if (sym->type == SYM_VECTOR) { vector_free(sym->vector); } else if (sym->type == SYM_SCALAR) { scalar_free(sym->scalar); } free(sym); sym = osym; } while ((sym != NULL) && (sym->ident != NEW_SCOPE)); return; } void sym_declare_ident(ident_t id, sym_t sym) { (void) new_sym(id, sym); return; } static sym_t sym_lookup(ident_t id, sym_t sym){ while (sym) { if (sym->ident == id) break; sym = sym->next; } return sym; } void sym_set_scalar(int width, int *eval_flags, scalar_t sc, ident_t id, sym_t sym){ int ivalue; /* Find the symbol - it is does not exist make a new one */ sym_t newsym = sym_lookup(id, sym); if (newsym == NULL) { newsym = new_sym(id, sym); } /* Make sure that any existing one is of the right type */ if (newsym->type == SYM_VECTOR) { /* errx(1, "%s is not a scalar", ident_str(id)); */ fprintf(stderr, "%s is not a scalar(lowercase)\n", ident_str(id)); exit(1); } /* Create a new scalar if needed */ if (newsym->type == SYM_UNKNOWN || newsym->scalar->width < width) { if (newsym->type == SYM_SCALAR) scalar_free(newsym->scalar); newsym->type = SYM_SCALAR; newsym->scalar = new_scalar(width); } /* Copy in the values */ for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; newsym->scalar->vals[ivalue] = sc->vals[ivalue]; } return; } void sym_set_vector(int width, int *eval_flags, vector_t v, ident_t id, sym_t sym){ int ivalue, iel; scalar_t sc; /* Find the symbol - it is does not exist make a new one */ sym_t newsym = sym_lookup(id, sym); if (newsym == NULL) { newsym = new_sym(id, sym); } /* Make sure that any existing one is of the right type */ if (newsym->type == SYM_SCALAR) { /* errx(1, "%s is not a vector", ident_str(id)); */ fprintf(stderr, "%s is not a vector\n", ident_str(id)); exit(1); } /* Create a new vector if needed - either it does not exist or the length is changing or the width is increasing*/ if (newsym->type == SYM_UNKNOWN || newsym->vector->len != v->len || (newsym->vector->len > 0 && newsym->vector->el[0]->width < width) ) { /* Free an existing vector. If eval_flags is set, then we cannot change the length of the vector */ if (newsym->type == SYM_VECTOR) { if (eval_flags != NULL && newsym->vector->len != v->len) { /* errx(1, "assigned vector must match length of %s in if", ident_str(id)); */ fprintf(stderr, "assigned vector must match length of %s in if", ident_str(id)); exit(1); } vector_free(newsym->vector); } newsym->type = SYM_VECTOR; newsym->vector = new_vector(); for (iel=0; iel < v->len; iel++) { sc = new_scalar(width); vector_append(newsym->vector, sc); scalar_free(sc); } } /* Copy in the values */ for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; for (iel=0; iel < v->len; iel++) { newsym->vector->el[iel]->vals[ivalue] = v->el[iel]->vals[ivalue]; } } return; } scalar_t sym_lookup_scalar(ident_t id, sym_t sym){ sym_t s = sym_lookup(id, sym); if (!s) { /* errx(1, "%s undefined", ident_str(id)); */ fprintf(stderr, "%s undefined\n", ident_str(id)); exit(1); } if (s->type != SYM_SCALAR) { /* errx(1, "%s is not scalar (lowercase)", ident_str(id)); */ fprintf(stderr, "%s is not scalar (lowercase)\n", ident_str(id)); exit(1); } return s->scalar; } vector_t sym_lookup_vector(ident_t id, sym_t sym){ sym_t s = sym_lookup(id, sym); if (!s) { /* errx(1, "%s undefined", ident_str(id)); */ fprintf(stderr, "%s undefined\n", ident_str(id)); exit(1); } if (s->type != SYM_VECTOR) return NULL; return s->vector; } minc-tools-2.3.00+dfsg/progs/minccalc/gram.c0000644000175000000620000023530212574624760017624 0ustar stevestaff /* A Bison parser, made by GNU Bison 2.4.1. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 189 of yacc.c */ #line 1 "gram.y" #include #include #include #include #include #include "node.h" #define INVALID_VALUE (-DBL_MAX) /* Avoid problems with conflicting declarations */ void yyerror(const char *msg); /* Line 189 of yacc.c */ #line 88 "progs/minccalc/gram.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 1 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { NAN = 258, IN = 259, TO = 260, IDENT = 261, REAL = 262, AVG = 263, PROD = 264, SUM = 265, LET = 266, NEG = 267, LEN = 268, MAX = 269, MIN = 270, IMAX = 271, IMIN = 272, ISNAN = 273, SQRT = 274, ABS = 275, EXP = 276, LOG = 277, SIN = 278, COS = 279, TAN = 280, ASIN = 281, ACOS = 282, ATAN = 283, CLAMP = 284, SEGMENT = 285, LT = 286, LE = 287, GT = 288, GE = 289, EQ = 290, NE = 291, NOT = 292, AND = 293, OR = 294, IF = 295, ELSE = 296, FOR = 297 }; #endif /* Tokens. */ #define NAN 258 #define IN 259 #define TO 260 #define IDENT 261 #define REAL 262 #define AVG 263 #define PROD 264 #define SUM 265 #define LET 266 #define NEG 267 #define LEN 268 #define MAX 269 #define MIN 270 #define IMAX 271 #define IMIN 272 #define ISNAN 273 #define SQRT 274 #define ABS 275 #define EXP 276 #define LOG 277 #define SIN 278 #define COS 279 #define TAN 280 #define ASIN 281 #define ACOS 282 #define ATAN 283 #define CLAMP 284 #define SEGMENT 285 #define LT 286 #define LE 287 #define GT 288 #define GE 289 #define EQ 290 #define NE 291 #define NOT 292 #define AND 293 #define OR 294 #define IF 295 #define ELSE 296 #define FOR 297 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { /* Line 214 of yacc.c */ #line 15 "gram.y" int pos; node_t node; double real; ident_t ident; /* Line 214 of yacc.c */ #line 217 "progs/minccalc/gram.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 229 "progs/minccalc/gram.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 69 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 800 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 61 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 6 /* YYNRULES -- Number of rules. */ #define YYNRULES 63 /* YYNRULES -- Number of states. */ #define YYNSTATES 152 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 297 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 48, 45, 43, 56, 44, 51, 46, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 59, 58, 2, 52, 2, 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 49, 2, 50, 53, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 54, 57, 55, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint8 yyprhs[] = { 0, 0, 3, 5, 9, 12, 14, 18, 22, 26, 34, 40, 46, 52, 58, 62, 66, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 118, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 194, 203, 209, 217, 223, 231, 233, 235, 237, 243, 249, 251 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 62, 0, -1, 63, -1, 64, 58, 63, -1, 64, 58, -1, 64, -1, 47, 64, 48, -1, 54, 63, 55, -1, 49, 66, 50, -1, 54, 6, 4, 64, 57, 64, 55, -1, 47, 64, 59, 64, 48, -1, 47, 64, 59, 64, 50, -1, 49, 64, 59, 64, 48, -1, 49, 64, 59, 64, 50, -1, 64, 43, 64, -1, 64, 44, 64, -1, 44, 64, -1, 64, 45, 64, -1, 64, 46, 64, -1, 64, 53, 64, -1, 64, 31, 64, -1, 64, 32, 64, -1, 64, 33, 64, -1, 64, 34, 64, -1, 64, 35, 64, -1, 64, 36, 64, -1, 64, 38, 64, -1, 64, 39, 64, -1, 64, 49, 64, 50, -1, 6, 52, 64, -1, 11, 65, -1, 37, 64, -1, 10, 64, -1, 9, 64, -1, 8, 64, -1, 13, 64, -1, 14, 64, -1, 15, 64, -1, 16, 64, -1, 17, 64, -1, 18, 64, -1, 19, 64, -1, 20, 64, -1, 21, 64, -1, 22, 64, -1, 23, 64, -1, 24, 64, -1, 25, 64, -1, 26, 64, -1, 27, 64, -1, 28, 64, -1, 29, 47, 64, 56, 64, 56, 64, 48, -1, 30, 47, 64, 56, 64, 56, 64, 48, -1, 64, 60, 64, 59, 64, -1, 40, 47, 64, 48, 64, 41, 64, -1, 40, 47, 64, 48, 64, -1, 42, 54, 6, 4, 64, 55, 64, -1, 6, -1, 7, -1, 3, -1, 6, 52, 64, 56, 65, -1, 6, 52, 64, 4, 64, -1, 64, -1, 66, 56, 64, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 53, 53, 57, 64, 67, 71, 74, 77, 80, 88, 96, 104, 112, 120, 128, 136, 146, 154, 162, 170, 178, 186, 194, 202, 210, 218, 226, 234, 241, 248, 251, 258, 264, 270, 276, 282, 288, 294, 300, 306, 313, 320, 327, 334, 341, 348, 355, 362, 369, 376, 383, 392, 401, 409, 417, 424, 432, 438, 444, 453, 460, 469, 474 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "NAN", "IN", "TO", "IDENT", "REAL", "AVG", "PROD", "SUM", "LET", "NEG", "LEN", "MAX", "MIN", "IMAX", "IMIN", "ISNAN", "SQRT", "ABS", "EXP", "LOG", "SIN", "COS", "TAN", "ASIN", "ACOS", "ATAN", "CLAMP", "SEGMENT", "LT", "LE", "GT", "GE", "EQ", "NE", "NOT", "AND", "OR", "IF", "ELSE", "FOR", "'+'", "'-'", "'*'", "'/'", "'('", "')'", "'['", "']'", "'.'", "'='", "'^'", "'{'", "'}'", "','", "'|'", "';'", "':'", "'?'", "$accept", "top", "exprlist", "expr", "letexpr", "vector", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 43, 45, 42, 47, 40, 41, 91, 93, 46, 61, 94, 123, 125, 44, 124, 59, 58, 63 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 61, 62, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 66, 66 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 3, 2, 1, 3, 3, 3, 7, 5, 5, 5, 5, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 5, 7, 5, 7, 1, 1, 1, 5, 5, 1, 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 59, 57, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 0, 34, 33, 32, 0, 30, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 0, 31, 0, 0, 16, 0, 62, 0, 57, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 29, 0, 0, 0, 0, 0, 6, 0, 0, 8, 0, 0, 7, 20, 21, 22, 23, 24, 25, 26, 27, 14, 15, 17, 18, 0, 19, 3, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 28, 0, 0, 0, 0, 0, 55, 0, 10, 11, 12, 13, 0, 53, 61, 60, 0, 0, 0, 0, 0, 0, 0, 54, 56, 9, 51, 52 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { -1, 33, 34, 35, 41, 66 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -48 static const yytype_int16 yypact[] = { 167, -48, -21, -48, 167, 167, 167, 27, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 6, 11, 167, 12, -11, 167, 167, 167, 216, 45, -48, 3, 167, 1, 1, 1, 8, -48, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 167, 167, -9, 167, 56, -9, 240, 316, -27, -1, 9, -48, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 701, 167, 346, 376, 406, 61, -48, 167, 167, -48, 167, 167, -48, 42, 42, 42, 42, 747, 747, 74, 724, -25, -25, -9, -9, 429, -9, -48, 452, 112, 167, 167, 167, 167, 270, 293, 701, 482, -48, 167, 167, 27, 512, 542, 572, 595, -48, -48, -48, -48, 167, 701, 701, -48, 167, 167, 167, 167, 625, 655, 678, 701, 701, -48, -48, -48 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -48, -48, -2, -4, -47, -48 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { 37, 38, 39, 97, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 80, 81, 60, 95, 82, 63, 64, 65, 83, 96, 68, 36, 86, 40, 70, 71, 72, 73, 74, 75, 82, 76, 77, 62, 83, 69, 78, 79, 80, 81, 82, 36, 82, 58, 88, 89, 83, 90, 59, 61, 87, 84, 91, 85, 98, 119, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 139, 114, 113, 115, 0, 78, 79, 80, 81, 120, 121, 82, 122, 123, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 71, 72, 73, 74, 75, 0, 128, 129, 130, 131, 126, 78, 79, 80, 81, 137, 138, 82, 0, 0, 0, 83, 0, 0, 0, 0, 144, 0, 0, 0, 145, 146, 147, 148, 0, 0, 0, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 127, 0, 1, 0, 85, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 0, 0, 27, 0, 28, 0, 29, 0, 0, 30, 0, 31, 0, 0, 1, 0, 32, 67, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 0, 0, 27, 0, 28, 0, 29, 0, 0, 30, 0, 31, 0, 0, 0, 0, 32, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 92, 82, 0, 0, 0, 83, 0, 0, 0, 0, 0, 93, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 132, 82, 133, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 134, 82, 135, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 0, 0, 0, 94, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 116, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 117, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 118, 82, 0, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 124, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 0, 0, 0, 125, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 0, 136, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 140, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 141, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 142, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 143, 0, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 0, 149, 0, 0, 0, 0, 85, 70, 71, 72, 73, 74, 75, 0, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 150, 82, 0, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 151, 82, 0, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 77, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 70, 71, 72, 73, 74, 75, 85, 76, 0, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83, 70, 71, 72, 73, 0, 0, 0, 0, 0, 0, 0, 0, 78, 79, 80, 81, 0, 0, 82, 0, 0, 0, 83 }; static const yytype_int16 yycheck[] = { 4, 5, 6, 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 45, 46, 26, 50, 49, 29, 30, 31, 53, 56, 32, 52, 36, 6, 31, 32, 33, 34, 35, 36, 49, 38, 39, 54, 53, 0, 43, 44, 45, 46, 49, 52, 49, 47, 58, 59, 53, 61, 47, 47, 52, 58, 6, 60, 55, 4, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 127, 85, 84, 87, -1, 43, 44, 45, 46, 93, 94, 49, 96, 97, -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, 31, 32, 33, 34, 35, 36, -1, 116, 117, 118, 119, 4, 43, 44, 45, 46, 125, 126, 49, -1, -1, -1, 53, -1, -1, -1, -1, 136, -1, -1, -1, 140, 141, 142, 143, -1, -1, -1, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, 56, -1, 3, -1, 60, 6, 7, 8, 9, 10, 11, -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, -1, 37, -1, -1, 40, -1, 42, -1, 44, -1, -1, 47, -1, 49, -1, -1, 3, -1, 54, 6, 7, 8, 9, 10, 11, -1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, -1, 37, -1, -1, 40, -1, 42, -1, 44, -1, -1, 47, -1, 49, -1, -1, -1, -1, 54, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, 50, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, 50, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, 56, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, 56, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, -1, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, 50, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, -1, 57, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, 56, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, -1, 56, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, 41, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, 55, -1, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, -1, 55, -1, -1, -1, -1, 60, 31, 32, 33, 34, 35, 36, -1, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, -1, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, 48, 49, -1, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, 39, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, 31, 32, 33, 34, 35, 36, 60, 38, -1, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53, 31, 32, 33, 34, -1, -1, -1, -1, -1, -1, -1, -1, 43, 44, 45, 46, -1, -1, 49, -1, -1, -1, 53 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 3, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 37, 40, 42, 44, 47, 49, 54, 62, 63, 64, 52, 64, 64, 64, 6, 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 47, 47, 64, 47, 54, 64, 64, 64, 66, 6, 63, 0, 31, 32, 33, 34, 35, 36, 38, 39, 43, 44, 45, 46, 49, 53, 58, 60, 64, 52, 64, 64, 64, 6, 48, 59, 59, 50, 56, 4, 55, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 64, 64, 56, 56, 48, 4, 64, 64, 64, 64, 50, 59, 4, 56, 64, 64, 64, 64, 48, 50, 48, 50, 57, 64, 64, 65, 56, 56, 41, 55, 64, 64, 64, 64, 64, 55, 48, 48 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: /* Line 1455 of yacc.c */ #line 54 "gram.y" { root = (yyvsp[(1) - (1)].node); } break; case 3: /* Line 1455 of yacc.c */ #line 58 "gram.y" { (yyval.node) = new_node(2, node_is_scalar((yyvsp[(3) - (3)].node))); (yyval.node)->type = NODETYPE_EXPRLIST; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 4: /* Line 1455 of yacc.c */ #line 65 "gram.y" { (yyval.node) = (yyvsp[(1) - (2)].node); } break; case 5: /* Line 1455 of yacc.c */ #line 68 "gram.y" { (yyval.node) = (yyvsp[(1) - (1)].node); } break; case 6: /* Line 1455 of yacc.c */ #line 72 "gram.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 7: /* Line 1455 of yacc.c */ #line 75 "gram.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 8: /* Line 1455 of yacc.c */ #line 78 "gram.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 9: /* Line 1455 of yacc.c */ #line 81 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->type = NODETYPE_GEN; (yyval.node)->pos = (yyvsp[(7) - (7)].pos); (yyval.node)->ident = (yyvsp[(2) - (7)].ident); (yyval.node)->expr[0] = (yyvsp[(4) - (7)].node); (yyval.node)->expr[1] = (yyvsp[(6) - (7)].node); } break; case 10: /* Line 1455 of yacc.c */ #line 89 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->type = NODETYPE_RANGE; (yyval.node)->flags = 0; (yyval.node)->pos = (yyvsp[(5) - (5)].pos); (yyval.node)->expr[0] = (yyvsp[(2) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(4) - (5)].node); } break; case 11: /* Line 1455 of yacc.c */ #line 97 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->type = NODETYPE_RANGE; (yyval.node)->flags = RANGE_EXACT_UPPER; (yyval.node)->pos = (yyvsp[(5) - (5)].pos); (yyval.node)->expr[0] = (yyvsp[(2) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(4) - (5)].node); } break; case 12: /* Line 1455 of yacc.c */ #line 105 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->type = NODETYPE_RANGE; (yyval.node)->flags = RANGE_EXACT_LOWER; (yyval.node)->pos = (yyvsp[(5) - (5)].pos); (yyval.node)->expr[0] = (yyvsp[(2) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(4) - (5)].node); } break; case 13: /* Line 1455 of yacc.c */ #line 113 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->type = NODETYPE_RANGE; (yyval.node)->flags = RANGE_EXACT_UPPER | RANGE_EXACT_LOWER; (yyval.node)->pos = (yyvsp[(5) - (5)].pos); (yyval.node)->expr[0] = (yyvsp[(2) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(4) - (5)].node); } break; case 14: /* Line 1455 of yacc.c */ #line 121 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_ADD; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 15: /* Line 1455 of yacc.c */ #line 129 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_SUB; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 16: /* Line 1455 of yacc.c */ #line 137 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_SUB; (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = new_scalar_node(0); (yyval.node)->expr[0]->type = NODETYPE_REAL; (yyval.node)->expr[0]->real = 0.0; (yyval.node)->expr[1] = (yyvsp[(2) - (2)].node); } break; case 17: /* Line 1455 of yacc.c */ #line 147 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->type = NODETYPE_MUL; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 18: /* Line 1455 of yacc.c */ #line 155 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->type = NODETYPE_DIV; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 19: /* Line 1455 of yacc.c */ #line 163 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->type = NODETYPE_POW; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 20: /* Line 1455 of yacc.c */ #line 171 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_LT; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 21: /* Line 1455 of yacc.c */ #line 179 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_LE; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 22: /* Line 1455 of yacc.c */ #line 187 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_GT; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 23: /* Line 1455 of yacc.c */ #line 195 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_GE; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 24: /* Line 1455 of yacc.c */ #line 203 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_EQ; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 25: /* Line 1455 of yacc.c */ #line 211 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_NE; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 26: /* Line 1455 of yacc.c */ #line 219 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_AND; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 27: /* Line 1455 of yacc.c */ #line 227 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_OR; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; case 28: /* Line 1455 of yacc.c */ #line 235 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_INDEX; (yyval.node)->pos = (yyvsp[(4) - (4)].pos); (yyval.node)->expr[0] = (yyvsp[(1) - (4)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (4)].node); } break; case 29: /* Line 1455 of yacc.c */ #line 242 "gram.y" { (yyval.node) = new_node(1, node_is_scalar((yyvsp[(3) - (3)].node))); (yyval.node)->type = NODETYPE_ASSIGN; (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->ident = (yyvsp[(1) - (3)].ident); (yyval.node)->expr[0] = (yyvsp[(3) - (3)].node); } break; case 30: /* Line 1455 of yacc.c */ #line 249 "gram.y" { (yyval.node) = (yyvsp[(2) - (2)].node); } break; case 31: /* Line 1455 of yacc.c */ #line 252 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_NOT; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 32: /* Line 1455 of yacc.c */ #line 259 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_SUM; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 33: /* Line 1455 of yacc.c */ #line 265 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_PROD; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 34: /* Line 1455 of yacc.c */ #line 271 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_AVG; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 35: /* Line 1455 of yacc.c */ #line 277 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_LEN; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 36: /* Line 1455 of yacc.c */ #line 283 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_MAX; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 37: /* Line 1455 of yacc.c */ #line 289 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_MIN; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 38: /* Line 1455 of yacc.c */ #line 295 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_IMAX; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 39: /* Line 1455 of yacc.c */ #line 301 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_IMIN; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 40: /* Line 1455 of yacc.c */ #line 307 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_ISNAN; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 41: /* Line 1455 of yacc.c */ #line 314 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_SQRT; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 42: /* Line 1455 of yacc.c */ #line 321 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_ABS; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 43: /* Line 1455 of yacc.c */ #line 328 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_EXP; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 44: /* Line 1455 of yacc.c */ #line 335 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_LOG; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 45: /* Line 1455 of yacc.c */ #line 342 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_SIN; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 46: /* Line 1455 of yacc.c */ #line 349 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_COS; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 47: /* Line 1455 of yacc.c */ #line 356 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_TAN; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 48: /* Line 1455 of yacc.c */ #line 363 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_ASIN; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 49: /* Line 1455 of yacc.c */ #line 370 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_ACOS; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 50: /* Line 1455 of yacc.c */ #line 377 "gram.y" { (yyval.node) = new_scalar_node(1); (yyval.node)->pos = (yyvsp[(1) - (2)].pos); (yyval.node)->type = NODETYPE_ATAN; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(2) - (2)].node); } break; case 51: /* Line 1455 of yacc.c */ #line 384 "gram.y" { (yyval.node) = new_scalar_node(3); (yyval.node)->pos = (yyvsp[(1) - (8)].pos); (yyval.node)->type = NODETYPE_CLAMP; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(3) - (8)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (8)].node); (yyval.node)->expr[2] = (yyvsp[(7) - (8)].node); } break; case 52: /* Line 1455 of yacc.c */ #line 393 "gram.y" { (yyval.node) = new_scalar_node(3); (yyval.node)->pos = (yyvsp[(1) - (8)].pos); (yyval.node)->type = NODETYPE_SEGMENT; (yyval.node)->flags |= ALLARGS_SCALAR; (yyval.node)->expr[0] = (yyvsp[(3) - (8)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (8)].node); (yyval.node)->expr[2] = (yyvsp[(7) - (8)].node); } break; case 53: /* Line 1455 of yacc.c */ #line 402 "gram.y" { (yyval.node) = new_node(3, node_is_scalar((yyvsp[(3) - (5)].node))); (yyval.node)->pos = (yyvsp[(2) - (5)].pos); (yyval.node)->type = NODETYPE_IFELSE; (yyval.node)->expr[0] = (yyvsp[(1) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (5)].node); (yyval.node)->expr[2] = (yyvsp[(5) - (5)].node); } break; case 54: /* Line 1455 of yacc.c */ #line 410 "gram.y" { (yyval.node) = new_node(3, node_is_scalar((yyvsp[(5) - (7)].node))); (yyval.node)->pos = (yyvsp[(1) - (7)].pos); (yyval.node)->type = NODETYPE_IFELSE; (yyval.node)->expr[0] = (yyvsp[(3) - (7)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (7)].node); (yyval.node)->expr[2] = (yyvsp[(7) - (7)].node); } break; case 55: /* Line 1455 of yacc.c */ #line 418 "gram.y" { (yyval.node) = new_node(2, node_is_scalar((yyvsp[(5) - (5)].node))); (yyval.node)->pos = (yyvsp[(1) - (5)].pos); (yyval.node)->type = NODETYPE_IFELSE; (yyval.node)->expr[0] = (yyvsp[(3) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (5)].node); } break; case 56: /* Line 1455 of yacc.c */ #line 425 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->pos = (yyvsp[(1) - (7)].pos); (yyval.node)->type = NODETYPE_FOR; (yyval.node)->ident = (yyvsp[(3) - (7)].ident); (yyval.node)->expr[0] = (yyvsp[(5) - (7)].node); (yyval.node)->expr[1] = (yyvsp[(7) - (7)].node); } break; case 57: /* Line 1455 of yacc.c */ #line 433 "gram.y" { (yyval.node) = new_node(0, ident_is_scalar((yyvsp[(1) - (1)].ident))); (yyval.node)->type = NODETYPE_IDENT; (yyval.node)->pos = -1; (yyval.node)->ident = (yyvsp[(1) - (1)].ident); } break; case 58: /* Line 1455 of yacc.c */ #line 439 "gram.y" { (yyval.node) = new_scalar_node(0); (yyval.node)->pos = -1; (yyval.node)->type = NODETYPE_REAL; (yyval.node)->real = (yyvsp[(1) - (1)].real); } break; case 59: /* Line 1455 of yacc.c */ #line 445 "gram.y" { (yyval.node) = new_scalar_node(0); (yyval.node)->pos = -1; (yyval.node)->type = NODETYPE_REAL; (yyval.node)->real = INVALID_VALUE; } break; case 60: /* Line 1455 of yacc.c */ #line 454 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->type = NODETYPE_LET; (yyval.node)->pos = (yyvsp[(2) - (5)].pos); (yyval.node)->ident = (yyvsp[(1) - (5)].ident); (yyval.node)->expr[0] = (yyvsp[(3) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (5)].node); } break; case 61: /* Line 1455 of yacc.c */ #line 461 "gram.y" { (yyval.node) = new_scalar_node(2); (yyval.node)->pos = (yyvsp[(2) - (5)].pos); (yyval.node)->type = NODETYPE_LET; (yyval.node)->ident = (yyvsp[(1) - (5)].ident); (yyval.node)->expr[0] = (yyvsp[(3) - (5)].node); (yyval.node)->expr[1] = (yyvsp[(5) - (5)].node); } break; case 62: /* Line 1455 of yacc.c */ #line 470 "gram.y" { (yyval.node) = new_vector_node(1); (yyval.node)->pos = (yyvsp[(1) - (1)].node)->pos; (yyval.node)->type = NODETYPE_VEC1; (yyval.node)->expr[0] = (yyvsp[(1) - (1)].node); } break; case 63: /* Line 1455 of yacc.c */ #line 475 "gram.y" { (yyval.node) = new_vector_node(2); (yyval.node)->pos = (yyvsp[(2) - (3)].pos); (yyval.node)->type = NODETYPE_VEC2; (yyval.node)->expr[0] = (yyvsp[(1) - (3)].node); (yyval.node)->expr[1] = (yyvsp[(3) - (3)].node); } break; /* Line 1455 of yacc.c */ #line 2348 "progs/minccalc/gram.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } /* Line 1675 of yacc.c */ #line 482 "gram.y" node_t root; void yyerror(msg) const char *msg; { extern int lexpos; show_error(lexpos, msg); } minc-tools-2.3.00+dfsg/progs/minccalc/optim.c0000644000175000000620000000010312574624760020013 0ustar stevestaff#include "node.h" node_t optimize(node_t root){ return root; } minc-tools-2.3.00+dfsg/progs/minccalc/scalar.c0000644000175000000620000000116512574624760020141 0ustar stevestaff/* Copyright David Leonard and Andrew Janke, 2000. All rights reserved. */ #include #include #include "node.h" #define INIT_SIZE 20 scalar_t new_scalar(int width){ scalar_t s; s = malloc(sizeof *s); s->width = width; s->vals = malloc(width * sizeof(s->vals[0])); s->refcnt = 1; return s; } void scalar_incr_ref(scalar_t s) { s->refcnt++; } void scalar_free(scalar_t s){ if (s->refcnt <= 0) { (void) fprintf(stderr, "Internal error: scalar freed too often\n"); exit(1); } if (--s->refcnt == 0) { free(s->vals); s->vals = NULL; free(s); } } minc-tools-2.3.00+dfsg/progs/minccalc/lex.c0000644000175000000620000014515712574624760017476 0ustar stevestaff #line 3 "progs/minccalc/lex.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart (FILE *input_file ); void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); void yy_delete_buffer (YY_BUFFER_STATE b ); void yy_flush_buffer (YY_BUFFER_STATE b ); void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); void yypop_buffer_state (void ); static void yyensure_buffer_stack (void ); static void yy_load_buffer_state (void ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); void *yyalloc (yy_size_t ); void *yyrealloc (void *,yy_size_t ); void yyfree (void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; extern int yylineno; int yylineno = 1; extern char *yytext; #define yytext_ptr yytext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 42 #define YY_END_OF_BUFFER 43 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[108] = { 0, 0, 0, 43, 41, 40, 40, 35, 41, 39, 29, 41, 31, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 41, 40, 34, 36, 39, 39, 0, 30, 33, 32, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 25, 38, 23, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 24, 37, 39, 0, 39, 28, 8, 38, 38, 38, 1, 38, 16, 38, 13, 27, 38, 38, 38, 5, 4, 14, 9, 10, 38, 38, 15, 38, 2, 17, 19, 18, 20, 38, 26, 11, 12, 38, 3, 38, 7, 21, 6, 38, 38, 22, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, 1, 1, 5, 1, 1, 1, 1, 6, 1, 6, 7, 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 9, 10, 11, 1, 1, 12, 12, 12, 12, 13, 12, 12, 12, 12, 12, 12, 12, 12, 14, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 15, 1, 16, 17, 18, 19, 20, 21, 22, 12, 23, 12, 12, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 12, 35, 12, 12, 1, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[37] = { 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 } ; static yyconst flex_int16_t yy_base[109] = { 0, 0, 0, 135, 136, 35, 37, 124, 128, 34, 122, 121, 120, 0, 113, 26, 21, 26, 101, 38, 26, 33, 97, 42, 39, 90, 49, 136, 136, 59, 69, 62, 136, 136, 136, 0, 111, 93, 96, 99, 105, 98, 103, 87, 86, 88, 85, 0, 57, 0, 88, 52, 91, 77, 85, 83, 87, 82, 77, 81, 79, 0, 136, 73, 96, 95, 0, 0, 71, 75, 74, 0, 74, 0, 78, 0, 0, 62, 70, 79, 0, 0, 0, 0, 0, 75, 67, 0, 59, 0, 0, 0, 0, 0, 62, 0, 0, 0, 62, 0, 67, 0, 0, 0, 59, 51, 0, 136, 72 } ; static yyconst flex_int16_t yy_def[109] = { 0, 107, 1, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 0, 107 } ; static yyconst flex_int16_t yy_nxt[173] = { 0, 4, 5, 6, 7, 8, 4, 4, 9, 10, 11, 12, 13, 13, 14, 4, 15, 13, 16, 13, 17, 18, 13, 19, 20, 21, 13, 13, 22, 13, 13, 23, 24, 13, 13, 13, 25, 26, 26, 26, 26, 29, 30, 37, 38, 42, 51, 31, 43, 53, 44, 26, 26, 52, 31, 60, 54, 39, 40, 47, 41, 45, 56, 48, 49, 57, 61, 63, 64, 50, 65, 58, 31, 77, 35, 59, 29, 30, 80, 31, 78, 63, 31, 106, 81, 105, 31, 104, 103, 31, 102, 101, 100, 31, 99, 98, 97, 96, 95, 94, 93, 92, 91, 65, 65, 90, 89, 88, 87, 86, 85, 84, 83, 82, 79, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 62, 55, 46, 36, 34, 33, 32, 28, 27, 107, 3, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107 } ; static yyconst flex_int16_t yy_chk[173] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 6, 6, 9, 9, 15, 15, 16, 20, 9, 16, 21, 17, 26, 26, 20, 9, 24, 21, 15, 15, 19, 15, 17, 23, 19, 19, 23, 24, 29, 31, 19, 31, 23, 29, 48, 108, 23, 30, 30, 51, 29, 48, 63, 30, 105, 51, 104, 63, 100, 98, 30, 94, 88, 86, 63, 85, 79, 78, 77, 74, 72, 70, 69, 68, 65, 64, 60, 59, 58, 57, 56, 55, 54, 53, 52, 50, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 25, 22, 18, 14, 12, 11, 10, 8, 7, 3, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_flex_debug; int yy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *yytext; #line 1 "lex.l" #line 2 "lex.l" #include #include "node.h" #include "gram.h" int lexpos = 0; #define setpos() yylval.pos = lexpos; lexpos += yyleng #line 537 "progs/minccalc/lex.c" #define INITIAL 0 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy (void ); int yyget_debug (void ); void yyset_debug (int debug_flag ); YY_EXTRA_TYPE yyget_extra (void ); void yyset_extra (YY_EXTRA_TYPE user_defined ); FILE *yyget_in (void ); void yyset_in (FILE * in_str ); FILE *yyget_out (void ); void yyset_out (FILE * out_str ); int yyget_leng (void ); char *yyget_text (void ); int yyget_lineno (void ); void yyset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (void ); #else extern int yywrap (void ); #endif #endif static void yyunput (int c,char *buf_ptr ); #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex (void); #define YY_DECL int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 10 "lex.l" #line 727 "progs/minccalc/lex.c" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 108 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 136 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 12 "lex.l" setpos(); return AVG; YY_BREAK case 2: YY_RULE_SETUP #line 13 "lex.l" setpos(); return SUM; YY_BREAK case 3: YY_RULE_SETUP #line 14 "lex.l" setpos(); return PROD; YY_BREAK case 4: YY_RULE_SETUP #line 15 "lex.l" setpos(); return LET; YY_BREAK case 5: YY_RULE_SETUP #line 16 "lex.l" setpos(); return LEN; YY_BREAK case 6: YY_RULE_SETUP #line 17 "lex.l" setpos(); return ISNAN; YY_BREAK case 7: YY_RULE_SETUP #line 18 "lex.l" setpos(); return SQRT; YY_BREAK case 8: YY_RULE_SETUP #line 19 "lex.l" setpos(); return ABS; YY_BREAK case 9: YY_RULE_SETUP #line 20 "lex.l" setpos(); return MAX; YY_BREAK case 10: YY_RULE_SETUP #line 21 "lex.l" setpos(); return MIN; YY_BREAK case 11: YY_RULE_SETUP #line 22 "lex.l" setpos(); return IMAX; YY_BREAK case 12: YY_RULE_SETUP #line 23 "lex.l" setpos(); return IMIN; YY_BREAK case 13: YY_RULE_SETUP #line 24 "lex.l" setpos(); return EXP; YY_BREAK case 14: YY_RULE_SETUP #line 25 "lex.l" setpos(); return LOG; YY_BREAK case 15: YY_RULE_SETUP #line 26 "lex.l" setpos(); return SIN; YY_BREAK case 16: YY_RULE_SETUP #line 27 "lex.l" setpos(); return COS; YY_BREAK case 17: YY_RULE_SETUP #line 28 "lex.l" setpos(); return TAN; YY_BREAK case 18: YY_RULE_SETUP #line 29 "lex.l" setpos(); return ASIN; YY_BREAK case 19: YY_RULE_SETUP #line 30 "lex.l" setpos(); return ACOS; YY_BREAK case 20: YY_RULE_SETUP #line 31 "lex.l" setpos(); return ATAN; YY_BREAK case 21: YY_RULE_SETUP #line 32 "lex.l" setpos(); return CLAMP; YY_BREAK case 22: YY_RULE_SETUP #line 33 "lex.l" setpos(); return SEGMENT; YY_BREAK case 23: YY_RULE_SETUP #line 34 "lex.l" setpos(); return IN; YY_BREAK case 24: YY_RULE_SETUP #line 35 "lex.l" setpos(); return TO; YY_BREAK case 25: YY_RULE_SETUP #line 36 "lex.l" setpos(); return IF; YY_BREAK case 26: YY_RULE_SETUP #line 37 "lex.l" setpos(); return ELSE; YY_BREAK case 27: YY_RULE_SETUP #line 38 "lex.l" setpos(); return FOR; YY_BREAK case 28: YY_RULE_SETUP #line 39 "lex.l" setpos(); return NAN; YY_BREAK case 29: YY_RULE_SETUP #line 40 "lex.l" setpos(); return LT; YY_BREAK case 30: YY_RULE_SETUP #line 41 "lex.l" setpos(); return LE; YY_BREAK case 31: YY_RULE_SETUP #line 42 "lex.l" setpos(); return GT; YY_BREAK case 32: YY_RULE_SETUP #line 43 "lex.l" setpos(); return GE; YY_BREAK case 33: YY_RULE_SETUP #line 44 "lex.l" setpos(); return EQ; YY_BREAK case 34: YY_RULE_SETUP #line 45 "lex.l" setpos(); return NE; YY_BREAK case 35: YY_RULE_SETUP #line 46 "lex.l" setpos(); return NOT; YY_BREAK case 36: YY_RULE_SETUP #line 47 "lex.l" setpos(); return AND; YY_BREAK case 37: YY_RULE_SETUP #line 48 "lex.l" setpos(); return OR; YY_BREAK case 38: YY_RULE_SETUP #line 49 "lex.l" { setpos(); yylval.ident = new_ident(yytext); return IDENT; } YY_BREAK case 39: YY_RULE_SETUP #line 54 "lex.l" { setpos(); yylval.real = atof(yytext);; return REAL; } YY_BREAK case 40: /* rule 40 can match eol */ YY_RULE_SETUP #line 59 "lex.l" setpos(); YY_BREAK case 41: YY_RULE_SETUP #line 60 "lex.l" setpos(); return yytext[0]; YY_BREAK case 42: YY_RULE_SETUP #line 63 "lex.l" ECHO; YY_BREAK #line 1029 "progs/minccalc/lex.c" case YY_STATE_EOF(INITIAL): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_c_buf_p); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), (size_t) num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 108 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 108 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 107); return yy_is_jam ? 0 : yy_current_state; } static void yyunput (int c, register char * yy_bp ) { register char *yy_cp; yy_cp = (yy_c_buf_p); /* undo effects of setting up yytext */ *yy_cp = (yy_hold_char); if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = (yy_n_chars) + 2; register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; register char *source = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; (yytext_ptr) = yy_bp; (yy_hold_char) = *yy_cp; (yy_c_buf_p) = yy_cp; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ); yyfree((void *) b ); } #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) { return yy_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; buf = (char *) yyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ int yyget_leng (void) { return yyleng; } /** Get the current token. * */ char *yyget_text (void) { return yytext; } /** Set the current line number. * @param line_number * */ void yyset_lineno (int line_number ) { yylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_switch_to_buffer */ void yyset_in (FILE * in_str ) { yyin = in_str ; } void yyset_out (FILE * out_str ) { yyout = out_str ; } int yyget_debug (void) { return yy_flex_debug; } void yyset_debug (int bdebug ) { yy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size ) { return (void *) malloc( size ); } void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } void yyfree (void * ptr ) { free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 63 "lex.l" static YY_BUFFER_STATE lex_state; void lex_init(const char *s){ lex_state = yy_scan_string(s); } void lex_finalize(){ yy_delete_buffer(lex_state); } int yywrap(){ return 1; } minc-tools-2.3.00+dfsg/progs/minccalc/errx.h0000644000175000000620000000064012574624760017656 0ustar stevestaff/* Just in case you don't have errx, but you have gcc */ #if !defined(__OpenBSD__) && defined(__GNUC__) # define errx(exitcode, fmt , args) \ do { fprintf(stderr, "imgcalc: " fmt "\n", ## args ); exit(1); } while(0) # define err(exitcode, fmt , args) \ do { fprintf(stderr, "imgcalc: " fmt , ## args ); \ fprintf(stderr, ": %s\n", strerror(errno)); exit(1); } while(0) #else # include #endif minc-tools-2.3.00+dfsg/progs/minccalc/ident.c0000644000175000000620000000237712574624760020005 0ustar stevestaff/* Copyright David Leonard & Andrew Janke, 2000. All rights reserved. */ #define _GNU_SOURCE 1 #include #include #include #include #include "node.h" struct intern { const char *s; int id; struct intern *next; int is_scalar; }; static struct intern *head = NULL; static ident_t nextid = 0; ident_t new_ident(const char *s){ struct intern *i; for (i = head; i; i = i->next) if (strcmp(i->s, s) == 0) return i->id; i = malloc(sizeof *i); i->id = nextid++; i->s = strdup(s); i->next = head; i->is_scalar = islower(s[0]); head = i; return i->id; } const char *ident_str(ident_t id){ struct intern *i; for (i = head; i; i = i->next) if (i->id == id) return i->s; return "?"; } int ident_is_scalar(ident_t id){ struct intern *i; for (i = head; i; i = i->next) if (i->id == id) return i->is_scalar; /* errx(1, "ident_is_scalar: no such ident %d", id); */ fprintf(stderr, "ident_is_scalar: no such ident %d\n", id); exit(1); return 0; } ident_t ident_lookup(char *string) { struct intern *i; for (i = head; i; i = i->next) { if (strcmp(i->s, string) == 0) { return i->id; } } return -1; } minc-tools-2.3.00+dfsg/progs/minccalc/minccalc.man10000644000175000000620000003123212574624760021055 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 2000 Andrew Janke .TH MINCCALC 1 "$Date: 2008-01-11 04:24:16 $" "" "MINC User's Guide" .SH NAME minccalc - perform complex math operations on minc files .SH SYNOPSIS .B mincalc [] .mnc [.mnc...] .mnc .SH DESCRIPTION \fIMinccalc\fR will perform complex, voxel-by-voxel math operations, on one or more minc files of the same shape and having the same coordinate sampling, producing a single output file. The operations to be performed are input using the \fB\-expression\fR argument (see \fBEXPRESSIONS\fR). By default, the output file is the last non-option argument. However, if the \fB-outfile\fR option is used, then all non-option arguments are considered input files and the output file names come from the \fB-outfile\fR options, of which there can be more than one. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .TP \fB\-2\fR Create MINC 2.0 format output files. .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-no_clobber\fR Synonym for -noclobber. .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-debug\fR Print out debugging information. .TP \fB\-copy_header\fR Copy all of the header information from the first input file (default for one input file). .TP \fB\-nocopy_header\fR Do not copy all of the header from the first input file; copy only coordinate information (default for more than one input file). .TP \fB\-filetype\fR Create an output file with the same type as the first input file (default). .TP \fB\-byte\fR Store output voxels in 8-bit integer format. .TP \fB\-short\fR Store output voxels in 16-bit integer format. .TP \fB\-int\fR Store output voxels in 32-bit integer format. .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB-float\fR Store output voxels in 32-bit floating point format. .TP \fB-double\fR Store output voxels in 64-bit floating point format. .TP \fB\-signed\fR Use signed, two's complement integer format. Applies only if the output voxel type is specified to be an integer type (one of \fB\-byte\fR, \fB\-short\fR, \fB\-int\fR or \fB-long\fR). .TP \fB\-unsigned\fR Use unsigned integer format. Applies only if the output voxel type is specified to be an integer type (one of \fB\-byte\fR, \fB\-short\fR, \fB\-int\fR or \fB-long\fR). .TP \fB\-range\fR \fImin max\fR Restrict the valid range of integer data. Applies only if one of the \fB-byte\fR, \fB-short\fR, \fB-int\fR or \fB\-long\fR options is specified. .TP \fB\-max_buffer_size_in_kb\fR \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4096 (4MB). .TP \fB\-dimension\fR\ \fIdimname\fR Specify a dimension along which we wish to perform a cumulative operation. .TP \fB\-check_dimensions\fR Check that all input files have matching sampling in world dimensions (default). .TP \fB\-nocheck_dimensions\fR Ignore any differences in world dimensions sampling for input files. .TP \fB\-propagate_nan\fR For cumulative vector operations (\fBsum\fR, \fBprod\fR and \fBavg\fR), invalid data (Not-A-Number or NaN) in any element of the vector will produce invalid data in the result (default). .TP \fB\-ignore_nan\fR For cumulative vector operations, invalid data (NaN) in the vector is ignored, ie. treated as though it is not present. .TP \fB\-nan\fR When an illegal operation is attempted at a voxel (such as divide by zero), the result is invalid data (NaN) (default). Having no valid input data for a cumulative operation is also considered an illegal operation when \fB-ignore_nan\fR is used. .TP \fB\-zero\fR When an illegal operation is attempted at a voxel (such as divide by zero), the result is value zero. .TP \fB\-illegal_value\fR\ \fIvalue\fR When an illegal operation is attempted at a voxel (such as divide by zero), the result is the value specified by this option. .TP \fB\-expression\fR \fIstring\fR Specify the expression to evaluate at each voxel (see EXPRESSIONS). .TP \fB\-expfile\fR \fIfilename\fR Specify a file containing an expression to evaluate at each voxel (see EXPRESSIONS). If filename ``-'' is given, then the expression is read from stdin. The only difference from command-line expressions is that comments can be given in the file. A comment line is specified by placing a ``#'' as the first non-whitespace character of the line. Minccalc scripts can be created by setting the first line to #! /usr/local/mni/bin/minccalc -expfile .TP \fB\-outfile\fR \fIsymbol output-file\fR Specify that output should be written to the specified file, taking values from the symbol which should be created in the expression (see the \fBEXAMPLES\fR section). If this option is given, then all non-option arguments are taken as input files. This option can be used multiple times for multiple output files. .TP \fB\-eval_width\fR \fIvalue\fR Specify the number of voxels to process in parallel. Default is 200. .SH EXPRESSIONS .PP The \fB\-expression\fR argument is a single string that describes the function to evaluate. The function expression is typically written in terms of the vector A. For example, the following expression will sum the first two input files together: A[0] + A[1] Multiple expressions can be given separated by semicolons, in which case only the value of the last expression is used. These expression lists can be used with assignment expressions to make the syntax very C-like: ratio = A[0]/A[1]; A[2]*exp(-ratio) An expression list in curly brackets is a valid expression and returns the value of last expression in the list. This is particularly useful in \fBfor\fR and \fBif\fR expressions (see below). There are two types of values in the language: vectors and scalars. Scalars literals are floating point numbers or may appear as symbols whose name starts with a lowercase letter. Besides normal scalar operators such as +, -, * and /, the expression language also supports the infix exponentiation operator ^ , the usual relational operators <, <=, >, >=, ==, != as well as the boolean operators && (and), || (or) and ! (not). Note that the && and || boolean operators always evaluate both operands, unlike C. Scalar mathematical functions include \fBabs\fR, \fBsqrt\fR, \fBexp\fR, \fBlog\fR, \fBsin\fR, \fBcos\fR, \fBtan\fR, \fBasin\fR, \fBacos\fR and \fBatan\fR. There are also some specialized functions: isnan(v) - 1 if v is invalid and 0 otherwise clamp(v1,v2,v3) - v1 bounded by [v2, v3] segment(v1,v2,v3) - tests if v1 is in [v2, v3] The scalar constant NaN is defined such that isnan(NaN) return 1. Vectors can be written in the following `extensional' form [ value1, value2, ... ] or by using the following range-generating notations: [ a : b ] generates {a, a+1, ..., b-1, b} [ a : b ) generates {a, a+1, ..., b-1} ( a : b ] generates {a+1, ..., b-1, b} ( a : b ) generates {a+1, ..., b-1} or be generated, by `intension'. The following intension expression generates the vector {3,2,1}: { i in [1:3] | 4 - i } Vectors may also appear as symbols whose name starts with an uppercase letter. In addition to the scalar operators, the following vector operators are supplied: avg - the average value of the scalars in vector len - the length of sum - the sum of the elements of prod - the product of the elements of max - the maximum value of min - the minimum value of imax - the index of the maximum value of imin - the index of the minimum value of V[s] - the s'th element of vector V with origin 0. Symbol names are introduced into a global symbol table by assignment expressions of the form a = A[2] * log(2) Symbols starting with a lowercase letter represent scalars while those starting with an uppercase letter represent vectors. Since = is an operator, its result can be used in an expression (as in C). A few control constructs are provided: For loops can be created to loop over a vector, assigning each value to a symbol and then evaluating an expression. This is done with expressions of the form total=0; for{i in [0:len(A))} total=total+A[i]; total which is equivalent to sum(A). Note that this is similar to using total=0; len{i in [0:len(A)) | total=total+A[i]}; total since the \fBfor\fR construct is actually an operator (although it is usually only used for changing symbol values). Note also that without the final "total", the expression would not be very useful since it would only return the length of the vector. As in C, a list of expressions can be specified in curlies: total=total2 = 0; for {i in [0:len(A))} { total = total + A[i]; total2 = total2 + A[i]^2 } There are also a few forms of the \fBif-then-else\fR construct: A[0]<0 ? 0 : A[0] if (A[0]<0) result=0 else result=A[0] The \fBelse\fR is optional. Again, the if construct is an operator, and the \fBthen\fR or \fBelse\fR expressions can be expression lists in curlies, in which case the value of the last expression is returned. If the \fBelse\fR expression is missing, then the value 0 is returned when the test expression is 0 (false). The principal oddity with the \fBfor\fR and \fBif\fR constructs is that unlike C statements, they must be separated from the next expression by a semicolon even when an expression list in curlies is used: for i in [0:len(A)) {total=total+A[i]} ; total/len(A) if (A[i]>0) {result=2;} else {result=1} ; result*5 An alternative way to introduce symbol names is through \fBlet\fR-expressions. For example, the following expression will always evaluate to 3: let a = 1, b = 2 in a + b These were originally designed to create variables only within the evaluated expression, but modifications have been made so that the global symbol table is changed. .SH EXAMPLES Here is an expression for calculating standard deviation, taking into account the possibility of invalid input data, which is ignored: s0 = s1 = s2 = 0; for { i in [0:len(A)) } { v=A[i]; if (!isnan(v)) { s0 = s0 + 1; s1 = s1 + v; s2 = s2 + v*v; } }; if (s0 > 1) { sqrt((s2 - s1*s1/s0) / (s0-1)); } else { NaN; }; The last if could be changed to return 0 if s0 is > 0 but <= 1. We also drop the curly brackets, but then there must not be a ";" between the if and the else if (s0 > 1) sqrt((s2 - s1*s1/s0) / (s0-1)) else if (s0 > 0) 0 else NaN If we want both the mean and the standard deviation, we can use the \fB\-outfile\fR option, invoking the command with minccalc -expfile stdev \\ -outfile mean mean.mnc \\ -outfile stdev stdev.mnc \\ infile1.mnc infile2.mnc ... And using the expression file (with yet another form of if expression): s0 = s1 = s2 = 0; for {i in [0:len(A))} { v=A[i]; if (!isnan(v)) { s0 = s0 + 1; s1 = s1 + v; s2 = s2 + v*v; } }; stdev = (s0 > 1) ? sqrt((s2 - s1*s1/s0) / (s0-1)) : (s0 > 0) ? 0 : NaN ; mean = (s0 > 0) ? s1 / s0 : NaN ; .SH CAVEATS A few things you should remember... Vector variables must start with an uppercase letter. Vector variable names must not be one of the function keywords, sum, len, prod, ... etc For loops and if expressions always need to be separated from the next expression by a semicolon. The symbol table is global. Boolean operators && and || always evaluate both operands. A note on parallelism: For efficiency reasons, evaluations are done on many voxels at once (the number of voxels is referred to as the width of the evaluation and is changed with the \fB-eval_width\fR option). An odd consequence of this is that both sides of an if-else statement are always evaluated (unless all voxels give the same test result), but statements within each consequent are only evaluated on the appropriate voxels. In particular, entries in the symbol table are only modified according to a voxel mask. A side-effect of this is that any vector symbol set in an if-else consequent must not change the length of the symbol (although it can create it) and both sides of the consequent must agree on the length of any vector symbols that they both modify. If this is not clear, just try it - the program will complain if it is not happy. .SH AUTHOR Andrew Janke - a.janke@gmail.com .SH COPYRIGHTS Copyright \(co 2000 by Andrew Janke .SH "SEE ALSO" .LP .BR mincmath (1) minc-tools-2.3.00+dfsg/progs/minccalc/lex.l0000644000175000000620000000407312574624760017476 0ustar stevestaff%{ #include #include "node.h" #include "gram.h" int lexpos = 0; #define setpos() yylval.pos = lexpos; lexpos += yyleng %} %% avg setpos(); return AVG; sum setpos(); return SUM; prod setpos(); return PROD; let setpos(); return LET; len setpos(); return LEN; isnan setpos(); return ISNAN; sqrt setpos(); return SQRT; abs setpos(); return ABS; max setpos(); return MAX; min setpos(); return MIN; imax setpos(); return IMAX; imin setpos(); return IMIN; exp setpos(); return EXP; log setpos(); return LOG; sin setpos(); return SIN; cos setpos(); return COS; tan setpos(); return TAN; asin setpos(); return ASIN; acos setpos(); return ACOS; atan setpos(); return ATAN; clamp setpos(); return CLAMP; segment setpos(); return SEGMENT; in setpos(); return IN; to setpos(); return TO; if setpos(); return IF; else setpos(); return ELSE; for setpos(); return FOR; NaN setpos(); return NAN; \< setpos(); return LT; \<= setpos(); return LE; \> setpos(); return GT; \>= setpos(); return GE; == setpos(); return EQ; \!= setpos(); return NE; \! setpos(); return NOT; && setpos(); return AND; \|\| setpos(); return OR; [a-zA-Z][_a-zA-Z0-9]* { setpos(); yylval.ident = new_ident(yytext); return IDENT; } [0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)? { setpos(); yylval.real = atof(yytext);; return REAL; } [ \t\n\f]+ setpos(); . setpos(); return yytext[0]; %% static YY_BUFFER_STATE lex_state; void lex_init(const char *s){ lex_state = yy_scan_string(s); } void lex_finalize(){ yy_delete_buffer(lex_state); } int yywrap(){ return 1; } minc-tools-2.3.00+dfsg/progs/minccalc/eval.c0000644000175000000620000006216212574624760017627 0ustar stevestaff/* Copyright David Leonard & Andrew Janke, 2000. All rights reserved. */ #include #include #include #include #include #include "node.h" #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define INVALID_VALUE -DBL_MAX scalar_t eval_index(int, int *, node_t, vector_t, scalar_t); scalar_t eval_sum(int, int *, node_t, vector_t); scalar_t eval_prod(int, int *, node_t, vector_t); scalar_t eval_max(int, int *, node_t, vector_t, double, int); vector_t eval_vector(int, int *, node_t, sym_t); vector_t gen_vector(int, int *, node_t, sym_t); vector_t gen_range(int, int *, node_t, sym_t); scalar_t for_loop(int, int *, node_t n, sym_t sym); extern int debug; extern int propagate_nan; extern double value_for_illegal_operations; void eval_error(node_t n, const char *msg){ int pos = n->pos; show_error(pos, msg); } void show_error(int pos, const char *msg){ extern const char *expression; const char *c; int thisline, ichar, linenum; if (pos != -1) { thisline = 0; linenum=1; for (ichar=0; ichar < pos; ichar++) { if (expression[ichar] == '\n') { thisline = ichar+1; linenum++; } } pos -= thisline; fprintf(stderr, "\nLine %d:\n", linenum); for (c = &expression[thisline]; *c && *c != '\n'; c++) { (void) putc((int) *c, stderr); } (void) putc((int) '\n', stderr); for (c = &expression[thisline]; *c; c++) { if (pos-- == 0) break; if (*c == '\t') fprintf(stderr, "\t"); else fprintf(stderr, " "); } fprintf(stderr, "^\n"); } fprintf(stderr, "%s\n", msg); exit(1); } /* Try to evaluate an expression in a scalar context */ scalar_t eval_scalar(int width, int *eval_flags, node_t n, sym_t sym){ vector_t v; scalar_t s, s2, result; scalar_t args[3]; double vals[3]; int *eval_flags2, *isnan_flags; int found_invalid, all_true, all_false; int iarg, ivalue; /* Check that node is of correct type */ if (!node_is_scalar(n)) { eval_error(n, "Expression is not a scalar"); } /* Check special case where all arguments are scalar and we can test for invalid values in a general way */ if (n->flags & ALLARGS_SCALAR) { /* Check that we don't have too many arguments */ if (n->numargs > (int) (sizeof(args) / sizeof(args[0]))) { eval_error(n, "Internal error: too many arguments"); } /* Evaluate each argument and save the result. */ for (iarg=0; iarg < n->numargs; iarg++) { args[iarg] = eval_scalar(width, eval_flags, n->expr[iarg], sym); } /* Set up the result scalar. We re-use the first argument if no one else is using it. */ if (n->numargs > 0 && args[0]->refcnt == 1) { result = args[0]; scalar_incr_ref(result); } else { result = new_scalar(width); } /* Loop over all values in scalar */ for (ivalue=0; ivalue < width; ivalue++) { /* Check the eval flag */ if (eval_flags != NULL && !eval_flags[ivalue]) continue; /* Get the values, checking for invalid values. */ found_invalid = FALSE; for (iarg=0; iarg < n->numargs; iarg++) { vals[iarg] = args[iarg]->vals[ivalue]; if (vals[iarg] == INVALID_VALUE) { found_invalid = TRUE; } } /* Debug */ if (debug) { (void) fprintf(stderr, "scalar %s:", node_name(n)); for (iarg=0; iarg < n->numargs; iarg++) (void) fprintf(stderr, " %g", vals[iarg]); (void) fprintf(stderr, "\n"); } /* Check for an invalid value. If we are testing for them, return 1.0, otherwise return an invalid value. */ if (found_invalid) { result->vals[ivalue] = ( (n->type == NODETYPE_ISNAN) ? 1.0 : INVALID_VALUE ); continue; } /* Do the operation */ switch (n->type) { case NODETYPE_ADD: result->vals[ivalue] = vals[0] + vals[1]; break; case NODETYPE_SUB: result->vals[ivalue] = vals[0] - vals[1]; break; case NODETYPE_MUL: result->vals[ivalue] = vals[0] * vals[1]; break; case NODETYPE_DIV: if (vals[1] == 0.0) result->vals[ivalue] = value_for_illegal_operations; else result->vals[ivalue] = vals[0] / vals[1]; break; case NODETYPE_LT: result->vals[ivalue] = vals[0] < vals[1]; break; case NODETYPE_LE: result->vals[ivalue] = vals[0] <= vals[1]; break; case NODETYPE_GT: result->vals[ivalue] = vals[0] > vals[1]; break; case NODETYPE_GE: result->vals[ivalue] = vals[0] >= vals[1]; break; case NODETYPE_EQ: result->vals[ivalue] = vals[0] == vals[1]; break; case NODETYPE_NE: result->vals[ivalue] = vals[0] != vals[1]; break; case NODETYPE_NOT: result->vals[ivalue] = (vals[0] == 0.0); break; case NODETYPE_AND: result->vals[ivalue] = (vals[0] != 0.0) && (vals[1] != 0.0); break; case NODETYPE_OR: result->vals[ivalue] = (vals[0] != 0.0) || (vals[1] != 0.0); break; case NODETYPE_ISNAN: /* We only get here if the value is valid */ result->vals[ivalue] = 0.0; break; case NODETYPE_POW: result->vals[ivalue] = pow(vals[0], vals[1]); break; case NODETYPE_SQRT: if (vals[0] < 0.0) result->vals[ivalue] = value_for_illegal_operations; else result->vals[ivalue] = sqrt(vals[0]); break; case NODETYPE_ABS: result->vals[ivalue] = fabs(vals[0]); break; case NODETYPE_EXP: result->vals[ivalue] = exp(vals[0]); break; case NODETYPE_LOG: if (vals[0] <= 0.0) result->vals[ivalue] = value_for_illegal_operations; else result->vals[ivalue] = log(vals[0]); break; case NODETYPE_SIN: result->vals[ivalue] = sin(vals[0]); break; case NODETYPE_COS: result->vals[ivalue] = cos(vals[0]); break; case NODETYPE_TAN: result->vals[ivalue] = tan(vals[0]); break; case NODETYPE_ASIN: result->vals[ivalue] = asin(vals[0]); break; case NODETYPE_ACOS: result->vals[ivalue] = acos(vals[0]); break; case NODETYPE_ATAN: result->vals[ivalue] = atan(vals[0]); break; case NODETYPE_CLAMP: if (vals[0] < vals[1]) result->vals[ivalue] = vals[1]; else if (vals[0] > vals[2]) result->vals[ivalue] = vals[2]; else result->vals[ivalue] = vals[0]; break; case NODETYPE_SEGMENT: result->vals[ivalue] = ( (vals[0] >= vals[1] && vals[0] <= vals[2]) ? 1.0 : 0.0); break; default: break; } /* switch on type */ } /* Loop over values of scalar */ /* Free the intermediate results */ for (iarg=0; iarg < n->numargs; iarg++) { scalar_free(args[iarg]); } /* Return the result vector */ return result; } /* If all args are scalar */ /* If we get here then we are not doing a simple scalar operation and we have to handle invalid values on a case-by-case basis. */ switch (n->type) { case NODETYPE_EXPRLIST: if (node_is_scalar(n->expr[0])) { s = eval_scalar(width, eval_flags, n->expr[0], sym); scalar_free(s); } else { v = eval_vector(width, eval_flags, n->expr[0], sym); vector_free(v); } return eval_scalar(width, eval_flags, n->expr[1], sym); case NODETYPE_INDEX: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_scalar(width, eval_flags, n->expr[1], sym); result = eval_index(width, eval_flags, n, v, s); vector_free(v); scalar_free(s); return result; case NODETYPE_SUM: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_sum(width, eval_flags, n, v); vector_free(v); return s; case NODETYPE_PROD: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_prod(width, eval_flags, n, v); vector_free(v); return s; case NODETYPE_AVG: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_sum(width, eval_flags, n, v); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; if (s->vals[ivalue] != INVALID_VALUE) s->vals[ivalue] /= (double) v->len; } vector_free(v); return s; case NODETYPE_LEN: v = eval_vector(width, eval_flags, n->expr[0], sym); s = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; s->vals[ivalue] = (double) v->len; } if (debug) { (void) fprintf(stderr, "len : %d\n", v->len); } vector_free(v); return s; case NODETYPE_MAX: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_max(width, eval_flags, n, v, 1.0, 0); vector_free(v); return s; case NODETYPE_MIN: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_max(width, eval_flags, n, v, -1.0, 0); vector_free(v); return s; case NODETYPE_IMAX: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_max(width, eval_flags, n, v, 1.0, 1); vector_free(v); return s; case NODETYPE_IMIN: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_max(width, eval_flags, n, v, -1.0, 1); vector_free(v); return s; case NODETYPE_FOR: return for_loop(width, eval_flags, n, sym); case NODETYPE_IDENT: s = sym_lookup_scalar(n->ident, sym); if (s == NULL) { return NULL; } result = new_scalar(width); for (ivalue = 0; ivalue < width; ivalue++) { result->vals[ivalue] = s->vals[ivalue]; } return result; case NODETYPE_REAL: s = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { s->vals[ivalue] = n->real; } return s; case NODETYPE_ASSIGN: s = eval_scalar(width, eval_flags, n->expr[0], sym); sym_set_scalar(width, eval_flags, s, n->ident, sym); return s; case NODETYPE_LET: if (ident_is_scalar(n->ident)) { s = eval_scalar(width, eval_flags, n->expr[0], sym); sym_set_scalar(width, eval_flags, s, n->ident, sym); scalar_free(s); } else { v = eval_vector(width, eval_flags, n->expr[0], sym); sym_set_vector(width, eval_flags, v, n->ident, sym); vector_free(v); } s = eval_scalar(width, eval_flags, n->expr[1], sym); return s; case NODETYPE_IFELSE: /* Do the test */ s = eval_scalar(width, eval_flags, n->expr[0], sym); /* Set the eval flags based on the results. Keep track of invalid data in the expression - we will not evaluate either part in that case. */ eval_flags2 = malloc(sizeof(eval_flags[0]) * width); isnan_flags = malloc(sizeof(eval_flags[0]) * width); all_true = TRUE; all_false = TRUE; for (ivalue=0; ivalue < width; ivalue++) { isnan_flags[ivalue] = (s->vals[ivalue] == INVALID_VALUE); eval_flags2[ivalue] = ((eval_flags == NULL ? 1 : eval_flags[ivalue]) && (s->vals[ivalue] != 0.0) && (!isnan_flags[ivalue])); if (eval_flags2[ivalue]) all_false = FALSE; else all_true = FALSE; } scalar_free(s); if (all_true || all_false) { free(eval_flags2); eval_flags2 = NULL; } /* Evaluate the then part */ s = NULL; if (!all_false) { s = eval_scalar(width, eval_flags2, n->expr[1], sym); } /* Evaluate the else part if needed - remember to invert the flags */ s2 = NULL; if (!all_true && n->numargs > 2) { if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) eval_flags2[ivalue] = !eval_flags2[ivalue] && !isnan_flags[ivalue]; } s2 = eval_scalar(width, eval_flags2, n->expr[2], sym); if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) eval_flags2[ivalue] = !eval_flags2[ivalue] && !isnan_flags[ivalue]; } } /* Make sure that we have an answer */ if (s == NULL) { if (s2 != NULL) { s = s2; s2 = NULL; } else { s = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) s->vals[ivalue] = 0.0; } } /* Merge the results */ if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) { if (!eval_flags2[ivalue]) { s->vals[ivalue] = (n->numargs > 2 ? s2->vals[ivalue] : 0.0); } } } /* Mark appropriate invalid values */ for (ivalue=0; ivalue < width; ivalue++) { if (isnan_flags[ivalue]) { s->vals[ivalue] = value_for_illegal_operations; } } /* Free things and return */ if (s2 != NULL) scalar_free(s2); if (eval_flags2 != NULL) free(eval_flags2); if (isnan_flags != NULL) free(isnan_flags); return s; default: eval_error(n, "expected a scalar value"); /* NOTREACHED */ return 0; } } /* Index into a vector */ scalar_t eval_index(int width, int *eval_flags, node_t n, vector_t v, scalar_t i){ scalar_t s; int idx; int ivalue; s = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; idx = SCALAR_ROUND(i->vals[ivalue]); if (idx < 0 || idx >= v->len) eval_error(n, "index out of bounds"); s->vals[ivalue] = v->el[idx]->vals[ivalue]; if (debug) (void) fprintf(stderr, "Index [%d] = %g\n", idx, s->vals[ivalue]); } return s; } /* Perform a sum over the arguments */ scalar_t eval_sum(int width, int *eval_flags, node_t n, vector_t v) { int i, ivalue; scalar_t result; double value; int found_invalid, found_valid; result = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; result->vals[ivalue] = 0.0; found_invalid = found_valid = FALSE; for (i = 0; i < v->len; i++) { value = v->el[i]->vals[ivalue]; if (value == INVALID_VALUE) found_invalid = TRUE; else { result->vals[ivalue] += value; found_valid = TRUE; } } if ((found_invalid && propagate_nan) || !found_valid) { result->vals[ivalue] = value_for_illegal_operations; } } return result; } /* Perform a product over the arguments */ scalar_t eval_prod(int width, int *eval_flags, node_t n, vector_t v) { int i, ivalue; scalar_t result; double value; int found_invalid, found_valid; result = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; result->vals[ivalue] = 1.0; found_invalid = found_valid = FALSE; for (i = 0; i < v->len; i++) { value = v->el[i]->vals[ivalue]; if (value == INVALID_VALUE) found_invalid = TRUE; else { result->vals[ivalue] *= value; found_valid = TRUE; } } if ((found_invalid && propagate_nan) || !found_valid) { result->vals[ivalue] = value_for_illegal_operations; } } return result; } /* Find the maximum of a vector. Sign should be +1.0 for maxima search and -1.0 for minima search. type should be 0 for value and 1 for index */ scalar_t eval_max(int width, int *eval_flags, node_t n, vector_t v, double sign, int type) { int i, ivalue; scalar_t result; double value, max, idx; result = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; result->vals[ivalue] = max = INVALID_VALUE; for (i = 0; i < v->len; i++) { value = v->el[i]->vals[ivalue]; if (value != INVALID_VALUE) { if (max == INVALID_VALUE || (sign*(value-max) > 0.0)) { max = value; idx = (double)i; } } } result->vals[ivalue] = (type == 0) ? max : idx; } return result; } /* Evaluate an expression in a vector context */ vector_t eval_vector(int width, int *eval_flags, node_t n, sym_t sym){ vector_t v, v2; scalar_t s; int ivalue, iel; int *eval_flags2, *isnan_flags; int all_true, all_false; /* Check that node is of correct type */ if (node_is_scalar(n)) { eval_error(n, "Expression is not a vector"); } switch (n->type) { case NODETYPE_EXPRLIST: if (node_is_scalar(n->expr[0])) { s = eval_scalar(width, eval_flags, n->expr[0], sym); scalar_free(s); } else { v = eval_vector(width, eval_flags, n->expr[0], sym); vector_free(v); } return eval_vector(width, eval_flags, n->expr[1], sym); case NODETYPE_ASSIGN: v = eval_vector(width, eval_flags, n->expr[0], sym); sym_set_vector(width, eval_flags, v, n->ident, sym); return v; case NODETYPE_LET: if (ident_is_scalar(n->ident)) { s = eval_scalar(width, eval_flags, n->expr[0], sym); sym_set_scalar(width, eval_flags, s, n->ident, sym); scalar_free(s); } else { v = eval_vector(width, eval_flags, n->expr[0], sym); sym_set_vector(width, eval_flags, v, n->ident, sym); vector_free(v); } v = eval_vector(width, eval_flags, n->expr[1], sym); return v; case NODETYPE_VEC2: v = eval_vector(width, eval_flags, n->expr[0], sym); s = eval_scalar(width, eval_flags, n->expr[1], sym); vector_append(v, s); scalar_free(s); return v; case NODETYPE_VEC1: s = eval_scalar(width, eval_flags, n->expr[0], sym); v = new_vector(); vector_append(v, s); scalar_free(s); return v; case NODETYPE_GEN: return gen_vector(width, eval_flags, n, sym); case NODETYPE_RANGE: return gen_range(width, eval_flags, n, sym); case NODETYPE_IFELSE: /* Do the test */ s = eval_scalar(width, eval_flags, n->expr[0], sym); /* Set the eval flags based on the results */ eval_flags2 = malloc(sizeof(eval_flags[0]) * width); isnan_flags = malloc(sizeof(eval_flags[0]) * width); all_true = TRUE; all_false = TRUE; for (ivalue=0; ivalue < width; ivalue++) { isnan_flags[ivalue] = (s->vals[ivalue] == INVALID_VALUE); eval_flags2[ivalue] = ((eval_flags == NULL ? 1 : eval_flags[ivalue]) && (s->vals[ivalue] != 0.0) && (!isnan_flags[ivalue])); if (eval_flags2[ivalue]) all_false = FALSE; else all_true = FALSE; } scalar_free(s); if (all_true || all_false) { free(eval_flags2); eval_flags2 = NULL; } /* Evaluate the then part */ v = NULL; if (!all_false) { v = eval_vector(width, eval_flags2, n->expr[1], sym); } /* Evaluate the else part if needed - remember to invert the flags */ v2 = NULL; if (!all_true && n->numargs > 2) { if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) eval_flags2[ivalue] = !eval_flags2[ivalue] && !isnan_flags[ivalue]; } v2 = eval_vector(width, eval_flags2, n->expr[2], sym); if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) eval_flags2[ivalue] = !eval_flags2[ivalue] && !isnan_flags[ivalue]; } } /* Make sure that we have an answer */ if (v == NULL) { if (v2 != NULL) { v = v2; v2 = NULL; } else { v = new_vector(); } } /* Merge the results */ if (v2 != NULL && v->len != v2->len) { eval_error(n, "VIO_Vector expressions in if-else do not have the same length"); } if (eval_flags2 != NULL) { for (ivalue=0; ivalue < width; ivalue++) { if (!eval_flags2[ivalue]) { for (iel=0; iel < v->len; iel++) { v->el[iel]->vals[ivalue] = (n->numargs > 2 ? v2->el[iel]->vals[ivalue] : 0.0); } } } } /* Mark appropriate invalid values */ for (ivalue=0; ivalue < width; ivalue++) { if (isnan_flags[ivalue]) { for (iel=0; iel < v->len; iel++) { v->el[iel]->vals[ivalue] = value_for_illegal_operations; } } } /* Free things and return */ if (v2 != NULL) vector_free(v2); if (eval_flags2 != NULL) free(eval_flags2); if (isnan_flags != NULL) free(isnan_flags); return v; case NODETYPE_IDENT: v = sym_lookup_vector(n->ident, sym); if (v) { vector_incr_ref(v); return v; } /* fallthrough */ default: /* XXX coerce scalar to vector! */ v = new_vector(); s = eval_scalar(width, eval_flags, n, sym); vector_append(v, s); scalar_free(s); return v; } } /* Generate a vector */ vector_t gen_vector(int width, int *eval_flags, node_t n, sym_t sym){ int i; scalar_t value; ident_t ident; node_t expr; vector_t v; vector_t els; ident = n->ident; if (!ident_is_scalar(ident)) eval_error(n, "expected scalar (lowercase) index as 1st arg"); els = eval_vector(width, eval_flags, n->expr[0], sym); expr = n->expr[1]; v = new_vector(); for (i = 0; i < els->len; i++) { value = els->el[i]; scalar_incr_ref(value); sym_set_scalar(width, eval_flags, value, ident, sym); scalar_free(value); value = eval_scalar(width, eval_flags, expr, sym); vector_append(v, value); scalar_free(value); } vector_free(els); return v; } /* Implement a for loop */ scalar_t for_loop(int width, int *eval_flags, node_t n, sym_t sym){ int i, ivalue; scalar_t value; ident_t ident; node_t expr; vector_t els; ident = n->ident; if (!ident_is_scalar(ident)) eval_error(n, "expected scalar (lowercase) index as 1st arg"); els = eval_vector(width, eval_flags, n->expr[0], sym); expr = n->expr[1]; for (i = 0; i < els->len; i++) { if (debug) { (void) fprintf(stderr, "For loop iteration %d\n", i); } value = els->el[i]; scalar_incr_ref(value); sym_set_scalar(width, eval_flags, value, ident, sym); scalar_free(value); value = eval_scalar(width, eval_flags, expr, sym); scalar_free(value); } vector_free(els); value = new_scalar(width); for (ivalue=0; ivalue < width; ivalue++) { value->vals[ivalue] = (double) i; } return value; } vector_t gen_range(int width, int *eval_flags, node_t n, sym_t sym){ int i, ivalue; scalar_t start; scalar_t stop; vector_t v; int length; v = new_vector(); start = eval_scalar(width, eval_flags, n->expr[0], sym); stop = eval_scalar(width, eval_flags, n->expr[1], sym); for (ivalue = 0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; start->vals[ivalue] = SCALAR_ROUND(start->vals[ivalue]); stop->vals[ivalue] = SCALAR_ROUND(stop->vals[ivalue]); if (!(n->flags & RANGE_EXACT_LOWER)) start->vals[ivalue]++; if (!(n->flags & RANGE_EXACT_UPPER)) stop->vals[ivalue]--; if (ivalue == 0) { length = stop->vals[ivalue] - start->vals[ivalue]; } else if (length != (int) (stop->vals[ivalue] - start->vals[ivalue])) { eval_error(n, "Vectors must have same size in vector generator"); } } length++; scalar_free(stop); for (i = 0; i < length ; i++) { stop = new_scalar(width); for (ivalue = 0; ivalue < width; ivalue++) { if (eval_flags != NULL && !eval_flags[ivalue]) continue; stop->vals[ivalue] = start->vals[ivalue] + i; if (debug) { (void) fprintf(stderr, "Range %d -> %d\n", i, (int) stop->vals[ivalue]); } } vector_append(v, stop); scalar_free(stop); } scalar_free(start); return v; } minc-tools-2.3.00+dfsg/progs/minccalc/gram.y0000644000175000000620000002671212574624760017655 0ustar stevestaff%{ #include #include #include #include #include #include "node.h" #define INVALID_VALUE (-DBL_MAX) /* Avoid problems with conflicting declarations */ void yyerror(const char *msg); %} %union{ int pos; node_t node; double real; ident_t ident; } %token NAN %token IN TO IDENT REAL AVG PROD SUM LET NEG LEN MAX MIN IMAX IMIN %token ISNAN SQRT ABS EXP LOG SIN COS TAN ASIN ACOS ATAN CLAMP SEGMENT %token LT LE GT GE EQ NE NOT AND OR %token IF ELSE FOR %type IDENT %type REAL %type IN TO AVG SUM PROD LET NEG LEN IF ELSE FOR %type ISNAN SQRT ABS MAX MIN IMAX IMIN EXP LOG SIN COS TAN ASIN ACOS ATAN %type CLAMP SEGMENT %type NOT LT LE GT GE EQ NE AND OR %type '+' '-' '*' '/' '(' ')' '[' ']' '.' '=' '^' '{' '}' ',' '|' ';' %type ':' '?' %type exprlist expr letexpr vector %right '=' %right LET %right '?' %left OR %left AND %left EQ NE %left LT LE GT GE %left '-' '+' %left '*' '/' %right NEG NOT %right '^' %right AVG SUM PROD LEN ISNAN SQRT ABS MAX MIN IMAX IMIN EXP LOG SIN COS TAN ASIN ACOS ATAN %% top : exprlist { root = $1; } ; exprlist : expr ';' exprlist { $$ = new_node(2, node_is_scalar($3)); $$->type = NODETYPE_EXPRLIST; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr ';' { $$ = $1; } | expr { $$ = $1; } ; expr : '(' expr ')' { $$ = $2; } | '{' exprlist '}' { $$ = $2; } | '[' vector ']' { $$ = $2; } | '{' IDENT IN expr '|' expr '}' { $$ = new_vector_node(2); $$->type = NODETYPE_GEN; $$->pos = $7; $$->ident = $2; $$->expr[0] = $4; $$->expr[1] = $6; } | '(' expr ':' expr ')' { $$ = new_vector_node(2); $$->type = NODETYPE_RANGE; $$->flags = 0; $$->pos = $5; $$->expr[0] = $2; $$->expr[1] = $4; } | '(' expr ':' expr ']' { $$ = new_vector_node(2); $$->type = NODETYPE_RANGE; $$->flags = RANGE_EXACT_UPPER; $$->pos = $5; $$->expr[0] = $2; $$->expr[1] = $4; } | '[' expr ':' expr ')' { $$ = new_vector_node(2); $$->type = NODETYPE_RANGE; $$->flags = RANGE_EXACT_LOWER; $$->pos = $5; $$->expr[0] = $2; $$->expr[1] = $4; } | '[' expr ':' expr ']' { $$ = new_vector_node(2); $$->type = NODETYPE_RANGE; $$->flags = RANGE_EXACT_UPPER | RANGE_EXACT_LOWER; $$->pos = $5; $$->expr[0] = $2; $$->expr[1] = $4; } | expr '+' expr { $$ = new_scalar_node(2); $$->type = NODETYPE_ADD; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr '-' expr { $$ = new_scalar_node(2); $$->type = NODETYPE_SUB; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | '-' expr %prec NEG { $$ = new_scalar_node(2); $$->type = NODETYPE_SUB; $$->pos = $1; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = new_scalar_node(0); $$->expr[0]->type = NODETYPE_REAL; $$->expr[0]->real = 0.0; $$->expr[1] = $2; } | expr '*' expr { $$ = new_scalar_node(2); $$->pos = $2; $$->type = NODETYPE_MUL; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $1; $$->expr[1] = $3; } | expr '/' expr { $$ = new_scalar_node(2); $$->pos = $2; $$->type = NODETYPE_DIV; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $1; $$->expr[1] = $3; } | expr '^' expr { $$ = new_scalar_node(2); $$->pos = $2; $$->type = NODETYPE_POW; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $1; $$->expr[1] = $3; } | expr LT expr { $$ = new_scalar_node(2); $$->type = NODETYPE_LT; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr LE expr { $$ = new_scalar_node(2); $$->type = NODETYPE_LE; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr GT expr { $$ = new_scalar_node(2); $$->type = NODETYPE_GT; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr GE expr { $$ = new_scalar_node(2); $$->type = NODETYPE_GE; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr EQ expr { $$ = new_scalar_node(2); $$->type = NODETYPE_EQ; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr NE expr { $$ = new_scalar_node(2); $$->type = NODETYPE_NE; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr AND expr { $$ = new_scalar_node(2); $$->type = NODETYPE_AND; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr OR expr { $$ = new_scalar_node(2); $$->type = NODETYPE_OR; $$->flags |= ALLARGS_SCALAR; $$->pos = $2; $$->expr[0] = $1; $$->expr[1] = $3; } | expr '[' expr ']' { $$ = new_scalar_node(2); $$->type = NODETYPE_INDEX; $$->pos = $4; $$->expr[0] = $1; $$->expr[1] = $3; } | IDENT '=' expr { $$ = new_node(1, node_is_scalar($3)); $$->type = NODETYPE_ASSIGN; $$->pos = $2; $$->ident = $1; $$->expr[0] = $3; } | LET letexpr { $$ = $2; } | NOT expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_NOT; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | SUM expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_SUM; $$->expr[0] = $2; } | PROD expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_PROD; $$->expr[0] = $2; } | AVG expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_AVG; $$->expr[0] = $2; } | LEN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_LEN; $$->expr[0] = $2; } | MAX expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_MAX; $$->expr[0] = $2; } | MIN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_MIN; $$->expr[0] = $2; } | IMAX expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_IMAX; $$->expr[0] = $2; } | IMIN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_IMIN; $$->expr[0] = $2; } | ISNAN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_ISNAN; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | SQRT expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_SQRT; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | ABS expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_ABS; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | EXP expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_EXP; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | LOG expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_LOG; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | SIN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_SIN; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | COS expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_COS; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | TAN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_TAN; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | ASIN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_ASIN; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | ACOS expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_ACOS; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | ATAN expr { $$ = new_scalar_node(1); $$->pos = $1; $$->type = NODETYPE_ATAN; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $2; } | CLAMP '(' expr ',' expr ',' expr ')' { $$ = new_scalar_node(3); $$->pos = $1; $$->type = NODETYPE_CLAMP; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $3; $$->expr[1] = $5; $$->expr[2] = $7; } | SEGMENT '(' expr ',' expr ',' expr ')' { $$ = new_scalar_node(3); $$->pos = $1; $$->type = NODETYPE_SEGMENT; $$->flags |= ALLARGS_SCALAR; $$->expr[0] = $3; $$->expr[1] = $5; $$->expr[2] = $7; } | expr '?' expr ':' expr { $$ = new_node(3, node_is_scalar($3)); $$->pos = $2; $$->type = NODETYPE_IFELSE; $$->expr[0] = $1; $$->expr[1] = $3; $$->expr[2] = $5; } | IF '(' expr ')' expr ELSE expr { $$ = new_node(3, node_is_scalar($5)); $$->pos = $1; $$->type = NODETYPE_IFELSE; $$->expr[0] = $3; $$->expr[1] = $5; $$->expr[2] = $7; } | IF '(' expr ')' expr { $$ = new_node(2, node_is_scalar($5)); $$->pos = $1; $$->type = NODETYPE_IFELSE; $$->expr[0] = $3; $$->expr[1] = $5; } | FOR '{' IDENT IN expr '}' expr { $$ = new_scalar_node(2); $$->pos = $1; $$->type = NODETYPE_FOR; $$->ident = $3; $$->expr[0] = $5; $$->expr[1] = $7; } | IDENT { $$ = new_node(0, ident_is_scalar($1)); $$->type = NODETYPE_IDENT; $$->pos = -1; $$->ident = $1; } | REAL { $$ = new_scalar_node(0); $$->pos = -1; $$->type = NODETYPE_REAL; $$->real = $1; } | NAN { $$ = new_scalar_node(0); $$->pos = -1; $$->type = NODETYPE_REAL; $$->real = INVALID_VALUE; } ; letexpr : IDENT '=' expr ',' letexpr { $$ = new_scalar_node(2); $$->type = NODETYPE_LET; $$->pos = $2; $$->ident = $1; $$->expr[0] = $3; $$->expr[1] = $5; } | IDENT '=' expr IN expr { $$ = new_scalar_node(2); $$->pos = $2; $$->type = NODETYPE_LET; $$->ident = $1; $$->expr[0] = $3; $$->expr[1] = $5; } ; vector : expr { $$ = new_vector_node(1); $$->pos = $1->pos; $$->type = NODETYPE_VEC1; $$->expr[0] = $1; } | vector ',' expr { $$ = new_vector_node(2); $$->pos = $2; $$->type = NODETYPE_VEC2; $$->expr[0] = $1; $$->expr[1] = $3; } ; %% node_t root; void yyerror(msg) const char *msg; { extern int lexpos; show_error(lexpos, msg); } minc-tools-2.3.00+dfsg/progs/minccalc/node.c0000644000175000000620000000511512574624760017620 0ustar stevestaff/* Copyright David Leonard and Andrew Janke, 2000. All rights reserved. */ #include #include "node.h" struct nodename { enum nodetype type; const char *name; } nodenames[] = { { NODETYPE_ADD, "add" }, { NODETYPE_SUB, "sub" }, { NODETYPE_MUL, "mul" }, { NODETYPE_DIV, "div" }, { NODETYPE_POW, "pow" }, { NODETYPE_INDEX, "index" }, { NODETYPE_SUM, "sum" }, { NODETYPE_PROD, "prod" }, { NODETYPE_AVG, "avg" }, { NODETYPE_LEN, "len" }, { NODETYPE_MAX, "max" }, { NODETYPE_MIN, "min" }, { NODETYPE_IMAX, "imax" }, { NODETYPE_IMIN, "imin" }, { NODETYPE_IDENT, "ident" }, { NODETYPE_REAL, "real" }, { NODETYPE_LET, "let" }, { NODETYPE_VEC1, "vec1" }, { NODETYPE_VEC2, "vec2" }, { NODETYPE_GEN, "gen" }, { NODETYPE_RANGE, "range" }, { NODETYPE_LT, "lt" }, { NODETYPE_LE, "le" }, { NODETYPE_GT, "gt" }, { NODETYPE_GE, "ge" }, { NODETYPE_EQ, "eq" }, { NODETYPE_NE, "ne" }, { NODETYPE_NOT, "not" }, { NODETYPE_AND, "and" }, { NODETYPE_OR, "or" }, { NODETYPE_ISNAN, "isnan" }, { NODETYPE_SQRT, "sqrt" }, { NODETYPE_ABS, "abs" }, { NODETYPE_EXP, "exp" }, { NODETYPE_LOG, "log" }, { NODETYPE_SIN, "sin" }, { NODETYPE_COS, "cos" }, { NODETYPE_TAN, "tan" }, { NODETYPE_ASIN, "asin" }, { NODETYPE_ACOS, "acos" }, { NODETYPE_ATAN, "atan" }, { NODETYPE_CLAMP, "clamp" }, { NODETYPE_SEGMENT, "segment" }, { NODETYPE_EXPRLIST, "exprlist" }, { NODETYPE_ASSIGN, "assign" }, { NODETYPE_IFELSE, "ifelse" }, { NODETYPE_FOR, "for" }, { (enum nodetype) 0, NULL } }; node_t new_node(int numargs, int is_scalar) { node_t n; n = malloc(sizeof *n); n->numargs = numargs; n->flags = 0; if (is_scalar) { n->flags |= NODE_IS_SCALAR; } return n; } node_t new_scalar_node(int numargs) { return new_node(numargs, 1); } node_t new_vector_node(int numargs) { return new_node(numargs, 0); } const char *node_name(node_t n){ struct nodename *p; for (p = nodenames; p->name; p++) if (p->type == n->type) return p->name; return "unknown"; } int node_is_scalar(node_t n) { return (n->flags & NODE_IS_SCALAR); } minc-tools-2.3.00+dfsg/progs/mincsample/0002755000175000000620000000000012574624760017106 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincsample/mincsample.man10000644000175000000620000000643512574624760022022 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 2004 Andrew Janke, 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. .\" .\" .TH MINCSAMPLE 1 "MINC User's Guide" .SH NAME mincsample - generate samplings from minc files. .SH SYNOPSIS .B mincsample [] [ [<..>]] .SH DESCRIPTION \fIMincsample\fR produces a data sampling on STDOUT from an input series of minc files. The output can be either ascii (-ascii) or as a raw binary stream of doubles (-double). The output data is ordered first by file then voxel. When -ascii is used the data values from each file are separated by a tab and the sampling points with a newline. When using -double, no separators are used. If -coords is also specified, the world co-ordinate at each sampling point will precede the data from each of the files. An optional -outfile argument can also be used to direct the output to a file and -append used to append the data to the file as opposed to overwriting the file. By default all data points are written out (-all) the output of points can also be constrained to be points within a mask (-mask and -mask_val) and further by a random sampling of a sub-set of points via the -random_samples and -random_seed arguments .SH OPTIONS .TP \fB\-verbose\fR Print extra information during processing. .TP \fB\-quiet\fR Print only the ouput sampling data. .TP \fB\-clobber\fR Overwrite existing output files (when used with -outfile) .TP \fB\-max_buffer\fR \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4096 (4MB). .TP \fB\-mask\fR \fImask.mnc\fR Specify and input mask, only sampling points within this mask will be used. .TP \fB\-mask_val\fR \fIvalue\fR Specify the value to use from the mask (Default: 1). .TP \fB\-all\fR Sample all the data points. .TP \fB\-random_seed\fR \fIvalue\fR Specify the random seed value to use during random sampling, this is to enable reproducible runs. If no seed is given a semi-random seed will be chosen (from time). .TP \fB\-random_samples\fR \fIvalue\fR Specify the number of random samples to take from the input files. This value must be smaller than the maximum possible number of samples. .TP \fB\-sample\fR \fIsample.mnc\fR Output a mask file that corresponds to where samples were taken from. .TP \fB\-outfile\fR \fIfile\fR Output sampling data to a file. (Default: STDOUT). .TP \fB\-append\fR Append output data to an existing file. .TP \fB\-ascii\fR Write out data as ascii strings (Default). .TP \fB\-ascii\fR Write out data as double precision floating-point values. .TP \fB\-coords\fR Write out world co-ordinates as well as sampling values. .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Andrew Janke and Mark Griffin .SH COPYRIGHTS Copyright \(co 2004 by Andrew Janke and Mark Griffin minc-tools-2.3.00+dfsg/progs/mincsample/mt19937ar.c0000644000175000000620000001437612574624760020643 0ustar stevestaff/* A C-program for MT19937, with initialization improved 2002/1/26. Coded by Takuji Nishimura and Makoto Matsumoto. Before using, initialize the state by using init_genrand(seed) or init_by_array(init_key, key_length). Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND TRUE ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any feedback is very welcome. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ /* Period parameters */ #define N 624 #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ static unsigned long mt[N]; /* the array for the state vector */ static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ /* initializes mt[N] with a seed */ void init_genrand(unsigned long s) { mt[0]= s & 0xffffffffUL; for (mti=1; mti> 30)) + mti); /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ mt[mti] &= 0xffffffffUL; /* for >32 bit machines */ } } /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ void init_by_array(unsigned long init_key[], int key_length) { int i, j, k; init_genrand(19650218UL); i=1; j=0; k = (N>key_length ? N : key_length); for (; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; /* non linear */ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ i++; j++; if (i>=N) { mt[0] = mt[N-1]; i=1; } if (j>=key_length) j=0; } for (k=N-1; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - i; /* non linear */ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ i++; if (i>=N) { mt[0] = mt[N-1]; i=1; } } mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ } /* generates a random number on [0,0xffffffff]-interval */ unsigned long genrand_int32(void) { unsigned long y; static unsigned long mag01[2]={0x0UL, MATRIX_A}; /* mag01[x] = x * MATRIX_A for x=0,1 */ if (mti >= N) { /* generate N words at one time */ int kk; if (mti == N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; } for (;kk> 1) ^ mag01[y & 0x1UL]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } y = mt[mti++]; /* Tempering */ y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } /* generates a random number on [0,0x7fffffff]-interval */ long genrand_int31(void) { return (long)(genrand_int32()>>1); } /* generates a random number on [0,1]-real-interval */ double genrand_real1(void) { return genrand_int32()*(1.0/4294967295.0); /* divided by 2^32-1 */ } /* generates a random number on [0,1)-real-interval */ double genrand_real2(void) { return genrand_int32()*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on (0,1)-real-interval */ double genrand_real3(void) { return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); /* divided by 2^32 */ } /* generates a random number on [0,1) with 53-bit resolution*/ double genrand_res53(void) { unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; return(a*67108864.0+b)*(1.0/9007199254740992.0); } /* These real versions are due to Isaku Wada, 2002/01/09 added */ // int main(void) // { // int i; // unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4; // init_by_array(init, length); // printf("1000 outputs of genrand_int32()\n"); // for (i=0; i<1000; i++) { // printf("%10lu ", genrand_int32()); // if (i%5==4) printf("\n"); // } // printf("\n1000 outputs of genrand_real2()\n"); // for (i=0; i<1000; i++) { // printf("%10.8f ", genrand_real2()); // if (i%5==4) printf("\n"); // } // return 0; // } minc-tools-2.3.00+dfsg/progs/mincsample/mt19937ar.h0000644000175000000620000000541712574624760020644 0ustar stevestaff/* A C-program for MT19937, with initialization improved 2002/1/26. Coded by Takuji Nishimura and Makoto Matsumoto. Before using, initialize the state by using init_genrand(seed) or init_by_array(init_key, key_length). Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND TRUE ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any feedback is very welcome. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ /* initializes mt[N] with a seed */ void init_genrand(unsigned long s); /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ void init_by_array(unsigned long init_key[], int key_length); /* generates a random number on [0,0xffffffff]-interval */ unsigned long genrand_int32(void); /* generates a random number on [0,0x7fffffff]-interval */ long genrand_int31(void); /* generates a random number on [0,1]-real-interval */ double genrand_real1(void); /* generates a random number on [0,1)-real-interval */ double genrand_real2(void); /* generates a random number on (0,1)-real-interval */ double genrand_real3(void); /* generates a random number on [0,1) with 53-bit resolution*/ double genrand_res53(void); minc-tools-2.3.00+dfsg/progs/mincsample/mincsample.c0000644000175000000620000004506612574624760021413 0ustar stevestaff/* mincsample.c * * Generate samplings from MINC files * * Andrew Janke - a.janke@gmail.com * Mark Griffin * * Copyright Andrew Janke and Mark Griffin, McConnell Brain Imaging Centre * 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 the 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 #include #include #include #include #include #include #include "mt19937ar.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define WORLD_NDIMS 3 #define DEFAULT_INT -1 /* typedefs */ typedef enum { SAMPLE_ALL, SAMPLE_RND } Sample_enum; typedef enum { OUTPUT_ASCII, OUTPUT_DOUBLE } Output_enum; typedef struct { Sample_enum sample_type; /* input parameters */ int masking; double mask_val; int mask_idx; /* sampling */ int rand_samples; int max_samples; /* output parameters */ int sample_mask; int sample_mask_idx; Output_enum output_type; int output_coords; FILE *outFP; } Loop_Data; /* function prototypes */ void count_points(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info); void get_points(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info); void write_data(FILE * fp, double value, Output_enum ot); void get_minc_attribute(int mincid, char *varname, char *attname, int maxvals, double vals[]); int get_minc_ndims(int mincid); void find_minc_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]); void get_minc_voxel_to_world(int mincid, double voxel_to_world[WORLD_NDIMS][WORLD_NDIMS + 1]); void normalize_vector(double vector[]); void transform_coord(double out_coord[], double transform[WORLD_NDIMS][WORLD_NDIMS + 1], double in_coord[]); /* global vars for printing coordinates */ int space_to_dim[WORLD_NDIMS] = { -1, -1, -1 }; int dim_to_space[MAX_VAR_DIMS]; int file_ndims = 0; double voxel_to_world[WORLD_NDIMS][WORLD_NDIMS + 1]; /* Argument variables and table */ static int verbose = FALSE; static int quiet = FALSE; static int clobber = FALSE; static int max_buffer = 4 * 1024; static char *mask_fname = NULL; static char *sample_fname = NULL; static char *out_fname = NULL; static int append_output = FALSE; static int rand_seed = DEFAULT_INT; static Loop_Data md = { SAMPLE_ALL, FALSE, 1.0, 0, 0, 0, FALSE, 0, OUTPUT_ASCII, FALSE, NULL }; static 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, "be very quiet."}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "clobber existing files."}, {"-max_buffer", ARGV_INT, (char *)1, (char *)&max_buffer, "maximum size of buffers (in kbytes)"}, {"-mask", ARGV_STRING, (char *)1, (char *)&mask_fname, "select voxels within the specified mask"}, {"-mask_val", ARGV_FLOAT, (char *)1, (char *)&(md.mask_val), "mask value to use"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nSampling Types:"}, {"-all", ARGV_CONSTANT, (char *)SAMPLE_ALL, (char *)&md.sample_type, "sample all the data (Default)"}, {"-random_seed", ARGV_INT, (char *)1, (char *)&rand_seed, "Random seed to use (use to get reproducible runs) Default: use tv_usec"}, {"-random_samples", ARGV_INT, (char *)1, (char *)&md.rand_samples, "take # random samples from the input data"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nOutput Options:"}, {"-sample", ARGV_STRING, (char *)1, (char *)&sample_fname, "Output a file of chosen points"}, {"-outfile", ARGV_STRING, (char *)1, (char *)&out_fname, " for output data (Default: stdout)"}, {"-append", ARGV_CONSTANT, (char *)TRUE, (char *)&append_output, "append output data to existing file"}, {"-ascii", ARGV_CONSTANT, (char *)OUTPUT_ASCII, (char *)&md.output_type, "Write out data as ascii strings (default)"}, {"-double", ARGV_CONSTANT, (char *)OUTPUT_DOUBLE, (char *)&md.output_type, "Write out data as double precision floating-point values"}, {"-coords", ARGV_CONSTANT, (char *)TRUE, (char *)&md.output_coords, "Write out world co-ordinates as well as values"}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { char **infiles; char *outfiles[1]; int n_infiles, n_outfiles; char *arg_string; Loop_Options *loop_opts; int mincid; struct timeval timer; int i; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* get arguments */ if(ParseArgv(&argc, argv, argTable, 0) || (argc < 2)){ fprintf(stderr, "\nUsage: %s [options] ... \n", argv[0]); fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } /* check arguments */ if(md.rand_samples != 0){ md.sample_type = SAMPLE_RND; if(md.rand_samples < 0){ fprintf(stderr, "%s: -rand_samples (%d) must be greater than 0\n\n", argv[0], md.rand_samples); exit(EXIT_FAILURE); } } /* check arguments */ if(rand_seed != DEFAULT_INT && rand_seed < 0){ fprintf(stderr, "%s: -rand_seed (%d) must be 0 or greater\n\n", argv[0], rand_seed); exit(EXIT_FAILURE); } /* get infile names */ n_infiles = argc - 1; infiles = (char **)malloc(sizeof(char *) * (n_infiles + 1)); /* + 1 for mask */ for(i = 0; i < n_infiles; i++){ infiles[i] = argv[i + 1]; } if(mask_fname != NULL){ infiles[n_infiles] = mask_fname; md.masking = TRUE; md.mask_idx = n_infiles; n_infiles++; } /* check for the infile(s) */ for(i = 0; i < n_infiles; i++){ if(access(infiles[i], F_OK) != 0){ fprintf(stderr, "%s: Couldn't find input file: %s\n\n", argv[0], infiles[i]); exit(EXIT_FAILURE); } } /* set up and check for voxel_loop outfiles */ if(sample_fname != NULL){ if(access(sample_fname, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists, use -clobber to overwrite\n\n", argv[0], sample_fname); exit(EXIT_FAILURE); } md.sample_mask = TRUE; md.sample_mask_idx = 0; n_outfiles = 1; outfiles[0] = sample_fname; } else { n_outfiles = 0; } /* set up data outfile */ if(out_fname == NULL || strcmp(out_fname, "-") == 0){ md.outFP = stdout; } else { if(!append_output && access(out_fname, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists, use -clobber to overwrite\n\n", argv[0], out_fname); exit(EXIT_FAILURE); } if((md.outFP = fopen(out_fname, (append_output) ? "a" : "w")) == NULL){ fprintf(stderr, "%s: problems opening %s\n", argv[0], out_fname); exit(EXIT_FAILURE); } } /* Get some information from the first file for printing co-ordinates */ mincid = miopen(infiles[0], NC_NOWRITE | 0x8000); file_ndims = get_minc_ndims(mincid); find_minc_spatial_dims(mincid, space_to_dim, dim_to_space); get_minc_voxel_to_world(mincid, voxel_to_world); /* set up voxel loop options */ loop_opts = create_loop_options(); set_loop_verbose(loop_opts, FALSE); set_loop_clobber(loop_opts, clobber); set_loop_buffer_size(loop_opts, (long)1024 * max_buffer); /* set up random sampling if required */ if(md.sample_type == SAMPLE_RND){ void *tmp = NULL; /* for gettimeofday */ /* get max number of samples */ voxel_loop(1, (md.masking) ? &mask_fname : infiles, 0, NULL, NULL, loop_opts, count_points, (void *)&md); if(verbose){ fprintf(stderr, " | Got max # of points: %d\n", md.max_samples); } if(md.rand_samples > md.max_samples){ fprintf(stderr, "%s: -rand_samples (%d) must be less than max samples (%d)\n\n", argv[0], md.rand_samples, md.max_samples); exit(EXIT_FAILURE); } /* initialise random number generator */ if(rand_seed == DEFAULT_INT){ gettimeofday(&timer, tmp); rand_seed = timer.tv_usec; } if(verbose){ fprintf(stderr, " | Using random seed: %d\n", rand_seed); } init_genrand((unsigned long)rand_seed); } /* do the sampling */ voxel_loop(n_infiles, infiles, n_outfiles, outfiles, arg_string, loop_opts, get_points, (void *)&md); /* tidy up */ fclose(md.outFP); free_loop_options(loop_opts); return (EXIT_SUCCESS); } /* get points from file(s), write out to an input FP */ void get_points(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info) { Loop_Data *md = (Loop_Data *) caller_data; int i, idim, ivox; int n_infiles; int do_sample; double mask_value; int dim_index; long index[MAX_VAR_DIMS]; double voxel_coord[WORLD_NDIMS]; double world_coord[WORLD_NDIMS]; /* shut the compiler up */ (void)output_num_buffers; (void)output_vector_length; n_infiles = (md->masking) ? input_num_buffers - 1 : input_num_buffers; /* for each voxel */ for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++){ /* nasty way that works for masking or not */ mask_value = 0; if(!md->masking || (md->masking && fabs(input_data[md->mask_idx][ivox] - md->mask_val) < 0.5)){ /* flag to sample */ do_sample = FALSE; switch (md->sample_type){ case SAMPLE_ALL: do_sample = TRUE; mask_value = 1.0; break; case SAMPLE_RND: /* if this voxel 'qualifies', write out data */ if(genrand_res53() < ((double)md->rand_samples / md->max_samples)){ md->rand_samples--; do_sample = TRUE; mask_value = 1.0; } md->max_samples--; break; default: fprintf(stderr, "ERROR - Sample type is undefined (%d)\n", md->sample_type); exit(EXIT_FAILURE); } /* now write out the data */ if(do_sample){ /* get and convert voxel to world coordinates */ if(md->output_coords){ get_info_voxel_index(loop_info, ivox, file_ndims, index); for(idim = 0; idim < WORLD_NDIMS; idim++){ dim_index = space_to_dim[idim]; if(dim_index >= 0){ voxel_coord[idim] = index[dim_index]; } } transform_coord(world_coord, voxel_to_world, voxel_coord); } switch (md->output_type){ case OUTPUT_ASCII: if(md->output_coords){ fprintf(md->outFP, "%.20g\t%.20g\t%.20g\t", world_coord[0], world_coord[1], world_coord[2]); } for(i = 0; i < n_infiles; i++){ fprintf(md->outFP, "%.20g\t", input_data[i][ivox]); } fprintf(md->outFP, "\n"); break; case OUTPUT_DOUBLE: if(md->output_coords){ fwrite(&(world_coord[0]), sizeof(double), 1, md->outFP); fwrite(&(world_coord[1]), sizeof(double), 1, md->outFP); fwrite(&(world_coord[2]), sizeof(double), 1, md->outFP); } for(i = 0; i < n_infiles; i++){ fwrite(&(input_data[i][ivox]), sizeof(double), 1, md->outFP); } break; default: fprintf(stderr, "ERROR - Output type is undefined (%d)\n", md->output_type); exit(EXIT_FAILURE); } } } /* output sampling mask */ if(md->sample_mask){ output_data[md->sample_mask_idx][ivox] = mask_value; } } } /* count points that are within a mask */ void count_points(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info) { Loop_Data *md = (Loop_Data *) caller_data; int ivox; /* shut the compiler up */ (void)input_num_buffers; (void)output_num_buffers; (void)output_vector_length; (void)output_data; (void)loop_info; for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++){ if(md->masking){ if(fabs(input_data[0][ivox] - md->mask_val) < 0.5){ md->max_samples++; } } else { md->max_samples++; } } } inline void write_data(FILE * fp, double value, Output_enum ot) { switch (ot){ case OUTPUT_ASCII: fprintf(fp, "%.20g\n", value); break; case OUTPUT_DOUBLE: fwrite(&(value), sizeof(double), 1, fp); break; default: fprintf(stderr, "ERROR - Output type is undefined (%d)\n", ot); exit(EXIT_FAILURE); } } 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; } } } /* Transforms a coordinate through a linear transform -- from mincstats */ 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]; } } } /* Get the voxel to world transform (for column vectors) -- from mincstats */ void get_minc_voxel_to_world(int mincid, double voxel_to_world[WORLD_NDIMS][WORLD_NDIMS + 1]) { int idim, jdim; double dircos[WORLD_NDIMS]; double step, start; char *dimensions[] = { MIxspace, MIyspace, MIzspace }; /* Zero the matrix */ for(idim = 0; idim < WORLD_NDIMS; idim++){ for(jdim = 0; jdim < WORLD_NDIMS + 1; jdim++) voxel_to_world[idim][jdim] = 0.0; } for(jdim = 0; jdim < WORLD_NDIMS; jdim++){ /* Set default values */ step = 1.0; start = 0.0; for(idim = 0; idim < WORLD_NDIMS; idim++) dircos[idim] = 0.0; dircos[jdim] = 1.0; /* Get the attributes */ get_minc_attribute(mincid, dimensions[jdim], MIstart, 1, &start); get_minc_attribute(mincid, dimensions[jdim], MIstep, 1, &step); get_minc_attribute(mincid, dimensions[jdim], MIdirection_cosines, WORLD_NDIMS, dircos); normalize_vector(dircos); /* Put them in the matrix */ for(idim = 0; idim < WORLD_NDIMS; idim++){ voxel_to_world[idim][jdim] = step * dircos[idim]; voxel_to_world[idim][WORLD_NDIMS] += start * dircos[idim]; } } } /* Get the mapping from spatial dimension - x, y, z - to file dimensions and vice-versa. -- from mincstats */ void find_minc_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]) { int imgid, dim[MAX_VAR_DIMS]; int idim, ndims, world_index; char dimname[MAX_NC_NAME]; /* Set default values */ for(idim = 0; idim < 3; idim++) space_to_dim[idim] = -1; for(idim = 0; idim < MAX_VAR_DIMS; idim++) dim_to_space[idim] = -1; /* Get the dimension ids for the image variable */ imgid = ncvarid(mincid, MIimage); (void)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 */ (void)ncdiminq(mincid, dim[idim], dimname, NULL); if((dimname[0] == '\0') || (strcmp(&dimname[1], "space") != 0)){ 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; } } /* Get a double attribute from a minc file -- from mincstats */ void get_minc_attribute(int mincid, char *varname, char *attname, int maxvals, double vals[]) { int varid; int old_ncopts; int att_length; if(!mivar_exists(mincid, varname)) return; varid = ncvarid(mincid, varname); old_ncopts = ncopts; ncopts = 0; (void)miattget(mincid, varid, attname, NC_DOUBLE, maxvals, vals, &att_length); ncopts = old_ncopts; } /* Get the total number of image dimensions in a minc file -- from mincstats */ int get_minc_ndims(int mincid) { int imgid; int ndims; imgid = ncvarid(mincid, MIimage); (void)ncvarinq(mincid, imgid, NULL, NULL, &ndims, NULL, NULL); return ndims; } minc-tools-2.3.00+dfsg/progs/mincsample/README0000644000175000000620000000010112574624760017754 0ustar stevestaffAndrew Janke - a.janke@gmail.com http://a.janke.googlepages.com/ minc-tools-2.3.00+dfsg/progs/mincwindow/0002755000175000000620000000000012574624760017134 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincwindow/mincwindow.c0000644000175000000620000001756212574624760021467 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincwindow @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to limit voxel values to a given range @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : * $Log: mincwindow.c,v $ * Revision 6.7 2008-01-17 02:33:06 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.6 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.5 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.4 2005/08/26 21:07:18 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.3 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.2 2004/04/27 15:28:39 bert * Added -2 option * * Revision 6.1 1999/10/19 14:45:30 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:05 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:05 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:34 neelin * Release of minc version 0.4 * * Revision 3.2 1995/11/16 13:15:11 neelin * Added include of math.h to get declaration of strtod under SunOs * * Revision 3.1 1995/10/25 17:36:49 neelin * Fixed check of arguments: comparing char to NULL. * * Revision 3.0 1995/05/15 19:32:20 neelin * Release of minc version 0.3 * * Revision 2.4 1995/03/21 14:35:25 neelin * Changed usage message and handle vector volumes properly. * * Revision 2.3 1995/03/21 14:05:00 neelin * Modified calls to voxel_loop routines. * * Revision 2.2 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.1 1994/12/14 10:20:23 neelin * Changed to use standard (Proglib) voxel_loop routines. * * Revision 2.0 94/09/28 10:36:27 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:36:20 neelin * Pre-release * * Revision 1.3 94/01/12 10:18:33 neelin * Added logging. Turned off filling. Added miclose for files. * * Revision 1.2 94/01/11 15:56:17 neelin * Added optional newvalue argument. * * Revision 1.1 94/01/11 15:02:56 neelin * Initial revision * ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Structure for window information */ typedef struct { int use_newvalue; double newvalue; double minimum; double maximum; } Window_Data; /* Function prototypes */ static void do_window(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; #if MINC2 static int v2format = FALSE; #endif /* MINC2 */ /* Argument table */ static ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &v2format, "Produce a MINC 2.0 format output file."}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *infile, *outfile; char *arg_string; Window_Data window_data; char *endptr; Loop_Options *loop_options; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc < 5) || (argc > 6)) { (void) fprintf(stderr, "\nUsage: %s [options] []\n", argv[0]); (void) fprintf(stderr, "Usage: %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; window_data.minimum = strtod(argv[3], &endptr); if ((endptr == argv[3]) || (*endptr != '\0')) { (void) fprintf(stderr, "Cannot get min value from %s\n", argv[3]); exit(EXIT_FAILURE); } window_data.maximum = strtod(argv[4], &endptr); if ((endptr == argv[4]) || (*endptr != '\0')) { (void) fprintf(stderr, "Cannot get max value from %s\n", argv[4]); exit(EXIT_FAILURE); } if (argc == 6) { window_data.use_newvalue = TRUE; window_data.newvalue = strtod(argv[5], &endptr); if ((endptr == argv[5]) || (*endptr != '\0')) { (void) fprintf(stderr, "Cannot get newvalue from %s\n", argv[5]); exit(EXIT_FAILURE); } } else { window_data.use_newvalue = FALSE; window_data.newvalue = 0.0; } /* Do loop */ loop_options = create_loop_options(); set_loop_verbose(loop_options, verbose); set_loop_clobber(loop_options, clobber); #if MINC2 set_loop_v2format(loop_options, v2format); #endif /* MINC2 */ voxel_loop(1, &infile, 1, &outfile, arg_string, loop_options, do_window, (void *) &window_data); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_window @INPUT : voxel_data - pointer to structure containing windowing info nvoxels - number of voxels to operate on data - array of voxels @OUTPUT : data @RETURNS : (nothing) @DESCRIPTION: Routine to loop through an array of voxels and limit the range of values. @METHOD : @GLOBALS : @CALLS : @CREATED : January 11, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_window(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Window_Data *window_data; long ivox; /* Get pointer to window info */ window_data = (Window_Data *) caller_data; /* Check arguments */ if ((input_num_buffers != 1) || (output_num_buffers != 1) || (output_vector_length != input_vector_length)) { (void) fprintf(stderr, "Bad arguments to do_window!\n"); exit(EXIT_FAILURE); } /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*input_vector_length; ivox++) { if (input_data[0][ivox] < window_data->minimum) { if (window_data->use_newvalue) output_data[0][ivox] = window_data->newvalue; else output_data[0][ivox] = window_data->minimum; } else if (input_data[0][ivox] > window_data->maximum) { if (window_data->use_newvalue) output_data[0][ivox] = window_data->newvalue; else output_data[0][ivox] = window_data->maximum; } else { output_data[0][ivox] = input_data[0][ivox]; } } return; } minc-tools-2.3.00+dfsg/progs/mincwindow/mincwindow.man10000644000175000000620000000221312574624760022064 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCWINDOW 1 "$Date: 2004-05-20 21:52:09 $" "" "MINC User's Guide" .SH NAME mincwindow \- limit voxel values to a given range .SH SYNOPSIS .B mincwindow .BI [options] .BI in.mnc .BI out.mnc .BI min .BI max .BI [newvalue] .SH DESCRIPTION \fImincwindow\fR copies \fIin.mnc\fR to \fIout.mnc\fR. Each voxel value that lies within the window [\fImin\fR,\fImax\fR] is copied unmodified. If the voxel value is outside that window and \fInewvalue\fR is specified, then that voxel is set to \fInewvalue\fR. Otherwise, the voxel value is set to \fImin\fR if it is less than \fImin\fR, and to \fImax\fR if it is higher than \fImax\fR. .SH OPTIONS .TP \fB\-2\fR Create MINC 2.0 format output files. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-verbose\fR Print out log messages (default). .TP \fB\-quiet\fR Do not print log messages. .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH "SEE ALSO" .LP .BR mincmath (1), .BR minccalc (1) minc-tools-2.3.00+dfsg/progs/mincaverage/0002755000175000000620000000000012574624760017237 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincaverage/mincaverage.man10000644000175000000620000001456512574624760022307 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1995 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincaverage/mincaverage.man1,v 6.3 2004-05-20 21:52:07 bert Exp $ .\" .TH MINCAVERAGE 1 "$Date: 2004-05-20 21:52:07 $" "" "MINC User's Guide" .SH NAME mincaverage - average minc files .SH SYNOPSIS .B mincaverage [] .mnc [.mnc...] .mnc .SH DESCRIPTION \fIMincaverage\fR averages minc files together. A range of optional behaviour is permitted as well: pre-normalizing volumes, creating a standard deviation volume, averaging over a specified dimension of the input files. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-debug\fR Print extra information (e.g. normalization factors). .TP \fB\-filelist\fR \fIfilename\fR Specify a file containing a list of input file names. If "-" is given, then file names are read from stdin. If this option is given, then there should be no input file names specified on the command line. Empty lines in the input file are ignored. .TP \fB\-max_buffer_size_in_kb\fR \fIbuffer-size\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4096 kbytes. .SH Output type options These options control the storage precision and size of individual voxel values in the output file. .TP \fB-filetype\fR Don't do any type conversion (default). .TP \fB\-byte\fR Write out 8-bit integer values. .TP \fB\-short\fR Write out 16-bit integer values. .TP \fB-int\fR Write out 32-bit integer values. .TP \fB\-long\fR Superseded by -int. .TP \fB\-float\fR Write out single-precision floating point values. .TP \fB\-double\fR Write out double-precision floating point values. .P \fB\-signed\fR Write out values as signed integers (default for short and long). Ignored for floating point types. .TP \fB\-unsigned\fR Write out values as unsigned integers (default for byte). Ignored for floating point types. .TP \fB\-range\fR \fImin max\fR specifies the valid range of output voxel values in their integer representation. Default is the full range for the type and sign. This option is ignored for floating point values. For it to have any effect, you must specify a type. .SH Averaging options .TP \fB\-normalize\fR Normalize volumes to their global average before averaging them (based on the mean of voxels with value greater than 2 percent of full range above the minimum). .TP \fB\-nonormalize\fR Do not normalize volumes (default). .TP \fB\-sdfile\fR \fIsdfile.mnc\fR Specify the name of an output standard deviation file, to be calculated in addition the mean that is normally calculated. .TP \fB\-weightfile\fR \fIweightfile.mnc\fR Specify an output cumulative voxel weight file (default=none). .TP \fB\-copy_header\fR Copy all of the additional header information from the first input file (default for one input file). .TP \fB\-nocopy_header\fR Do not copy additional header information (default for many input files). .TP \fB\-avgdim\fR \fIdimname\fR Specify the name of a dimension over which we should be averaging (or calculating standard deviation). If normalization is done, it still only applies to separate files only - no normalization is done within a file. .TP \fB\-binarize\fR Binarize the input volumes before calculating the average. The binarization is done by specifying a range of values that contribute 1 to the average. Normalization of the input is not permitted when performing binarization. .TP \fB\-binrange\fR \fImin max\fR Specify the range of values for binarization. .TP \fB\-binvalue\fR \fIvalue\fR Specify a single legal value (integer) for binarization. The range is set to be +/- 0.5 around this value to achieve an effective rounding of input values. .TP \fB\-ignore_below\fR \fIvalue\fR Do not include voxels below this value in the average and sd .TP \fB\-ignore_above\fR \fIvalue\fR Do not include voxels above this value in the average and sd .TP \fB\-floor\fR \fIvalue\fR Synonym for \fB\-ignore_below\fR .TP \fB\-ignore_above\fR \fIvalue\fR Synonym for \fB\-ignore_above\fR .TP \fB\-weights\fR \fI\fR Specify a series of weights for averaging. The number of weighting values must match the number of input files and the values must be provided as a single argument with commas or spaces as separators. The sum of the weights must be non-zero. If weights are used with an averaging dimension, then only one input file can be specified. .TP \fB\-width_weighted\fR This option can only be used when averaging across a dimension (\fB-avgdim\fR option). It specifies that weighting should be done using the width variable that corresponds to the averaging dimension. For example, using \fB\-width_weighted\fR with \fB\-avgdim\fR time will use the time-width variable to weight the values. .TP \fB\-min_weight\fR \fIvalue\fR This specifies the minimum weight (number of volumes if no weights are specified) needed for calculating a valid average. If the cumulative weight for a voxel is below this threshold, the average and sd reported will be 0. This would typically be useful if values are excluded using any of the \fB\-ignore\fR options, or by NaN values in the input volume(s). .TP \fB\-min_weight_fraction\fR \fIvalue\fR Same as \fB\-min_weight\fR, but specified as a fraction of the sum of the input weights (or the number of input volumes, if no weight is specified). .SH Generic options for all commands: .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1995 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincaverage/mincaverage.c0000644000175000000620000010736312574624760021674 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincaverage @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to average minc files @METHOD : @GLOBALS : @CALLS : @CREATED : April 28, 1995 (Peter Neelin) @MODIFIED : * $Log: mincaverage.c,v $ * Revision 6.11 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.10 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.9 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.8 2005/08/26 21:07:16 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.7 2004/12/14 23:52:50 bert * Get rid of compilation warnings w/c99 * * Revision 6.6 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.5 2004/04/27 15:38:15 bert * Added -2 option * * Revision 6.4 2001/04/24 13:38:42 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.3 2001/04/17 18:40:17 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/07/07 13:19:12 neelin * Added option -filelist to read file names from a file. This gets around * command-line length limits. * * Revision 6.1 1999/10/19 14:45:18 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:19 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:18 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:05:59 neelin * Release of minc version 0.4 * * Revision 3.2 1996/04/02 20:16:09 neelin * Added -width_weighted option. Allow -weights with -avgdim option. * * Revision 3.1 1995/11/20 14:24:47 neelin * Added -weights option. * * Revision 3.0 1995/05/15 19:32:44 neelin * Release of minc version 0.3 * * Revision 1.6 1995/05/05 18:08:17 neelin * Removed debugging line for sd calculation. * * Revision 1.5 1995/05/02 16:08:17 neelin * Added -check, -nocheck options. * * Revision 1.4 1995/04/27 14:05:38 neelin * Added binarization options. * * Revision 1.3 1995/04/27 12:48:42 neelin * Changed order of options. * * Revision 1.2 1995/04/27 11:50:35 neelin * Require either -norm or -nonorm on command line. * * Revision 1.1 1995/04/26 14:16:38 neelin * Initial revision * ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "read_file_names.h" /* Declaration of read_file_names() */ /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define THRESH_FRACTION (1/50.0) #define WIDTH_SUFFIX "-width" #define DEFAULT_BOOLEAN -1 /* Double_Array structure */ typedef struct { int numvalues; double *values; } Double_Array; /* Structures for averaging and normalizing information */ typedef struct { int binarize; int need_sd; int need_weight; double binrange[2]; double ignore_below; double ignore_above; double *norm_factor; int averaging_over_dimension; int num_weights; double *weights; double weight_thresh; } Average_Data; typedef struct { int threshold_set; double threshold; double sum0, sum1; } Norm_Data; /* Function prototypes */ static void do_normalization(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void find_mincfile_range(int mincid, double *minimum, double *maximum); static void do_average(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void start_average(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void finish_average(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static int get_double_list(char *dst, char *key, char *nextarg); /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; static int debug = FALSE; static int check_dimensions = TRUE; #ifdef NO_DEFAULT_NORM static int normalize = -1; #else static int normalize = FALSE; #endif static char *sdfile = NULL; static char *weightfile = NULL; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = FALSE; static double valid_range[2] = {0.0, 0.0}; static int copy_all_header = DEFAULT_BOOLEAN; static char *averaging_dimension = NULL; static int max_buffer_size_in_kb = 4 * 1024; static int binarize = FALSE; static double binrange[2] = {DBL_MAX, -DBL_MAX}; static double binvalue = -DBL_MAX; static double ignore_below = -DBL_MAX; static double ignore_above = DBL_MAX; static double weight_thresh = 0.0; static double weight_thresh_fraction = 0.0; static Double_Array weights = {0, NULL}; static int width_weighted = FALSE; static char *filelist = NULL; #if MINC2 static int minc2_format = FALSE; #endif /* MINC2 */ /* Argument table */ ArgvInfo argTable[] = { #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format, "Produce a MINC 2.0 format output file"}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-no_clobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Synonym for -noclobber."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-debug", ARGV_CONSTANT, (char *) TRUE, (char *) &debug, "Print out debugging messages."}, {"-filelist", ARGV_STRING, (char *) 1, (char *) &filelist, "Specify the name of a file containing input file names (- for stdin)."}, {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE, (char *) &check_dimensions, "Check that dimension info matches across files (default)."}, {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE, (char *) &check_dimensions, "Do not check dimension info."}, {"-max_buffer_size_in_kb", ARGV_INT, (char *) 1, (char *) &max_buffer_size_in_kb, "Specify the maximum size of the internal buffers (in kbytes)."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {"-normalize", ARGV_CONSTANT, (char *) TRUE, (char *) &normalize, "Normalize data sets for mean intensity."}, {"-nonormalize", ARGV_CONSTANT, (char *) FALSE, (char *) &normalize, "Do not normalize data sets (default)."}, {"-sdfile", ARGV_STRING, (char *) 1, (char *) &sdfile, "Specify an output sd file (default=none)."}, {"-weightfile", ARGV_STRING, (char *) 1, (char *) &weightfile, "Specify an output cumulative voxel weight file (default=none)."}, {"-copy_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all of the header from the first file (default for one file)."}, {"-nocopy_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Do not copy all of the header from the first file (default for many files))."}, {"-avgdim", ARGV_STRING, (char *) 1, (char *) &averaging_dimension, "Specify a dimension along which we wish to average."}, {"-binarize", ARGV_CONSTANT, (char *) TRUE, (char *) &binarize, "Binarize the volume by looking for values in a given range."}, {"-binrange", ARGV_FLOAT, (char *) 2, (char *) binrange, "Specify a range for binarization."}, {"-binvalue", ARGV_FLOAT, (char *) 1, (char *) &binvalue, "Specify a target value (+/- 0.5) for binarization."}, {"-ignore_below", ARGV_FLOAT, (char *) 1, (char *) &ignore_below, "Do not include voxels below this value in the average and sd."}, {"-ignore_above", ARGV_FLOAT, (char *) 1, (char *) &ignore_above, "Do not include voxels above this value in the average and sd."}, {"-floor", ARGV_FLOAT, (char *) 1, (char *) &ignore_below, "Do not include voxels below this value in the average and sd."}, {"-ceil", ARGV_FLOAT, (char *) 1, (char *) &ignore_above, "Do not include voxels above this value in the average and sd."}, {"-weights", ARGV_FUNC, (char *) get_double_list, (char *) &weights, "Specify weights for averaging (\",,...\")."}, {"-width_weighted", ARGV_CONSTANT, (char *) TRUE, (char *) &width_weighted, "Weight by dimension widths when -avgdim is used."}, {"-min_weight", ARGV_FLOAT, (char *) 1, (char *) &weight_thresh, "Minimum cumulative weight needed for calculating the average or sd." }, {"-min_weight_fraction", ARGV_FLOAT, (char *) 1, (char *) &weight_thresh_fraction, "Same as -min_weight, but specified as a fraction of the sum of the input weights (or the number of input volumes, if no weight is specified)." }, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char **infiles, *outfiles[3]; int nfiles, nout; char *arg_string; Norm_Data norm_data; Average_Data average_data; Loop_Options *loop_options; double *vol_mean, vol_total, nvols, global_mean, total_weight; int ifile, iweight; int weights_specified; int first_mincid, dimid, varid, dim[MAX_VAR_DIMS]; int ndims; long start, count; int old_ncopts; int strlength; char dimname[MAX_NC_NAME]; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* 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); } outfiles[0] = outfiles[1] = outfiles[2] = NULL; outfiles[0] = argv[argc-1]; nout = 1; if( sdfile != NULL ) outfiles[nout++] = sdfile; if( weightfile != NULL ) outfiles[nout++] = weightfile; first_mincid = MI_ERROR; /* Get the list of input files either from the command line or from a file, or report an error if both are specified */ nfiles = argc - 2; if (filelist == NULL) { infiles = &argv[1]; } else if (nfiles <= 0) { infiles = read_file_names(filelist, &nfiles); if (infiles == NULL) { (void) fprintf(stderr, "Error reading in file names from file \"%s\"\n", filelist); exit(EXIT_FAILURE); } } else { (void) fprintf(stderr, "Do not specify both -filelist and input file names\n"); exit(EXIT_FAILURE); } /* Make sure that we have something to process */ if (nfiles == 0) { (void) fprintf(stderr, "No input files specified\n"); exit(EXIT_FAILURE); } /* Set default value of copy_all_header */ if (copy_all_header == DEFAULT_BOOLEAN) { copy_all_header = (nfiles <= 1); } /* Are we averaging over a dimension? */ average_data.averaging_over_dimension = (averaging_dimension != NULL); /* Check for weights and width-weighting */ weights_specified = weights.numvalues > 0; if (weights_specified && width_weighted) { (void) fprintf(stderr, "%s: Please do not specify weights and width-weighting.\n", argv[0]); exit(EXIT_FAILURE); } /* Default is no weighting */ average_data.num_weights = 0; average_data.weights = NULL; /* Check for weights */ if (weights_specified) { if (averaging_dimension == NULL) { if (weights.numvalues != nfiles) { (void) fprintf(stderr, "%s: Number of weights does not match number of files.\n", argv[0]); exit(EXIT_FAILURE); } } else { if (nfiles > 1) { (void) fprintf(stderr, "%s: Only one input file allowed with -weights and -avgdim.\n", argv[0]); exit(EXIT_FAILURE); } /* Check that the dimension size matches the number of weights */ first_mincid = miopen(infiles[0], NC_NOWRITE); dimid = ncdimid(first_mincid, averaging_dimension); (void) ncdiminq(first_mincid, dimid, NULL, &count); if (weights.numvalues != count) { (void) fprintf(stderr, "%s: Number of weights does not match size of dimension.\n", argv[0]); } } /* Save the weights */ average_data.num_weights = weights.numvalues; average_data.weights = malloc(sizeof(*average_data.weights) * average_data.num_weights); for (iweight=0; iweight < average_data.num_weights; iweight++) { average_data.weights[iweight] = weights.values[iweight]; } free(weights.values); } /* Check for width weighting */ if (width_weighted) { /* Check for errors */ if (averaging_dimension == NULL) { (void) fprintf(stderr, "%s: Please specify -avgdim with -width_weighted.\n", argv[0]); exit(EXIT_FAILURE); } if (nfiles > 1) { (void) fprintf(stderr, "%s: Use -width_weighted with only one input file.\n", argv[0]); exit(EXIT_FAILURE); } /* Open the file */ first_mincid = miopen(infiles[0], NC_NOWRITE); /* Get the dimension id */ dimid = ncdimid(first_mincid, averaging_dimension); /* Look for the width variable */ strlength = MAX_NC_NAME - strlen(WIDTH_SUFFIX) - 1; (void) strncpy(dimname, averaging_dimension, strlength); dimname[strlength] = '\0'; (void) strcat(dimname, WIDTH_SUFFIX); old_ncopts = ncopts; ncopts = 0; varid = ncvarid(first_mincid, dimname); (void) ncvarinq(first_mincid, varid, NULL, NULL, &ndims, dim, NULL); ncopts = old_ncopts; if (varid != MI_ERROR) { /* Check that things match up */ if ((ndims != 1) || (dim[0] != dimid)) { (void) fprintf(stderr, "%s: Dimension width variable does not match avgdim.\n", argv[0]); } /* Get the size of the dimension */ (void) ncdiminq(first_mincid, dim[0], NULL, &count); average_data.num_weights = count; average_data.weights = malloc(sizeof(*average_data.weights) * average_data.num_weights); /* Read in the widths */ start = 0; (void) mivarget(first_mincid, varid, &start, &count, NC_DOUBLE, NULL, average_data.weights); } } /* If width_weighted */ /* Check that weights sum to non-zero. We don't need to normalize them, since a running sum is done in the averaging. */ if (average_data.num_weights > 0) { total_weight = 0.0; for (iweight=0; iweight < average_data.num_weights; iweight++) { total_weight += average_data.weights[iweight]; } if (total_weight == 0.0) { (void) fprintf(stderr, "%s: Weights sum to zero.\n", argv[0]); exit(EXIT_FAILURE); } } /* If we don't have weights, each input will get a weight of 1 */ if (average_data.num_weights == 0) total_weight = nfiles; /* Check the cumulative weight thresholding */ if (weight_thresh > 0.0 && weight_thresh_fraction > 0.0){ (void) fprintf(stderr, "Do not specify both -min_weight -min_weight_fraction\n"); exit(EXIT_FAILURE); } else if( weight_thresh_fraction > 0.0 ){ weight_thresh = weight_thresh_fraction * total_weight; } average_data.weight_thresh = weight_thresh; /* Check for binarization */ if (binarize) { if (normalize == TRUE) { (void) fprintf(stderr, "%s: Normalization and binarization cannot both be specified\n", argv[0]); exit(EXIT_FAILURE); } normalize = FALSE; if (binvalue != -DBL_MAX) { binrange[0] = binvalue - 0.5; binrange[1] = binvalue + 0.5; } if (binrange[0] > binrange[1]) { (void) fprintf(stderr, "%s: Please specify a binarization range with min less than max\n", argv[0]); exit(EXIT_FAILURE); } average_data.binrange[0] = binrange[0]; average_data.binrange[1] = binrange[1]; } average_data.binarize = binarize; /* Store the ignore above/below values */ average_data.ignore_below = ignore_below; average_data.ignore_above = ignore_above; /* Check for no specification of normalization */ #ifdef NO_DEFAULT_NORM if (normalize == -1) { (void) fprintf(stderr, "\n%s: %s\n\n%s\n%s\n%s\n%s\n%s\n\n", argv[0], "Please specify either -norm or -nonorm.", "The default setting for normalization is being changed from \"-norm\" to", "\"-nonorm\". To prevent undetected problems with data, this program will ", "not work unless one of these flags is explicitly given on the command-line", "(ie. no default is permitted). The new default will come into effect some", "time in the future." ); exit(EXIT_FAILURE); } #endif /* Do normalization if needed */ average_data.norm_factor = malloc(sizeof(*average_data.norm_factor) * nfiles); if (normalize) { vol_mean = malloc(sizeof(*vol_mean) * nfiles); loop_options = create_loop_options(); set_loop_verbose(loop_options, FALSE); #if MINC2 set_loop_v2format(loop_options, minc2_format); #endif /* MINC2 */ set_loop_accumulate(loop_options, TRUE, 0, NULL, NULL); set_loop_buffer_size(loop_options, (long) 1024 * max_buffer_size_in_kb); set_loop_check_dim_info(loop_options, check_dimensions); vol_total = 0.0; nvols = 0; if (verbose) { (void) fprintf(stderr, "Normalizing:"); (void) fflush(stderr); } for (ifile=0; ifile < nfiles; ifile++) { norm_data.threshold_set = FALSE; norm_data.sum0 = 0.0; norm_data.sum1 = 0.0; if (verbose) { (void) fprintf(stderr, "."); (void) fflush(stderr); } if (first_mincid != MI_ERROR) { set_loop_first_input_mincid(loop_options, first_mincid); first_mincid = MI_ERROR; } voxel_loop(1, &infiles[ifile], 0, NULL, NULL, loop_options, do_normalization, (void *) &norm_data); if (norm_data.sum0 > 0.0) { vol_mean[ifile] = norm_data.sum1 / norm_data.sum0; vol_total += vol_mean[ifile]; nvols++; } else { vol_mean[ifile] = 0.0; } if (debug) { (void) fprintf(stderr, "Volume %d mean = %.15g\n", ifile, vol_mean[ifile]); } } free_loop_options(loop_options); if (verbose) { (void) fprintf(stderr, "Done\n"); (void) fflush(stderr); } if (nvols > 0) global_mean = vol_total / nvols; else global_mean = 0.0; for (ifile=0; ifile < nfiles; ifile++) { if (vol_mean[ifile] != 0.0) average_data.norm_factor[ifile] = global_mean / vol_mean[ifile]; else average_data.norm_factor[ifile] = 0.0; if (debug) { (void) fprintf(stderr, "Volume %d norm factor = %.15g\n", ifile, average_data.norm_factor[ifile]); } } free(vol_mean); } else { for (ifile=0; ifile < nfiles; ifile++) { average_data.norm_factor[ifile] = 1.0; } } /* Do averaging */ average_data.need_sd = (sdfile != NULL); average_data.need_weight = (weightfile != NULL); loop_options = create_loop_options(); if (first_mincid != MI_ERROR) { set_loop_first_input_mincid(loop_options, first_mincid); first_mincid = MI_ERROR; } set_loop_verbose(loop_options, verbose); set_loop_clobber(loop_options, clobber); set_loop_datatype(loop_options, datatype, is_signed, valid_range[0], valid_range[1]); set_loop_accumulate(loop_options, TRUE, 1, start_average, finish_average); set_loop_copy_all_header(loop_options, copy_all_header); set_loop_dimension(loop_options, averaging_dimension); set_loop_buffer_size(loop_options, (long) 1024 * max_buffer_size_in_kb); set_loop_check_dim_info(loop_options, check_dimensions); voxel_loop(nfiles, infiles, nout, outfiles, arg_string, loop_options, do_average, (void *) &average_data); free_loop_options(loop_options); /* Free stuff */ free(average_data.weights); free(average_data.norm_factor); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_normalization @INPUT : Standard for voxel_loop @OUTPUT : Standard for voxel_loop @RETURNS : (nothing) @DESCRIPTION: Routine to loop through an array of voxels and calculate normalization values. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_normalization(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Norm_Data *norm_data; long ivox; double value, minimum, maximum; /* Get pointer to window info */ norm_data = (Norm_Data *) caller_data; /* Check arguments */ if ((input_num_buffers != 1) || (output_num_buffers != 0)) { (void) fprintf(stderr, "Bad arguments to do_normalization!\n"); exit(EXIT_FAILURE); } /* Check to see if the threshold has been set */ if (!norm_data->threshold_set) { find_mincfile_range(get_info_current_mincid(loop_info), &minimum, &maximum); norm_data->threshold = minimum + (maximum - minimum) * THRESH_FRACTION; norm_data->threshold_set = TRUE; } /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*input_vector_length; ivox++) { value = input_data[0][ivox]; if ((value != -DBL_MAX) && (value > norm_data->threshold)) { norm_data->sum0 += 1.0; norm_data->sum1 += value; } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : find_mincfile_range @INPUT : mincid - id of minc file @OUTPUT : minimum - minimum for file maximum - maximum for file @RETURNS : (nothing) @DESCRIPTION: Routine to find the min and max in a minc file @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void find_mincfile_range(int mincid, double *minimum, double *maximum) { int varid; char *varname; double sign, value; double *extreme; long index[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int ndims, dim[MAX_VAR_DIMS]; int idim, imm; int old_ncopts; *minimum = 0.0; *maximum = 1.0; for (imm=0; imm < 2; imm++) { /* Set up for max or min */ if (imm == 0) { varname = MIimagemin; sign = -1.0; extreme = minimum; } else { varname = MIimagemax; sign = 1.0; extreme = maximum; } /* Get the variable id */ old_ncopts = ncopts; ncopts = 0; varid = ncvarid(mincid, varname); ncopts = old_ncopts; if (varid == MI_ERROR) continue; /* Get the dimension info */ (void) ncvarinq(mincid, varid, NULL, NULL, &ndims, dim, NULL); for (idim=0; idim < ndims; idim++) { (void) ncdiminq(mincid, dim[idim], NULL, &count[idim]); } if (ndims <= 0) { ndims = 1; count[0] = 1; } /* Loop through values, getting extrema */ (void) miset_coords(ndims, (long) 0, index); *extreme = sign * (-DBL_MAX); while (index[0] < count[0]) { (void) mivarget1(mincid, varid, index, NC_DOUBLE, NULL, &value); if ((value * sign) > (*extreme * sign)) { *extreme = value; } idim = ndims-1; index[idim]++; while ((index[idim] > count[idim]) && (idim > 0)) { idim--; index[idim]++; } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_average @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Routine to loop through an array of voxels and perform averaging of across volumes. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_average(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Average_Data *average_data; long ivox; double value; int curfile, curindex; int num_out; double norm_factor, binmin, binmax, weight, ignore_below, ignore_above; int binarize; /* Get pointer to window info */ average_data = (Average_Data *) caller_data; /* Check arguments */ num_out = 2 + ( average_data->need_sd != 0 ) + ( average_data->need_weight != 0 ); if ((input_num_buffers != 1) || (output_num_buffers != num_out) || (output_vector_length != input_vector_length)) { (void) fprintf(stderr, "Bad arguments to do_average!\n"); exit(EXIT_FAILURE); } /* Get the normalization factor and binarization range */ curfile = get_info_current_file(loop_info); curindex = get_info_current_index(loop_info); norm_factor = average_data->norm_factor[curfile]; if ((average_data->num_weights <= 0) || (average_data->weights == NULL)) { weight = 1.0; } else { if (average_data->averaging_over_dimension) { if (curindex >= average_data->num_weights) { (void) fprintf(stderr, "Internal error in index!\n"); exit(EXIT_FAILURE); } weight = average_data->weights[curindex]; } else { if (curfile >= average_data->num_weights) { (void) fprintf(stderr, "Internal error in file number!\n"); exit(EXIT_FAILURE); } weight = average_data->weights[curfile]; } } binarize = average_data->binarize; binmin = average_data->binrange[0]; binmax = average_data->binrange[1]; ignore_below = average_data->ignore_below; ignore_above = average_data->ignore_above; /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*input_vector_length; ivox++) { value = input_data[0][ivox]; if (binarize) { value = ( ((value >= binmin) && (value <= binmax)) ? 1.0 : 0.0 ); } if (value != -DBL_MAX && value > ignore_below && value < ignore_above ) { value *= norm_factor; output_data[0][ivox] += weight; output_data[1][ivox] += value * weight; if (average_data->need_sd) output_data[2][ivox] += value * value * weight; } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : start_average @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Start routine for averaging. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void start_average(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Average_Data *average_data; long ivox; int num_out; /* Get pointer to window info */ average_data = (Average_Data *) caller_data; /* Check arguments */ num_out = 2 + ( average_data->need_sd != 0 ) + ( average_data->need_weight != 0 ); if (output_num_buffers != num_out) { (void) fprintf(stderr, "Bad arguments to start_average!\n"); exit(EXIT_FAILURE); } /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*output_vector_length; ivox++) { output_data[0][ivox] = 0.0; output_data[1][ivox] = 0.0; if (average_data->need_sd) output_data[2][ivox] = 0.0; } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : finish_average @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Finish routine for averaging. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void finish_average(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Average_Data *average_data; long ivox; int num_out, i_weight; double sum0, sum1, sum2, value; /* Get pointer to window info */ average_data = (Average_Data *) caller_data; /* If we haven't asked for sd, the weight output index will be 1 */ i_weight = 2 - ( average_data->need_sd == 0 ); /* Check arguments */ num_out = 2 + ( average_data->need_sd != 0 ) + ( average_data->need_weight != 0 ); if (output_num_buffers != num_out) { (void) fprintf(stderr, "Bad arguments to finish_average!\n"); exit(EXIT_FAILURE); } /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*output_vector_length; ivox++) { sum0 = output_data[0][ivox]; sum1 = output_data[1][ivox]; if (sum0 > 0.0 && sum0 >= average_data->weight_thresh) { output_data[0][ivox] = sum1 / sum0; if (average_data->need_sd) { sum2 = output_data[2][ivox]; if (sum0 > 1.0) { value = (sum2 - sum1*sum1 / sum0) / (sum0 - 1.0); if (value > 0.0) value = sqrt(value); else value = 0.0; output_data[1][ivox] = value; } else output_data[1][ivox] = 0.0; } } else { output_data[0][ivox] = 0.0; if (average_data->need_sd) output_data[1][ivox] = 0.0; } if (average_data->need_weight) output_data[i_weight][ivox] = sum0; } return; } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static 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; } minc-tools-2.3.00+dfsg/progs/minccmp/0002755000175000000620000000000012574624760016404 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minccmp/minccmp.c0000644000175000000620000005177112574624760020207 0ustar stevestaff/* minccmp.c */ /* */ /* Copyright Andrew Janke - a.janke@gmail.com */ /* 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 makes no representations about the */ /* suitability of this software for any purpose. It is provided "as is" */ /* without express or implied warranty. */ /* */ /* calculates measures of similarity/difference between 2 or more volumes */ /* */ /* Measures used (sum(x) denotes the sum of x over a volume): */ /* RMSE - Root Mean Squared Error */ /* = sqrt( 1/n * sum((a-b)^2)) */ /* xcorr - Cross Correlation */ /* = sum((a*b)^2) / (sqrt(sum(a^2)) * sqrt(sum(b^2)) */ /* zscore - z-score differences */ /* = sum( |((a - mean(a)) / stdev(a)) - */ /* ((b - mean(b)) / stdev(b))| ) / nvox */ /* */ /* Tue Jun 17 11:31:10 EST 2003 - initial version inspired by voldiff and */ /* peter's compare_volumes */ #include #include #include #include #include #include #include #include #ifndef FALSE # define FALSE 0 #endif #ifndef TRUE # define TRUE 1 #endif #define SQR2(x) ((x) * (x)) /* For Dice statistics, this defines the largest label value on which * we can report. We don't bother to report statistics for labels that * are not present in the file. */ #define MAX_CMATRIX 10 /* Structure which is used to accumulate the overall similarity * measures. */ struct aggregate_similarity { double dice_num, dice_den; /* Dice. */ double sens_num, sens_den; /* Sensitivity. */ double spec_num, spec_den; /* Specificity. */ double acc_num, acc_den; /* Accuracy. */ double kappa_num, kappa_den; /* Kappa. */ }; typedef struct { double nvox; double sum; /* sum of valid voxels */ double ssum; /* squared sum of valid voxels */ double min; double max; double mean; double var; double sd; double sum_prd0; /* sum of product of file[x] with file[0] */ double ssum_add0; /* squared sum of addition of file[x] with file[0] */ double ssum_dif0; /* squared sum of difference of file[x] with file[0] */ double ssum_prd0; /* squared sum of product of file[x] with file[0] */ double sum_zdif0; /* sum of zscore differences of file[x] and file[0] */ /* result stores */ double rmse; double xcorr; double zscore; double vratio; size_t cmatrix[MAX_CMATRIX][MAX_CMATRIX]; /* Confusion matrix */ } Vol_Data; typedef struct { int n_datafiles; int mask; int mask_idx; /* individual volume data */ Vol_Data *vd; } Loop_Data; /* Function prototypes */ void pass_0(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info); void pass_1(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info); void print_result(char *title, double result); void print_id_result(char *title, int id, double result); void dump_stats(Loop_Data * ld); void do_int_calcs(Loop_Data * ld); void do_final_calcs(Loop_Data * ld); /* Argument variables and table */ static int verbose = FALSE; static int debug = FALSE; static int quiet = FALSE; static int clobber = FALSE; static int max_buffer_size_in_kb = 4 * 1024; static int check_dim_info = TRUE; static char *mask_fname = NULL; static double valid_range[2] = { -DBL_MAX, DBL_MAX }; static int do_all = FALSE; static int do_ssq = FALSE; static int do_rmse = FALSE; static int do_xcorr = FALSE; static int do_zscore = FALSE; static int do_vratio = FALSE; static int do_sim = FALSE; ArgvInfo argTable[] = { {"-verbose", ARGV_CONSTANT, (char *)TRUE, (char *)&verbose, "be verbose"}, {"-debug", ARGV_CONSTANT, (char *)TRUE, (char *)&debug, "dump all stats info"}, {"-quiet", ARGV_CONSTANT, (char *)TRUE, (char *)&quiet, "print requested values only"}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "clobber existing files"}, {"-max_buffer_size_in_kb", ARGV_INT, (char *)1, (char *)&max_buffer_size_in_kb, "maximum size of internal buffers."}, {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE, (char *) &check_dim_info, "Check that files have matching dimensions (default)."}, {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE, (char *) &check_dim_info, "Do not check that files have matching dimensions."}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nVoxel selection options (applies to first volume ONLY):"}, {"-floor", ARGV_FLOAT, (char *)1, (char *)&valid_range[0], "Ignore voxels below this value. (incl)"}, {"-ceil", ARGV_FLOAT, (char *)1, (char *)&valid_range[1], "Ignore voxels above this value. (incl)"}, {"-range", ARGV_FLOAT, (char *)2, (char *)&valid_range, "Ignore voxels outside the range. (incl)"}, {"-mask", ARGV_STRING, (char *)1, (char *)&mask_fname, "Use for calculations."}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nImage Statistics (printed in this order)"}, {"-all", ARGV_CONSTANT, (char *)TRUE, (char *)&do_all, "all statistics (default)."}, {"-ssq", ARGV_CONSTANT, (char *)TRUE, (char *)&do_ssq, "sum of squared difference (2 volumes)"}, {"-rmse", ARGV_CONSTANT, (char *)TRUE, (char *)&do_rmse, "root mean squared error (2 volumes)"}, {"-xcorr", ARGV_CONSTANT, (char *)TRUE, (char *)&do_xcorr, "cross correlation (2 volumes)"}, {"-zscore", ARGV_CONSTANT, (char *)TRUE, (char *)&do_zscore, "z-score (2 volumes)"}, {"-similarity", ARGV_CONSTANT, (char *)TRUE, (char *)&do_sim, "Similarity measures for labeled volumes (2 volumes)"}, // {"-vr", ARGV_CONSTANT, (char *)TRUE, (char *)&do_vratio, // "variance ratio (2 volumes)"}, // {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, // "\nBinary Image Only Statistics"}, // {"-kappa", ARGV_CONSTANT, (char *)TRUE, (char *)&do_kappa, // "all statistics (default)."}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]){ char **infiles; int n_infiles; Loop_Options *loop_opt; Loop_Data ld; int i; /* Get arguments */ if(ParseArgv(&argc, argv, argTable, 0) || (argc < 3)){ fprintf(stderr, "\nUsage: %s [options] []\n", argv[0]); fprintf(stderr, " %s -help\n\n", argv[0]); return (EXIT_FAILURE); } n_infiles = argc - 1; infiles = &argv[1]; /* check arguments */ if(!do_rmse && !do_xcorr && !do_zscore && !do_vratio && !do_ssq && !do_sim){ do_all = TRUE; } if(do_all){ do_ssq = do_rmse = do_xcorr = do_zscore = do_vratio = TRUE; } /* check for infiles */ if(verbose){ fprintf(stderr, "\n+++ infiles +++\n"); } for(i = 0; i < n_infiles; i++){ if(verbose){ fprintf(stderr, " | [%02d]: %s\n", i, infiles[i]); } if(access(infiles[i], F_OK) != 0){ fprintf(stderr, "%s: Couldn't find %s\n", argv[0], infiles[i]); exit(EXIT_FAILURE); } } /* set up Loop_Data struct and mask file */ ld.n_datafiles = n_infiles; if(mask_fname != NULL){ if(verbose){ fprintf(stderr, " | mask: %s\n", mask_fname); } if(access(mask_fname, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find mask file: %s\n", argv[0], mask_fname); exit(EXIT_FAILURE); } ld.mask = TRUE; ld.mask_idx = n_infiles; infiles[n_infiles] = mask_fname; n_infiles++; } else { ld.mask = FALSE; ld.mask_idx = 0; } /* allocate space and initialise volume stats data */ ld.vd = (Vol_Data *) malloc(sizeof(Vol_Data) * ld.n_datafiles); for(i = 0; i < ld.n_datafiles; i++){ ld.vd[i].nvox = 0; ld.vd[i].sum = 0; ld.vd[i].ssum = 0; ld.vd[i].min = DBL_MAX; ld.vd[i].max = -DBL_MAX; ld.vd[i].sum_prd0 = 0; ld.vd[i].ssum_add0 = 0; ld.vd[i].ssum_dif0 = 0; ld.vd[i].ssum_prd0 = 0; ld.vd[i].mean = 0; ld.vd[i].var = 0; ld.vd[i].sd = 0; ld.vd[i].rmse = 0.0; ld.vd[i].xcorr = 0.0; ld.vd[i].zscore = 0.0; ld.vd[i].vratio = 0.0; memset(ld.vd[i].cmatrix, 0, sizeof(ld.vd[i].cmatrix)); } /* set up and do voxel_loop(s) */ loop_opt = create_loop_options(); set_loop_verbose(loop_opt, verbose); set_loop_buffer_size(loop_opt, (long)1024 * max_buffer_size_in_kb); set_loop_check_dim_info(loop_opt, check_dim_info); /* first pass */ voxel_loop(n_infiles, infiles, 0, NULL, NULL, loop_opt, pass_0, (void *)&ld); /* intermediate calculations */ do_int_calcs(&ld); /* run the second pass if we have to */ if(do_zscore){ voxel_loop(n_infiles, infiles, 0, NULL, NULL, loop_opt, pass_1, (void *)&ld); } /* final calculations */ do_final_calcs(&ld); free_loop_options(loop_opt); if(debug){ dump_stats(&ld); } /* calculate and print result(s) */ if(do_all && !quiet){ fprintf(stdout, "file[0]: %s\n", infiles[0]); fprintf(stdout, "mask file: %s\n", mask_fname); } for (i = 1; i < ld.n_datafiles; i++) { if(do_all && !quiet){ fprintf(stdout, "file[%d]: %s\n", i, infiles[i]); } if(do_ssq){ print_result("ssq: ", ld.vd[i].ssum_dif0); } if(do_rmse){ print_result("rmse: ", ld.vd[i].rmse); } if(do_xcorr){ print_result("xcorr: ", ld.vd[i].xcorr); } if(do_zscore){ print_result("zscore: ", ld.vd[i].zscore); } if (do_sim) { double nvox = ld.vd[i].nvox; int j, k; struct aggregate_similarity agg_sim = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; if (!quiet) { printf("id dice sens. spec. acc. kappa\n"); } /* Calculate the Jaccard or Dice coefficients */ for (j = 0; j < MAX_CMATRIX; j++) { size_t true_pos = ld.vd[i].cmatrix[j][j]; size_t false_pos = 0; size_t true_neg = 0; size_t false_neg = 0; size_t ab_total = 0; /* Total voxels with this label. */ size_t a_total = 0; size_t b_total = 0; double dice_similarity_coefficient; double sensitivity; double specificity; double accuracy; double kappa; /* Sum all voxels either correctly or incorrectly classified as * class 'j': */ for (k = 0; k < MAX_CMATRIX; k++) { a_total += ld.vd[i].cmatrix[j][k]; b_total += ld.vd[i].cmatrix[k][j]; } ab_total = a_total + b_total; false_pos = b_total - true_pos; false_neg = a_total - true_pos; true_neg = nvox - (true_pos + false_pos + false_neg); if (ab_total == 0) { continue; } dice_similarity_coefficient = (2.0 * true_pos) / ab_total; sensitivity = (double) true_pos / (true_pos + false_neg); specificity = (double) true_neg / (true_neg + false_pos); accuracy = (double) (true_pos + true_neg) / nvox; kappa = (double) (nvox * true_pos - a_total * b_total) / (nvox * a_total - a_total * b_total); printf("%2d %.4f %.4f %.4f %.4f %.4f\n", j, dice_similarity_coefficient, sensitivity, specificity, accuracy, kappa); agg_sim.sens_num += true_pos; agg_sim.sens_den += (true_pos + false_neg); agg_sim.spec_num += true_neg; agg_sim.spec_den += (true_neg + false_pos); agg_sim.acc_num += (true_pos + true_neg); agg_sim.acc_den += nvox; agg_sim.kappa_num += nvox * true_pos - a_total * b_total; agg_sim.kappa_den += nvox * a_total - a_total * b_total; } /* This code for the overall Dice statistic is copied more-or-less * exactly from voldiff.c */ for (j = 1; j < MAX_CMATRIX; j++) { for (k = 1; k < MAX_CMATRIX; k++) { agg_sim.dice_num += ld.vd[i].cmatrix[j][k]; } } agg_sim.dice_den = 2.0 * agg_sim.dice_num; for (j = 1; j < MAX_CMATRIX; j++) { agg_sim.dice_num += ld.vd[i].cmatrix[j][j]; agg_sim.dice_den += ld.vd[i].cmatrix[0][j]; agg_sim.dice_den += ld.vd[i].cmatrix[j][0]; } printf(" X %.4f %.4f %.4f %.4f %.4f\n", agg_sim.dice_num / agg_sim.dice_den, agg_sim.sens_num / agg_sim.sens_den, agg_sim.spec_num / agg_sim.spec_den, agg_sim.acc_num / agg_sim.acc_den, agg_sim.kappa_num / agg_sim.kappa_den); } if(!quiet){ fprintf(stdout, "\n"); } } return EXIT_SUCCESS; } /* voxel loop function for first pass through data */ void pass_0(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info){ long ivox; double valuei, value0; int i; /* get pointer to loop data */ Loop_Data *ld = (Loop_Data *)caller_data; /* shut the compiler up - yes I _know_ I don't use these */ (void)output_num_buffers; (void)output_vector_length; (void)output_data; (void)loop_info; /* sanity check */ if((input_num_buffers < 2) || (output_num_buffers != 0)){ fprintf(stderr, "Bad arguments to pass_0\n"); exit(EXIT_FAILURE); } /* for each voxel */ for(ivox = num_voxels * input_vector_length; ivox--;){ /* skip voxels out of the mask region */ if(ld->mask && !(int)input_data[ld->mask_idx][ivox]){ continue; } value0 = input_data[0][ivox]; if(value0 >= valid_range[0] && value0 <= valid_range[1]){ /* for each volume */ for(i = 0; i < ld->n_datafiles; i++){ valuei = input_data[i][ivox]; /* various voxel sums */ ld->vd[i].nvox++; ld->vd[i].sum += valuei; ld->vd[i].ssum += SQR2(valuei); if (do_sim) { unsigned int iv0 = floor(value0); unsigned int ivi = floor(valuei); if (fabs(iv0 - value0) > 0.01 || fabs(ivi - valuei) > 0.01) { fprintf(stderr, "ERROR: This does not appear to be an integer volume, Dice or Jaccard statistics will not be useful.\n"); exit(EXIT_FAILURE); } if (iv0 < MAX_CMATRIX && ivi < MAX_CMATRIX) { ld->vd[i].cmatrix[iv0][ivi]++; } else { fprintf(stderr, "ERROR: Can only compute Dice or Jaccard statistics for labeled volumes with a maximum label value of %d.\n", MAX_CMATRIX - 1); } } if(i != 0){ ld->vd[i].sum_prd0 += valuei * value0; ld->vd[i].ssum_add0 += SQR2(valuei + value0); ld->vd[i].ssum_dif0 += SQR2(valuei - value0); ld->vd[i].ssum_prd0 += SQR2(valuei * value0); } /* min and max */ if(valuei < ld->vd[i].min){ ld->vd[i].min = valuei; } else if(valuei > ld->vd[i].max){ ld->vd[i].max = valuei; } } } } return; } /* intermediate calculations */ void do_int_calcs(Loop_Data * ld){ int i; double denom; for(i = 0; i < ld->n_datafiles; i++){ /* mean */ ld->vd[i].mean = ld->vd[i].sum / ld->vd[i].nvox; /* variance */ ld->vd[i].var = ((ld->vd[i].nvox * ld->vd[i].ssum) - SQR2(ld->vd[i].sum)) / (ld->vd[i].nvox * (ld->vd[i].nvox - 1)); /* sd */ ld->vd[i].sd = sqrt(ld->vd[i].var); /* RMSE */ ld->vd[i].rmse = sqrt((1.0 / ld->vd[0].nvox) * ld->vd[i].ssum_dif0); /* xcorr */ denom = sqrt(ld->vd[0].ssum * ld->vd[i].ssum); ld->vd[i].xcorr = (denom == 0.0) ? 0.0 : ld->vd[i].sum_prd0 / denom; } } /* voxel loop function for second pass through data */ void pass_1(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info * loop_info){ long ivox; double valuei, value0; int i; /* get pointer to loop data */ Loop_Data *ld = (Loop_Data *)caller_data; /* shut the compiler up - yes I _know_ I don't use these */ (void)output_num_buffers; (void)output_vector_length; (void)output_data; (void)loop_info; /* sanity check */ if((input_num_buffers < 2) || (output_num_buffers != 0)){ fprintf(stderr, "Bad arguments to pass_1\n"); exit(EXIT_FAILURE); } /* for each voxel */ for(ivox = num_voxels * input_vector_length; ivox--;){ /* skip voxels out of the mask region */ if(ld->mask && !(int)input_data[ld->mask_idx][ivox]){ continue; } value0 = input_data[0][ivox]; if(value0 >= valid_range[0] && value0 <= valid_range[1]){ /* for each volume */ for(i = 0; i < ld->n_datafiles; i++){ valuei = input_data[i][ivox]; if(i != 0){ /* zscore total */ ld->vd[i].sum_zdif0 += fabs(((value0 - ld->vd[0].mean) / ld->vd[0].sd) - ((valuei - ld->vd[i].mean) / ld->vd[i].sd)); } } } } return; } /* final calculations */ void do_final_calcs(Loop_Data * ld){ int i; for(i = 0; i < ld->n_datafiles; i++){ /* zscore */ ld->vd[i].zscore = ld->vd[i].sum_zdif0 / ld->vd[i].nvox; } } /* dirty little function to print out results */ void print_result(char *title, double result){ if(!quiet){ fprintf(stdout, "%s", title); } fprintf(stdout, "%.10g\n", result); } void print_id_result(char *title, int id, double result){ if(!quiet){ fprintf(stdout, "%s", title); } fprintf(stdout, "%d %.10g\n", id, result); } /* debug function to dump stats structure */ void dump_stats(Loop_Data * ld){ int i; fprintf(stdout, " + Main Loop data structure\n"); fprintf(stdout, " | n_datafiles %d\n", ld->n_datafiles); fprintf(stdout, " | mask %d\n", ld->mask); fprintf(stdout, " | mask_idx %d\n", ld->mask_idx); fprintf(stdout, " +++ volume data stats\n"); for(i = 0; i < ld->n_datafiles; i++){ fprintf(stdout, " |---------------------------------\n"); fprintf(stdout, " | [%02d] nvox %10g\n", i, ld->vd[i].nvox); fprintf(stdout, " | [%02d] sum %.10g\n", i, ld->vd[i].sum); fprintf(stdout, " | [%02d] ssum %.10g\n", i, ld->vd[i].ssum); fprintf(stdout, " | [%02d] min %.10g\n", i, ld->vd[i].min); fprintf(stdout, " | [%02d] max %.10g\n", i, ld->vd[i].max); fprintf(stdout, " | [%02d] mean %.10g\n", i, ld->vd[i].mean); fprintf(stdout, " | [%02d] var %.10g\n", i, ld->vd[i].var); fprintf(stdout, " | [%02d] sd %.10g\n", i, ld->vd[i].sd); fprintf(stdout, " | [%02d] sum_prd0 %.10g\n", i, ld->vd[i].sum_prd0); fprintf(stdout, " | [%02d] ssum_add0 %.10g\n", i, ld->vd[i].ssum_add0); fprintf(stdout, " | [%02d] ssum_dif0 %.10g\n", i, ld->vd[i].ssum_dif0); fprintf(stdout, " | [%02d] ssum_prd0 %.10g\n", i, ld->vd[i].ssum_prd0); fprintf(stdout, " | [%02d] sum_zdif0 %.10g\n", i, ld->vd[i].sum_zdif0); fprintf(stdout, " | [%02d] rmse %.10g\n", i, ld->vd[i].rmse); fprintf(stdout, " | [%02d] xcorr %.10g\n", i, ld->vd[i].xcorr); fprintf(stdout, " | [%02d] zscore %.10g\n", i, ld->vd[i].zscore); fprintf(stdout, " | [%02d] vratio %.10g\n", i, ld->vd[i].vratio); } } minc-tools-2.3.00+dfsg/progs/minccmp/minccmp.man10000644000175000000620000000747512574624760020623 0ustar stevestaff.\" Copyright 2010 Andrew Janke .\" 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 makes no .\" representations about the suitability of this software .\" for any purpose. It is provided "as is" without .\" express or implied warranty. .\" .\" .TH MINCCMP 1 "$Date: 2010-03-02 12:12:20 $" "" "MINC User's Guide" .SH NAME minccmp - compare one or more minc file using comparator operators .SH SYNOPSIS .B minccmp [] [ ...] .SH DESCRIPTION \fIminccmp\fR will calculate simple statistical measures between two minc files or more by comparing all subsequent files to the first. The results for each subseqent file are then returned in order. By default all statistics are calculated. If specifitc statistics are requested via a command-line option, then only the requested statistics are printed. A very useful feature of this program is the ability to restrict the set of voxels included in the statistic calculation, either by restricting the range of included values (-floor, -ceil or -range), or by using a mask file (-mask) with a restricted range. The comparison statistics available in minccmp are given below. Note that two of these (-xcorr and -zscore) are a very close approximation to what is used in minctracc. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-debug\fR Dump a lot of extra information (for when things go haywire). .TP \fB\-verbose\fR Print out extra information (more than the default). .TP \fB\-quiet\fR Print out only the requested numbers .TP \fB\-max_buffer_size_in_kb\fR\ \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4 MB. .TP \fB\-check_dimensions\fR Check that all input files have matching sampling in world dimensions (default). .TP \fB\-nocheck_dimensions\fR Ignore any differences in world dimensions sampling for input files . .SH Volume range options .TP \fB\-floor\fR\ \fImin\fR A lower bound for ranges of data to include in statistic calculations. .TP \fB\-ceil\fR\ \fImax\fR An upper bound for ranges of data to include in statistic calculations. .TP \fB\-range\fR\ \fImin\fR,\fImax\fR A lower and upper bound for the ranges of data to include in statistics. .TP \fB\-mask\fR\ \fIfilename.mnc\fR Name of file to be used for masking data included in statistic calculations. .SH Basic statistics .TP \fB\-all\fR Compute all statistical measures. This is the default. .TP \fB\-ssq\fR Print the Sum Squared Difference between two input files SSQ = Sum( (A-B)^2 ) .TP \fB\-rmse\fR Print the Root Mean Squared Error between two input files RMSE = sqrt( 1/n * Sum((A-B)^2)) .TP \fB\-xcorr\fR Print the Cross Correlation between two input files XCORR = Sum((A*B)^2) / (sqrt(Sum(A^2)) * sqrt(Sum(B^2)) .TP \fB\-zscore\fR Print the z-score difference between two input files ZSCORE = Sum( |((A - mean(A)) / stdev(A)) - ((B - mean(B)) / stdev(B))| ) / n .TP \fB\-similarity\fR Calculate the confusion matrix, assuming that the volume values represent a discrete class of possible values. The maximum label value is currently limited to ten (10). Prints the Dice similarity statistic as well as specificity, sensitivity, accuracy, and kappa for each class and for the overall volumes. .SH Generic options for all commands: .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Andrew Janke .SH COPYRIGHTS .P Copyright \(co 2010 by Andrew Janke minc-tools-2.3.00+dfsg/progs/mincmath/0002755000175000000620000000000012574624760016556 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmath/mincmath.man10000644000175000000620000002236512574624760021142 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1995 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincmath/mincmath.man1,v 6.3 2004-05-20 21:52:08 bert Exp $ .\" .TH MINCMATH 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincmath - perform simple math operations on minc files .SH SYNOPSIS .B mincmath [] .mnc [.mnc...] .mnc .SH DESCRIPTION \fIMincmath\fR will perform simple, voxel-by-voxel math operations, on one or more minc files of the same shape and having the same coordinate sampling, producing a single output file. Operations can be unary (operate on one file), binary (two input files) or cumulative (operate on two or more input files). Cumulative operations can also be performed across a specified dimension of the input files. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-no_clobber\fR Synonym for \fB\-noclobber\fR. .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-debug\fR Print out debugging information. .TP \fB\-filelist\fR\ \fIfilename\fR Specify a file containing a list of input file names. If "-" is given, then file names are read from the standard input. If this option is given, then there should be no input file names specified on the command line. Empty lines in the input file are ignored. .TP \fB\-copy_header\fR Copy all of the header information from the first input file (default for one input file). .TP \fB\-nocopy_header\fR Do not copy all of the header from the first input file; copy only coordinate information (default for more than one input file). .TP \fB\-filetype\fR Create an output file with the same type as the first input file (default). .TP \fB\-byte\fR Store output voxels in 8-bit integer format. .TP \fB\-short\fR Store output voxels in 16-bit integer format. .TP \fB-int\fR Store output voxels in 32-bit integer format. .TP \fB-long\fR Superseded by \fB\-int\fR. .TP \fB-float\fR Store output voxels in 32-bit floating point format. .TP \fB-double\fR Store output voxels in 64-bit floating point format. .TP \fB\-signed\fR Use signed, two's complement integer format. Applies only if the output voxel type is specified to be an integer type (one of \fB\-byte\fR, \fB\-short\fR, \fB\-int\fR or \fB-long\fR). .TP \fB\-unsigned\fR Use unsigned integer format. Applies only if the output voxel type is specified to be an integer type (one of \fB\-byte\fR, \fB\-short\fR, \fB\-int\fR or \fB-long\fR). .TP \fB\-range\fR \fImin max\fR Restrict the valid range of integer data. Applies only if one of the \fB-byte\fR, \fB-short\fR, \fB-int\fR or \fB\-long\fR options is specified. .TP \fB\-max_buffer_size_in_kb\fR \fIsize\fR Specify the maximum size of the internal buffers (in kbytes). Default is 4096 (4MB). .TP \fB\-dimension\fR\ \fIdimname\fR Specify a dimension along which we wish to perform a cumulative operation. .TP \fB\-check_dimensions\fR Check that all input files have matching sampling in world dimensions (default). .TP \fB\-nocheck_dimensions\fR Ignore any differences in world dimensions sampling for input files . .TP \fB\-propagate_nan\fR Invalid data (Not-A-Number or NaN) at a voxel in any of the input files will produce invalid data in the output file at that voxel (default). .TP \fB\-ignore_nan\fR For cumulative operations, invalid data (NaN) in an input file is ignored, ie. treated as though it is not present. .TP \fB\-nan\fR When an illegal operation is attempted at a voxel (such as divide by zero), invalid data (NaN) is stored in the output file (default). Having no valid input data for a cumulative operation is also considered an illegal operation when \fB\-ignore_nan\fR is used. .TP \fB\-zero\fR When an illegal operation is attempted at a voxel (such as divide by zero), value zero is stored in the output file. .TP \fB\-illegal_value\fR\ \fIvalue\fR When an illegal operation is attempted at a voxel (such as divide by zero), the specified value is stored in the output file. .SH Options for specifying constants .TP \fB\-constant\fR\ \fIvalue\fR Specify a single constant. .TP \fB\-const\fR\ \fIvalue\fR Synonym for \fB\-constant\fR. .TP \fB\-const2\fR\ \fIvalue1 value2\fR Specify two constants. .SH Operations .TP \fB\-add\fR Cumulatively add two or more volumes, or add a volume and a constant. .TP \fB\-sub\fR Subtract two volumes or a volume minus a constant. .TP \fB\-mult\fR Cumulatively multiply two or more volumes, or multiply a volume and a constant. .TP \fB\-div\fR Divide two volumes or a volume divided by a constant. .TP \fB\-invert\fR Calculate 1/x at each voxel, where x is the input voxel value. If a constant c is specified (with -constant), then calculate c/x at each voxel. .TP \fB\-sqrt\fR Calculate the square root of a volume. .TP \fB\-square\fR Calculate the square of a volume. .TP \fB\-abs\fR Calculate the absolute value of a volume. .TP \fB\-maximum\fR Calculate the maximum of a series of volumes. .TP \fB\-minimum\fR Calculate the minimum of a series of volumes. .TP \fB\-exp\fR Calculate \fIc2*exp(c1*x)\fR at each voxel of a volume, where \fIx\fR is the voxel value and \fIc1\fR and \fIc2\fR are constants specified by \fB\-constant c1\fR or \fB\-const2 c1 c2\fR. The default value for these constants is 1.0. .TP \fB\-log\fR Calculate \fIlog(x/c2)/c1\fR for each voxel of a volume, where \fIx\fR is the voxel value and \fIc1\fR and \fIc2\fR are constants specified by \fB\-constant c1\fR or \fB\-const2 c1 c2\fR. The default value for these constants is 1.0. .TP \fB\-scale\fR Scale a volume either by multiplying by a single constant (use -constant) or by multiplying by the first constant and adding the second (use -const2). .TP \fB\-clamp\fR Clamp a volume to lie between two values specified with \fB-const2\fR. .TP \fB\-segment\fR Segment (binarize) a volume so that values within the range specified by \fB\-const2\fR give value 1 and those outside it give value 0. .TP \fB\-nsegment\fR Opposite of \fB\-segment\fR: values within the range specified by \fB\-const2\fR give value 0 and those outside it give value 1. .TP \fB\-percentdiff\fR Calculate the percent difference between two volumes (normalized to the first volume). If the first volume is less than a threshold (or zero), then the value specified by \fB\-nan\fR or \fB\-zero\fR is used. The threshold is specified using \fB\-constant\fR, with a default of zero. .TP \fB\-pd\fR Synonym for \fB\-percentdiff\fR. .TP \fB\-eq\fR Test for equality of two volumes or a volume and a constant. Values are rounded to the nearest integer before performing the test. Output 1 for true and 0 for false at each voxel. .TP \fB\-ne\fR Test for inequality of two volumes or a volume and a constant. Values are rounded to the nearest integer before performing the test. Output 1 for true and 0 for false at each voxel. .TP \fB\-gt\fR Test for volume 1 > volume 2 or a volume > a constant. Output 1 for true and 0 for false at each voxel. .TP \fB\-ge\fR Test for volume 1 >= volume 2 or a volume >= a constant. Output 1 for true and 0 for false at each voxel. .TP \fB\-lt\fR Test for volume 1 < volume 2 or a volume < a constant. Output 1 for true and 0 for false at each voxel. .TP \fB\-le\fR Test for volume 1 <= volume 2 or a volume <= a constant. Output 1 for true and 0 for false at each voxel. .TP \fB\-and\fR Test for volume 1 && volume 2 or a volume && a constant. Values are rounded to the nearest integer before performing the test. Output 1 for true and 0 for false at each voxel. .TP \fB\-or\fR Test for volume 1 || volume 2 or a volume || a constant. Values are rounded to the nearest integer before performing the test. Output 1 for true and 0 for false at each voxel. .TP \fB\-not\fR Perform logical negation on a volume: convert non-zero to zero and zero to one. Values are rounded to the nearest integer before the negation. .TP \fB\-isnan\fR Test a volume for invalid values (NaN). Output 1 for invalid values and 0 for valid values. .TP \fB\-nisnan\fR Opposite of -isnan. Output 0 for invalid values and 1 for valid values. .TP \fB\-count_valid\fR Count the number of valid voxels across a series of volumes. If none of the volumes has valid data, then zero is written out (ie. \fB\-zero\fR and \fB\-ignore_nan\fR are always assumed, unlike other cumulative operations). .SH Generic options for all commands: .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1995 by Peter Neelin .SH "SEE ALSO" .LP .BR minccalc (1) minc-tools-2.3.00+dfsg/progs/mincmath/mincmath.c0000644000175000000620000010412012574624760020516 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincmath @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to do simple math on minc files @METHOD : @GLOBALS : @CALLS : @CREATED : April 28, 1995 (Peter Neelin) @MODIFIED : * $Log: mincmath.c,v $ * Revision 6.15 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.14 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.13 2008/01/13 04:30:28 stever * Add braces around static initializers. * * Revision 6.12 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.11 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 6.10 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.9 2004/12/14 23:40:07 bert * Get rid of c99 compilation problems * * Revision 6.8 2004/12/03 21:56:51 bert * Include config.h * * Revision 6.7 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.6 2004/04/27 15:31:45 bert * Added -2 option * * Revision 6.5 2001/04/24 13:38:44 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.4 2001/04/17 18:40:22 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.3 2001/01/15 14:58:43 neelin * Modified description of -segment option. * * Revision 6.2 2000/07/07 13:19:54 neelin * Added option -filelist to read file names from a file. This gets around * command-line length limits. * * Revision 6.1 1999/10/19 14:45:26 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:17 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:16 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:02:04 neelin * Release of minc version 0.4 * * Revision 3.5 1997/04/24 13:48:51 neelin * Fixed handling of invalid or uninitialized data for cumulative operations. * Added options -illegal_value and -count_valid. * * Revision 3.4 1997/04/23 19:34:56 neelin * Added options -maximum, -minimum, -abs. * * Revision 3.3 1996/01/17 21:24:06 neelin * Added -exp and -log options. * * Revision 3.2 1996/01/16 13:29:31 neelin * Added -invert option. * * Revision 3.1 1995/12/13 16:22:24 neelin * Added -check_dimensions and -nocheck_dimensions options. * * Revision 3.0 1995/05/15 19:32:42 neelin * Release of minc version 0.3 * * Revision 1.2 1995/05/03 16:13:46 neelin * Changed default for -copy/-nocopy to depend on number of input files. * * Revision 1.1 1995/05/03 13:19:56 neelin * Initial revision * ---------------------------------------------------------------------------- */ #define _GNU_SOURCE 1 #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Data values for invalid data and for uninitialized data */ #define INVALID_DATA -DBL_MAX #define UNINITIALIZED_DATA DBL_MAX /* Values for representing default case for command-line options */ #define DEFAULT_DBL DBL_MAX #define DEFAULT_BOOL -1 /* Typedefs */ typedef enum { UNSPECIFIED_OP = 0, ADD_OP, SUB_OP, MULT_OP, DIV_OP, SQRT_OP, SQUARE_OP, SCALE_OP, CLAMP_OP, SEGMENT_OP, NSEGMENT_OP, PERCENTDIFF_OP, EQ_OP, NE_OP, GT_OP, GE_OP, LT_OP, LE_OP, AND_OP, OR_OP, NOT_OP, ISNAN_OP, NISNAN_OP, INVERT_OP, EXP_OP, LOG_OP, MAX_OP, MIN_OP, ABS_OP, COUNT_OP } Operation; typedef enum { ILLEGAL_NUMOP, UNARY_NUMOP, BINARY_NUMOP, NARY_NUMOP } Num_Operands; /* Table that matches [Operation][Number of constants (0,1,2)] to a number of volume operands */ Num_Operands OperandTable[][3] = { { ILLEGAL_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* UNSPECIFIED_OP */ { NARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* ADD_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* SUB_OP */ { NARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* MULT_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* DIV_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* SQRT_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* SQUARE_OP */ { ILLEGAL_NUMOP, UNARY_NUMOP, UNARY_NUMOP }, /* SCALE_OP */ { ILLEGAL_NUMOP, ILLEGAL_NUMOP, UNARY_NUMOP }, /* CLAMP_OP */ { ILLEGAL_NUMOP, ILLEGAL_NUMOP, UNARY_NUMOP }, /* SEGMENT_OP */ { ILLEGAL_NUMOP, ILLEGAL_NUMOP, UNARY_NUMOP }, /* NSEGMENT_OP */ { BINARY_NUMOP, BINARY_NUMOP, ILLEGAL_NUMOP }, /* PERCENTDIFF_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* EQ_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* NE_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* GT_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* GE_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* LT_OP */ { BINARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* LE_OP */ { NARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* AND_OP */ { NARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* OR_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* NOT_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* ISNAN_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* NISNAN_OP */ { UNARY_NUMOP, UNARY_NUMOP, ILLEGAL_NUMOP }, /* INVERT_OP */ { UNARY_NUMOP, UNARY_NUMOP, UNARY_NUMOP }, /* EXP_OP */ { UNARY_NUMOP, UNARY_NUMOP, UNARY_NUMOP }, /* LOG_OP */ { NARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* MAX_OP */ { NARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* MIN_OP */ { UNARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* ABS_OP */ { NARY_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP }, /* COUNT_OP */ { ILLEGAL_NUMOP, ILLEGAL_NUMOP, ILLEGAL_NUMOP } /* nothing */ }; /* Structure for window information */ typedef struct { Operation operation; Num_Operands num_operands; int num_constants; double constants[2]; int propagate_nan; double illegal_value; } Math_Data; /* Function prototypes */ static void do_math(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void accum_math(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void start_math(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void end_math(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; static int debug = FALSE; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = FALSE; static double valid_range[2] = {0.0, 0.0}; static int copy_all_header = DEFAULT_BOOL; static char *loop_dimension = NULL; static int max_buffer_size_in_kb = 4 * 1024; static double constant = DEFAULT_DBL; static double constant2[2] = {DEFAULT_DBL, DEFAULT_DBL}; static Operation operation = UNSPECIFIED_OP; static int propagate_nan = TRUE; static int use_nan_for_illegal_values = TRUE; static double value_for_illegal_operations = DEFAULT_DBL; static int check_dim_info = TRUE; static char *filelist = NULL; #if MINC2 static int minc2_format = FALSE; #endif /* MINC2 */ /* Argument table */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "General options:"}, #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format, "Produce a MINC 2.0 format output file"}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-no_clobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Synonym for -noclobber."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages (default)."}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out log messages."}, {"-debug", ARGV_CONSTANT, (char *) TRUE, (char *) &debug, "Print out debugging messages."}, {"-filelist", ARGV_STRING, (char *) 1, (char *) &filelist, "Specify the name of a file containing input file names (- for stdin)."}, {"-copy_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all of the header from the first file."}, {"-nocopy_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Do not copy all of the header from the first file."}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Use data type of first file (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Write out short integer data."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Write out 32-bit integer data."}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Write out single-precision floating-point data."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Write out double-precision floating-point data."}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Write unsigned integer data (default if type specified)."}, {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data."}, {"-max_buffer_size_in_kb", ARGV_INT, (char *) 1, (char *) &max_buffer_size_in_kb, "Specify the maximum size of the internal buffers (in kbytes)."}, {"-dimension", ARGV_STRING, (char *) 1, (char *) &loop_dimension, "Specify a dimension along which we wish to perform a calculation."}, {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE, (char *) &check_dim_info, "Check that files have matching dimensions (default)."}, {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE, (char *) &check_dim_info, "Do not check that files have matching dimensions."}, {"-ignore_nan", ARGV_CONSTANT, (char *) FALSE, (char *) &propagate_nan, "Ignore invalid data (NaN) for accumulations."}, {"-propagate_nan", ARGV_CONSTANT, (char *) TRUE, (char *) &propagate_nan, "Invalid data in any file at a voxel produces a NaN (default)."}, {"-nan", ARGV_CONSTANT, (char *) TRUE, (char *) &use_nan_for_illegal_values, "Output NaN when an illegal operation is done (default)."}, {"-zero", ARGV_CONSTANT, (char *) FALSE, (char *) &use_nan_for_illegal_values, "Output zero when an illegal operation is done."}, {"-illegal_value", ARGV_FLOAT, (char *) 1, (char *) &value_for_illegal_operations, "Value to write out when an illegal operation is done."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Options for specifying constants:"}, {"-constant", ARGV_FLOAT, (char *) 1, (char *) &constant, "Specify a constant argument."}, {"-const", ARGV_FLOAT, (char *) 1, (char *) &constant, "Synonym for -constant."}, {"-const2", ARGV_FLOAT, (char *) 2, (char *) constant2, "Specify two constant arguments."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Operations:"}, {"-add", ARGV_CONSTANT, (char *) ADD_OP, (char *) &operation, "Add N volumes or volume + constant."}, {"-sub", ARGV_CONSTANT, (char *) SUB_OP, (char *) &operation, "Subtract 2 volumes or volume - constant."}, {"-mult", ARGV_CONSTANT, (char *) MULT_OP, (char *) &operation, "Multiply N volumes or volume * constant."}, {"-div", ARGV_CONSTANT, (char *) DIV_OP, (char *) &operation, "Divide 2 volumes or volume / constant."}, {"-invert", ARGV_CONSTANT, (char *) INVERT_OP, (char *) &operation, "Calculate 1/x at each voxel (use -constant for c/x)."}, {"-sqrt", ARGV_CONSTANT, (char *) SQRT_OP, (char *) &operation, "Take square root of a volume."}, {"-square", ARGV_CONSTANT, (char *) SQUARE_OP, (char *) &operation, "Take square of a volume."}, {"-abs", ARGV_CONSTANT, (char *) ABS_OP, (char *) &operation, "Take absolute value of a volume."}, {"-max", ARGV_CONSTANT, (char *) MAX_OP, (char *) &operation, "Synonym for -maximum."}, {"-maximum", ARGV_CONSTANT, (char *) MAX_OP, (char *) &operation, "Find maximum of N volumes."}, {"-minimum", ARGV_CONSTANT, (char *) MIN_OP, (char *) &operation, "Find minimum of N volumes."}, {"-exp", ARGV_CONSTANT, (char *) EXP_OP, (char *) &operation, "Calculate c2*exp(c1*x). The constants c1 and c2 default to 1."}, {"-log", ARGV_CONSTANT, (char *) LOG_OP, (char *) &operation, "Calculate log(x/c2)/c1. The constants c1 and c2 default to 1."}, {"-scale", ARGV_CONSTANT, (char *) SCALE_OP, (char *) &operation, "Scale a volume: volume * c1 + c2."}, {"-clamp", ARGV_CONSTANT, (char *) CLAMP_OP, (char *) &operation, "Clamp a volume to lie between two values."}, {"-segment", ARGV_CONSTANT, (char *) SEGMENT_OP, (char *) &operation, "Segment a volume using range of -const2: within range = 1, outside range = 0."}, {"-nsegment", ARGV_CONSTANT, (char *) NSEGMENT_OP, (char *) &operation, "Opposite of -segment: within range = 0, outside range = 1."}, {"-percentdiff", ARGV_CONSTANT, (char *) PERCENTDIFF_OP, (char *) &operation, "Percent difference between 2 volumes, thresholded (const def=0.0)."}, {"-pd", ARGV_CONSTANT, (char *) PERCENTDIFF_OP, (char *) &operation, "Synonym for -percentdiff."}, {"-eq", ARGV_CONSTANT, (char *) EQ_OP, (char *) &operation, "Test for integer vol1 == vol2 or vol1 == const."}, {"-ne", ARGV_CONSTANT, (char *) NE_OP, (char *) &operation, "Test for integer vol1 != vol2 or vol1 != const."}, {"-gt", ARGV_CONSTANT, (char *) GT_OP, (char *) &operation, "Test for vol1 > vol2 or vol1 > const."}, {"-ge", ARGV_CONSTANT, (char *) GE_OP, (char *) &operation, "Test for vol1 >= vol2 or vol1 >= const."}, {"-lt", ARGV_CONSTANT, (char *) LT_OP, (char *) &operation, "Test for vol1 < vol2 or vol1 < const."}, {"-le", ARGV_CONSTANT, (char *) LE_OP, (char *) &operation, "Test for vol1 <= vol2 or vol1 <= const."}, {"-and", ARGV_CONSTANT, (char *) AND_OP, (char *) &operation, "Calculate vol1 && vol2 (&& ...)."}, {"-or", ARGV_CONSTANT, (char *) OR_OP, (char *) &operation, "Calculate vol1 || vol2 (|| ...)."}, {"-not", ARGV_CONSTANT, (char *) NOT_OP, (char *) &operation, "Calculate !vol1."}, {"-isnan", ARGV_CONSTANT, (char *) ISNAN_OP, (char *) &operation, "Test for NaN values in vol1."}, {"-nisnan", ARGV_CONSTANT, (char *) NISNAN_OP, (char *) &operation, "Negation of -isnan."}, {"-count_valid", ARGV_CONSTANT, (char *) COUNT_OP, (char *) &operation, "Count the number of valid values in N volumes."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char **infiles, **outfiles; int nfiles, nout; char *arg_string; Math_Data math_data; Loop_Options *loop_options; char *pname; int num_constants; Num_Operands num_operands; VoxelFunction math_function; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { (void) fprintf(stderr, "\nUsage: %s [options] [ ...] \n", pname); (void) fprintf(stderr, " %s -help\n\n", pname); exit(EXIT_FAILURE); } nout = 1; outfiles = &argv[argc-1]; /* Get the list of input files either from the command line or from a file, or report an error if both are specified */ nfiles = argc - 2; if (filelist == NULL) { infiles = &argv[1]; } else if (nfiles <= 0) { infiles = read_file_names(filelist, &nfiles); if (infiles == NULL) { (void) fprintf(stderr, "Error reading in file names from file \"%s\"\n", filelist); exit(EXIT_FAILURE); } } else { (void) fprintf(stderr, "Do not specify both -filelist and input file names\n"); exit(EXIT_FAILURE); } /* Make sure that we have something to process */ if (nfiles == 0) { (void) fprintf(stderr, "No input files specified\n"); exit(EXIT_FAILURE); } /* Handle special case of COUNT_OP - it always assume -ignore_nan and -zero */ if (operation == COUNT_OP) { propagate_nan = FALSE; value_for_illegal_operations = 0.0; } /* Check that the arguments make sense */ if ((constant != DEFAULT_DBL) && (constant2[0] != DEFAULT_DBL)) { (void) fprintf(stderr, "%s: Specify only one of -constant or -const2\n", pname); exit(EXIT_FAILURE); } if (constant != DEFAULT_DBL) num_constants = 1; else if (constant2[0] != DEFAULT_DBL) num_constants = 2; else num_constants = 0; num_operands = OperandTable[operation][num_constants]; if (num_operands == ILLEGAL_NUMOP) { (void) fprintf(stderr, "%s: Operation and constants do not match.\n", pname); exit(EXIT_FAILURE); } if ((num_operands != NARY_NUMOP) && (loop_dimension != NULL)) { (void) fprintf(stderr, "%s: Use -dimension only for cumulative ops.\n", pname); exit(EXIT_FAILURE); } if ((num_operands == UNARY_NUMOP) && (nfiles != 1)) { (void) fprintf(stderr, "%s: Expected only one input file.\n", pname); exit(EXIT_FAILURE); } if ((num_operands == BINARY_NUMOP) && (nfiles != 2)) { (void) fprintf(stderr, "%s: Expected two input files.\n", pname); exit(EXIT_FAILURE); } if ((num_operands == NARY_NUMOP) && (nfiles < 1) && (loop_dimension == NULL)) { (void) fprintf(stderr, "%s: Expected at least one input files.\n", pname); exit(EXIT_FAILURE); } /* Set default copy_all_header according to number of input files */ if (copy_all_header == DEFAULT_BOOL) copy_all_header = (nfiles == 1); /* Set up math data structure */ math_data.operation = operation; math_data.num_operands = num_operands; math_data.propagate_nan = propagate_nan; math_data.num_constants = num_constants; switch (num_constants) { case 1: math_data.constants[0] = constant; break; case 2: math_data.constants[0] = constant2[0]; math_data.constants[1] = constant2[1]; break; } if (value_for_illegal_operations != DEFAULT_DBL) math_data.illegal_value = value_for_illegal_operations; else if (use_nan_for_illegal_values) math_data.illegal_value = INVALID_DATA; else math_data.illegal_value = 0.0; /* Do math */ loop_options = create_loop_options(); set_loop_verbose(loop_options, verbose); set_loop_clobber(loop_options, clobber); #if MINC2 set_loop_v2format(loop_options, minc2_format); #endif /* MINC2 */ set_loop_datatype(loop_options, datatype, is_signed, valid_range[0], valid_range[1]); if (num_operands == NARY_NUMOP) { math_function = accum_math; set_loop_accumulate(loop_options, TRUE, 0, start_math, end_math); } else { math_function = do_math; } set_loop_copy_all_header(loop_options, copy_all_header); set_loop_dimension(loop_options, loop_dimension); set_loop_buffer_size(loop_options, (long) 1024 * max_buffer_size_in_kb); set_loop_check_dim_info(loop_options, check_dim_info); voxel_loop(nfiles, infiles, nout, outfiles, arg_string, loop_options, math_function, (void *) &math_data); free_loop_options(loop_options); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_math @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Routine doing math operations. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_math(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Math_Data *math_data; long ivox; double value1, value2; double illegal_value; Operation operation; unsigned int num_constants, iconst; double constants[2]; /* Get pointer to window info */ math_data = (Math_Data *) caller_data; /* Check arguments */ if ((input_num_buffers > 2) || (output_num_buffers != 1) || (output_vector_length != input_vector_length)) { (void) fprintf(stderr, "Bad arguments to do_math!\n"); exit(EXIT_FAILURE); } /* Get info */ operation = math_data->operation; num_constants = math_data->num_constants; for (iconst=0; iconst < sizeof(constants)/sizeof(constants[0]); iconst++) { if (iconst < num_constants) constants[iconst] = math_data->constants[iconst]; else if ((operation == INVERT_OP) || (operation == EXP_OP) || (operation == LOG_OP)) constants[iconst] = 1.0; else constants[iconst] = 0.0; } illegal_value = math_data->illegal_value; /* Set default second value */ value2 = constants[0]; /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*input_vector_length; ivox++) { value1 = input_data[0][ivox]; if (input_num_buffers == 2) value2 = input_data[1][ivox]; if ((value1 == INVALID_DATA) || (value2 == INVALID_DATA)) { switch(operation) { case ISNAN_OP: output_data[0][ivox] = 1.0; break; case NISNAN_OP: output_data[0][ivox] = 0.0; break; default: output_data[0][ivox] = INVALID_DATA; break; } } else { switch (operation) { case ADD_OP: output_data[0][ivox] = value1 + value2; break; case SUB_OP: output_data[0][ivox] = value1 - value2; break; case MULT_OP: output_data[0][ivox] = value1 * value2; break; case DIV_OP: if (value2 != 0.0) output_data[0][ivox] = value1 / value2; else output_data[0][ivox] = illegal_value; break; case INVERT_OP: if (value1 == 0.0) output_data[0][ivox] = illegal_value; else output_data[0][ivox] = value2 / value1; break; case SQRT_OP: if (value1 < 0.0) output_data[0][ivox] = illegal_value; else output_data[0][ivox] = sqrt(value1); break; case SQUARE_OP: output_data[0][ivox] = value1 * value1; break; case ABS_OP: if (value1 < 0.0) output_data[0][ivox] = -value1; else output_data[0][ivox] = value1; break; case EXP_OP: output_data[0][ivox] = constants[1] * exp(value1 * constants[0]); break; case LOG_OP: if ((value1 <= 0.0) || (constants[1] <= 0.0) || (constants[0] == 0.0)) output_data[0][ivox] = illegal_value; else output_data[0][ivox] = log(value1/constants[1])/constants[0]; break; case SCALE_OP: output_data[0][ivox] = value1 * constants[0] + constants[1]; break; case CLAMP_OP: if (value1 < constants[0]) value1 = constants[0]; else if (value1 > constants[1]) value1 = constants[1]; output_data[0][ivox] = value1; break; case SEGMENT_OP: if ((value1 < constants[0]) || (value1 > constants[1])) output_data[0][ivox] = 0.0; else output_data[0][ivox] = 1.0; break; case NSEGMENT_OP: if ((value1 < constants[0]) || (value1 > constants[1])) output_data[0][ivox] = 1.0; else output_data[0][ivox] = 0.0; break; case PERCENTDIFF_OP: if ((value1 < constants[0]) || (value1 == 0.0)) output_data[0][ivox] = illegal_value; else { output_data[0][ivox] = 100.0 * (value1 - value2) / value1; } break; case EQ_OP: output_data[0][ivox] = (((rint(value1)-rint(value2)) == 0.0) ? 1.0 : 0.0); break; case NE_OP: output_data[0][ivox] = (((rint(value1)-rint(value2)) != 0.0) ? 1.0 : 0.0); break; case GT_OP: output_data[0][ivox] = value1 > value2; break; case GE_OP: output_data[0][ivox] = value1 >= value2; break; case LT_OP: output_data[0][ivox] = value1 < value2; break; case LE_OP: output_data[0][ivox] = value1 <= value2; break; case AND_OP: output_data[0][ivox] = (((rint(value1) != 0.0) && (rint(value2) != 0.0)) ? 1.0 : 0.0); break; case OR_OP: output_data[0][ivox] = (((rint(value1) != 0.0) || (rint(value2) != 0.0)) ? 1.0 : 0.0); break; case NOT_OP: output_data[0][ivox] = ((rint(value1) == 0.0) ? 1.0 : 0.0); break; case ISNAN_OP: output_data[0][ivox] = 0.0; /* To get here, value is not nan */ break; case NISNAN_OP: output_data[0][ivox] = 1.0; break; default: (void) fprintf(stderr, "Bad op in do_math!\n"); exit(EXIT_FAILURE); } } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : accum_math @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Routine for doing accumulation math operations. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void accum_math(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Math_Data *math_data; long ivox; double value, oldvalue; Operation operation; int propagate_nan; /* Get pointer to window info */ math_data = (Math_Data *) caller_data; /* Check arguments */ if ((input_num_buffers != 1) || (output_num_buffers != 1) || (output_vector_length != input_vector_length)) { (void) fprintf(stderr, "Bad arguments to accum_math!\n"); exit(EXIT_FAILURE); } /* Get info */ operation = math_data->operation; propagate_nan = math_data->propagate_nan; /* Loop through the voxels */ for (ivox=0; ivox < num_voxels*input_vector_length; ivox++) { /* Get previous value and the next value */ oldvalue = output_data[0][ivox]; value = input_data[0][ivox]; /* If the new data is invalid, then either mark the output as invalid or ignore it */ if (value == INVALID_DATA) { if (propagate_nan) { output_data[0][ivox] = INVALID_DATA; } } /* If we haven't set anything yet, then just copy the new value */ else if (oldvalue == UNINITIALIZED_DATA) { output_data[0][ivox] = input_data[0][ivox]; } /* Do the operation if the old data and the new data are valid */ else if (oldvalue != INVALID_DATA) { switch (operation) { case ADD_OP: output_data[0][ivox] = oldvalue + value; break; case MULT_OP: output_data[0][ivox] = oldvalue * value; break; case AND_OP: output_data[0][ivox] = (((oldvalue != 0.0) && (rint(value) != 0.0)) ? 1.0 : 0.0); break; case OR_OP: output_data[0][ivox] = (((oldvalue != 0.0) || (rint(value) != 0.0)) ? 1.0 : 0.0); break; case MAX_OP: if (value > oldvalue) output_data[0][ivox] = value; break; case MIN_OP: if (value < oldvalue) output_data[0][ivox] = value; break; case COUNT_OP: output_data[0][ivox]++; break; default: (void) fprintf(stderr, "Bad op in accum_math!\n"); exit(EXIT_FAILURE); } } } /* Loop over voxels */ return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : start_math @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Start routine for math accumulation. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void start_math(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Math_Data *math_data; long ivox; /* Get pointer to window info */ math_data = (Math_Data *) caller_data; /* Check arguments */ if (output_num_buffers != 1) { (void) fprintf(stderr, "Bad arguments to start_math!\n"); exit(EXIT_FAILURE); } /* Get info */ operation = math_data->operation; /* Loop through the voxels, marking them all as uninitialized. We treat COUNT_OP as a special case since it always has a value. This is especially important to prevent it from going through the code in accum_math for handling the first valid voxel which just assigns the first value. */ for (ivox=0; ivox < num_voxels*output_vector_length; ivox++) { switch (operation) { case COUNT_OP: output_data[0][ivox] = 0.0; break; default: output_data[0][ivox] = UNINITIALIZED_DATA; break; } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : end_math @INPUT : Standard for voxel loop @OUTPUT : Standard for voxel loop @RETURNS : (nothing) @DESCRIPTION: Start routine for math accumulation. @METHOD : @GLOBALS : @CALLS : @CREATED : April 25, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void end_math(void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Math_Data *math_data; long ivox; double value; double illegal_value; /* Get pointer to window info */ math_data = (Math_Data *) caller_data; /* Check arguments */ if (output_num_buffers != 1) { (void) fprintf(stderr, "Bad arguments to end_math!\n"); exit(EXIT_FAILURE); } /* Get info */ operation = math_data->operation; illegal_value = math_data->illegal_value; /* Loop through the voxels, checking for uninitialized values */ for (ivox=0; ivox < num_voxels*output_vector_length; ivox++) { value = output_data[0][ivox]; if ((value == UNINITIALIZED_DATA) || (value == INVALID_DATA)) { output_data[0][ivox] = illegal_value; } } return; } minc-tools-2.3.00+dfsg/progs/Proglib/0002755000175000000620000000000012574624760016354 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/Proglib/convert_origin_to_start.c0000644000175000000620000001460612574624760023473 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : convert_origin_to_start.c @DESCRIPTION: File containing routine to convert an origin specified in world coordinates to 3 minc start values by parallel projection. @METHOD : @GLOBALS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : * $Log: convert_origin_to_start.c,v $ * Revision 6.3 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.2 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.1 1999/10/19 14:45:12 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 1.1 1995/11/10 20:01:27 neelin * Initial revision * @COPYRIGHT : Copyright 1995 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 /* Constant definition */ #define WORLD_NDIMS 3 /* Function declarations */ static void calculate_orthogonal_vector(double vector1[], double vector2[], double result[]); /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_origin_to_start @INPUT : origin - point to project xdircos - vector specifying X direction cosine ydircos - vector specifying Y direction cosine zdircos - vector specifying Z direction cosine @OUTPUT : start - array giving X, Y and Z start values @RETURNS : 0 on success, 1 if some direction cosines have zero length, 2 if some direction cosines are parallel. @DESCRIPTION: Routine to convert an origin specified in world coordinates to an array of minc start positions by projecting the point along the edges of the parallelopiped formed by the 3 direction cosines. Direction cosines are normalized. @METHOD : @GLOBALS : @CALLS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int convert_origin_to_start(double origin[], double xdircos[], double ydircos[], double zdircos[], double start[]) { int idim, jdim; int nparallel; double *axes[WORLD_NDIMS]; double normal[WORLD_NDIMS], lengths[WORLD_NDIMS]; double numerator, denominator, normal_length; /* Set up dircos matrix */ axes[0] = xdircos; axes[1] = ydircos; axes[2] = zdircos; /* Calculate lengths of direction cosines, checking for zero lengths and parallel vectors. */ nparallel = 0; for (idim=0; idim < WORLD_NDIMS; idim++) { calculate_orthogonal_vector(axes[idim], axes[(idim+1)%WORLD_NDIMS], normal); lengths[idim] = 0.0; normal_length = 0.0; for (jdim=0; jdim < WORLD_NDIMS; jdim++) { lengths[idim] += axes[idim][jdim] * axes[idim][jdim]; normal_length += normal[jdim] * normal[jdim]; } lengths[idim] = sqrt(lengths[idim]); if (lengths[idim] == 0.0) { return 1; } if (normal_length == 0.0) { nparallel++; } } if (nparallel != 0) return 2; /* Loop over axes, calculating projections */ for (idim=0; idim < WORLD_NDIMS; idim++) { /* Calculate vector normal to other two axes by doing cross product */ calculate_orthogonal_vector(axes[(idim+1)%WORLD_NDIMS], axes[(idim+2)%WORLD_NDIMS], normal); /* Calculate dot product of origin with normal (numerator) and dot product of current axis with normal (denominator) */ denominator = numerator = 0.0; for (jdim=0; jdim < WORLD_NDIMS; jdim++) { numerator += origin[jdim] * normal[jdim]; denominator += axes[idim][jdim] * normal[jdim]; } /* Calculate parallel projection */ if (denominator != 0.0) start[idim] = lengths[idim] * numerator / denominator; else start[idim] = 0.0; } return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : calculate_orthogonal_vector @INPUT : vector1 - first vector vector2 - second vector @OUTPUT : result - vector orthogonal to vector1 and vector2 @RETURNS : (nothing) @DESCRIPTION: Routine to calculate a vector that is orthogonal to 2 other vectors by doing a cross product. The vectors are copied before the calculation so the result vector can be one of the input vectors. @METHOD : @GLOBALS : @CALLS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void calculate_orthogonal_vector(double vector1[], double vector2[], double result[]) { int idim; double v1[WORLD_NDIMS], v2[WORLD_NDIMS]; /* Make a copy of the vectors */ for (idim=0; idim < WORLD_NDIMS; idim++) { v1[idim] = vector1[idim]; v2[idim] = vector2[idim]; } for (idim=0; idim < WORLD_NDIMS; idim++) { result[idim] = v1[(idim+1)%WORLD_NDIMS] * v2[(idim+2)%WORLD_NDIMS] - v2[(idim+1)%WORLD_NDIMS] * v1[(idim+2)%WORLD_NDIMS]; } } minc-tools-2.3.00+dfsg/progs/Proglib/minc_endian.h0000644000175000000620000000057212574624760020773 0ustar stevestaff#define MINC_NATIVE_ENDIAN 0 /* Use CPU-native format */ #define MINC_BIG_ENDIAN 1 /* Force big-endian */ #define MINC_LITTLE_ENDIAN 2 /* Force little-endian */ typedef void (*minc_swap_fn_t)(char *, size_t); /** * Returns the "right" function pointer for swapping a data type. */ extern minc_swap_fn_t minc_get_swap_function(int desired_format, int type_size); minc-tools-2.3.00+dfsg/progs/Proglib/convert_origin_to_start.h0000644000175000000620000000314312574624760023472 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : convert_origin_to_start.h @DESCRIPTION: Header file for covnert_origin_to_start.c @METHOD : @GLOBALS : @CREATED : November 7, 1995 (Peter Neelin) @MODIFIED : * $Log: convert_origin_to_start.h,v $ * 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 1.1 1995/11/10 20:01:27 neelin * Initial revision * @COPYRIGHT : Copyright 1995 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. ---------------------------------------------------------------------------- */ int convert_origin_to_start(double origin[], double xdircos[], double ydircos[], double zdircos[], double start[]); minc-tools-2.3.00+dfsg/progs/Proglib/minc_endian.c0000644000175000000620000000426612574624760020772 0ustar stevestaff#include #include "minc_endian.h" /** * Returns either MINC_BIG_ENDIAN on a big-endian CPU, or MINC_LITTLE_ENDIAN * on a little-endian CPU. */ static int minc_cpu_endian() { union etest { short s; char b[sizeof(short)]; } x; x.s = 1; if (x.b[0] == 1 && x.b[sizeof(short) - 1] == 0) { return MINC_LITTLE_ENDIAN; } if (x.b[0] == 0 && x.b[sizeof(short) - 1] == 1) { return MINC_BIG_ENDIAN; } fprintf(stderr, "CPU is neither-endian??\n"); return MINC_NATIVE_ENDIAN; } /** * Function to swap 8-byte values in place. */ static void minc_swap_8bytes(char *p_data, size_t n_bytes) { char *p_last; char tmp; for (p_last = p_data + n_bytes; p_data < p_last; p_data += 8) { tmp = p_data[0]; p_data[0] = p_data[7]; p_data[7] = tmp; tmp = p_data[5]; p_data[5] = p_data[2]; p_data[2] = tmp; tmp = p_data[1]; p_data[1] = p_data[6]; p_data[6] = tmp; tmp = p_data[3]; p_data[3] = p_data[4]; p_data[4] = tmp; } } /** * Function to swap 4-byte values in place. */ static void minc_swap_4bytes(char *p_data, size_t n_bytes) { char *p_last; char tmp; for (p_last = p_data + n_bytes; p_data < p_last; p_data += 4) { tmp = p_data[0]; p_data[0] = p_data[3]; p_data[3] = tmp; tmp = p_data[2]; p_data[2] = p_data[1]; p_data[1] = tmp; } } /** * Function to swap 2-byte values in place. */ static void minc_swap_2bytes(char *p_data, size_t n_bytes) { char *p_last; char tmp; for (p_last = p_data + n_bytes; p_data < p_last; p_data += 2) { tmp = p_data[0]; p_data[0] = p_data[1]; p_data[1] = tmp; } } minc_swap_fn_t minc_get_swap_function(int desired_format, int type_size) { minc_swap_fn_t swap_fn = NULL; if (desired_format != MINC_NATIVE_ENDIAN && desired_format != minc_cpu_endian()) { switch (type_size) { case 8: swap_fn = minc_swap_8bytes; break; case 4: swap_fn = minc_swap_4bytes; break; case 2: swap_fn = minc_swap_2bytes; break; default: (void) fprintf(stderr, "I can't change the endianness of data of length %d\n", type_size); swap_fn = NULL; break; } } return swap_fn; } minc-tools-2.3.00+dfsg/progs/Proglib/vax_conversions.h0000644000175000000620000000361412574624760021755 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : vax_conversions.h @DESCRIPTION: Header file for vax type conversion routines @GLOBALS : @CALLS : @CREATED : January 8, 1993 @MODIFIED : * $Log: vax_conversions.h,v $ * Revision 6.2 1999-10-19 15:57:19 neelin * Fixed log message containing log substitution * * 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.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:32 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:34:21 neelin * Pre-release * * Revision 1.3 93/08/04 13:04:03 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. ---------------------------------------------------------------------------- */ void get_vax_short(int nvals, void *vax_value, short *mach_value); void get_vax_long(int nvals, void *vax_value, long *mach_value); void get_vax_float(int nvals, void *vax_value, float *mach_value); minc-tools-2.3.00+dfsg/progs/Proglib/vax_conversions.c0000644000175000000620000001253712574624760021754 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : vax_conversions.c @DESCRIPTION: File containing routines to convert machine values to vax format values. @METHOD : @CREATED : December 10, 1992 (Peter Neelin) @MODIFIED : * $Log: vax_conversions.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:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.2 1999/10/19 15:57:18 neelin * Fixed log message containing log substitution * * 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.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:32 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:34:18 neelin * Pre-release * * Revision 1.4 93/08/04 13:04:01 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. ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vax_short @INPUT : nvals - number of values to convert vax_value - pointer to array of shorts in vax format @OUTPUT : mach_value - pointer to array of shorts in current machine format @RETURNS : (nothing) @DESCRIPTION: Converts vax short integers to short integers in the format of the current machine. @METHOD : @GLOBALS : (none) @CALLS : memcpy @CREATED : December 10, 1992. @MODIFIED : ---------------------------------------------------------------------------- */ void get_vax_short(int nvals, void *vax_value, short *mach_value) { int i; char *ptr1, *ptr2, v0, v1; #ifdef vax memcpy((void *) mach_value, vax_value, nvals*sizeof(short)); #else ptr1 = (char *) vax_value; ptr2 = (char *) mach_value; for (i=0; i #include #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define VECTOR_SEPARATOR ',' #define TYPE_ASCII 0 #define TYPE_BYTE 1 #define TYPE_SHORT 2 #define TYPE_INT 3 #define TYPE_FLOAT 4 #define TYPE_DOUBLE 5 #define TYPE_FILE 6 static nc_type nc_type_list[8] = { NC_DOUBLE, NC_BYTE, NC_SHORT, NC_INT, NC_FLOAT, NC_DOUBLE, NC_DOUBLE }; /* Function declarations */ static int get_arg_vector(char *dst, char *key, char *nextArg); /* Variables used for argument parsing */ static int arg_odatatype = TYPE_ASCII; static nc_type output_datatype = NC_DOUBLE; static int output_signed = INT_MAX; static int output_endian = MINC_NATIVE_ENDIAN; static double valid_range[2] = {DBL_MAX, DBL_MAX}; static int normalize_output = TRUE; static double image_range[2] = {DBL_MAX, DBL_MAX}; static long hs_start[MAX_VAR_DIMS] = {LONG_MIN}; static long hs_count[MAX_VAR_DIMS] = {LONG_MIN}; static int xdirection = INT_MAX; static int ydirection = INT_MAX; static int zdirection = INT_MAX; static int default_direction = INT_MAX; /* Argument table */ ArgvInfo argTable[] = { {"-ascii", ARGV_CONSTANT, (char *) TYPE_ASCII, (char *) &arg_odatatype, "Write out data as ascii strings (default)"}, {"-byte", ARGV_CONSTANT, (char *) TYPE_BYTE, (char *) &arg_odatatype, "Write out data as bytes"}, {"-short", ARGV_CONSTANT, (char *) TYPE_SHORT, (char *) &arg_odatatype, "Write out data as short integers"}, {"-int", ARGV_CONSTANT, (char *) TYPE_INT, (char *) &arg_odatatype, "Write out data as 32-bit integers"}, {"-long", ARGV_CONSTANT, (char *) TYPE_INT, (char *) &arg_odatatype, "Superseded by -int"}, {"-float", ARGV_CONSTANT, (char *) TYPE_FLOAT, (char *) &arg_odatatype, "Write out data as single precision floating-point values"}, {"-double", ARGV_CONSTANT, (char *) TYPE_DOUBLE, (char *) &arg_odatatype, "Write out data as double precision floating-point values"}, {"-filetype", ARGV_CONSTANT, (char *) TYPE_FILE, (char *) &arg_odatatype, "Write out data in the type of the file"}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &output_signed, "Write out signed data"}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &output_signed, "Write out unsigned data"}, {"-range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Specify the range of output values"}, {"-normalize", ARGV_CONSTANT, (char *) TRUE, (char *) &normalize_output, "Normalize integer pixel values to file max and min (Default)"}, {"-nonormalize", ARGV_CONSTANT, (char *) FALSE, (char *) &normalize_output, "Turn off pixel normalization"}, {"-big-endian", ARGV_CONSTANT, (char *) MINC_BIG_ENDIAN, (char *) &output_endian, "Force big-endian output." }, {"-little-endian", ARGV_CONSTANT, (char *) MINC_LITTLE_ENDIAN, (char *) &output_endian, "Force little-endian output." }, {"-image_range", ARGV_FLOAT, (char *) 2, (char *) image_range, "Specify the range of real image values for normalization"}, {"-image_minimum", ARGV_FLOAT, (char *) 1, (char *) &image_range[0], "Specify the minimum real image value for normalization"}, {"-image_maximum", ARGV_FLOAT, (char *) 1, (char *) &image_range[1], "Specify the maximum real image value for normalization"}, {"-start", ARGV_FUNC, (char *) get_arg_vector, (char *) hs_start, "Specifies corner of hyperslab (C conventions for indices)"}, {"-count", ARGV_FUNC, (char *) get_arg_vector, (char *) hs_count, "Specifies edge lengths of hyperslab to read"}, {"-positive_direction", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &default_direction, "Flip images to always have positive direction."}, {"-negative_direction", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &default_direction, "Flip images to always have negative direction."}, {"-any_direction", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &default_direction, "Do not flip images (Default)."}, {"+xdirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &xdirection, "Flip images to give positive xspace:step value (left-to-right)."}, {"-xdirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &xdirection, "Flip images to give negative xspace:step value (right-to-left)."}, {"-xanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &xdirection, "Don't flip images along x-axis (default)."}, {"+ydirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &ydirection, "Flip images to give positive yspace:step value (post-to-ant)."}, {"-ydirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &ydirection, "Flip images to give negative yspace:step value (ant-to-post)."}, {"-yanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &ydirection, "Don't flip images along y-axis (default)."}, {"+zdirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &zdirection, "Flip images to give positive zspace:step value (inf-to-sup)."}, {"-zdirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &zdirection, "Flip images to give negative zspace:step value (sup-to-inf)."}, {"-zanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &zdirection, "Don't flip images along z-axis (default)."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Main program */ int main(int argc, char *argv[]) { char *filename; int mincid, imgid, icvid, ndims, dims[MAX_VAR_DIMS]; nc_type datatype; int is_signed; long start[MAX_VAR_DIMS], end[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS], cur[MAX_VAR_DIMS]; int element_size; int idim; int nstart, ncount; void *data; double temp; size_t nelements, ielement; double *dbl_data; int user_normalization; minc_swap_fn_t swap_fn = NULL; /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 2)) { (void) fprintf(stderr, "\nUsage: %s [] \n", argv[0]); (void) fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } filename = argv[1]; /* Set normalization if image_range specified */ user_normalization = FALSE; if ((image_range[0] != DBL_MAX) || (image_range[1] != DBL_MAX)) { user_normalization = TRUE; normalize_output = TRUE; } /* Check direction values */ if (default_direction == INT_MAX) default_direction = MI_ICV_ANYDIR; if (xdirection == INT_MAX) xdirection = default_direction; if (ydirection == INT_MAX) ydirection = default_direction; if (zdirection == INT_MAX) zdirection = default_direction; /* Open the file */ mincid = miopen(filename, NC_NOWRITE); /* Inquire about the image variable */ imgid = ncvarid(mincid, MIimage); (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dims, NULL); (void) miget_datatype(mincid, imgid, &datatype, &is_signed); /* Check if arguments set */ /* Check the start and count arguments */ for (nstart=0; (nstart valid_range[1]) { temp = valid_range[0]; valid_range[0] = valid_range[1]; valid_range[1] = temp; } /* Set up image conversion */ icvid = miicv_create(); (void) miicv_setint(icvid, MI_ICV_TYPE, output_datatype); (void) miicv_setstr(icvid, MI_ICV_SIGN, (output_signed ? MI_SIGNED : MI_UNSIGNED)); (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, valid_range[0]); (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, valid_range[1]); (void) miicv_setint(icvid, MI_ICV_DO_DIM_CONV, TRUE); (void) miicv_setint(icvid, MI_ICV_DO_SCALAR, FALSE); (void) miicv_setint(icvid, MI_ICV_XDIM_DIR, xdirection); (void) miicv_setint(icvid, MI_ICV_YDIM_DIR, ydirection); (void) miicv_setint(icvid, MI_ICV_ZDIM_DIR, zdirection); if ((output_datatype == NC_FLOAT) || (output_datatype == NC_DOUBLE)) { (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); } else if (normalize_output) { (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); if (user_normalization) { (void) miicv_attach(icvid, mincid, imgid); if (image_range[0] == DBL_MAX) { (void) miicv_inqdbl(icvid, MI_ICV_NORM_MIN, &image_range[0]); } if (image_range[1] == DBL_MAX) { (void) miicv_inqdbl(icvid, MI_ICV_NORM_MAX, &image_range[1]); } (void) miicv_detach(icvid); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MIN, image_range[0]); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MAX, image_range[1]); } } (void) miicv_attach(icvid, mincid, imgid); /* Set input file start, count and end vectors for reading a slice at a time */ nelements = 1; for (idim=0; idim < ndims; idim++) { /* Get start */ start[idim] = (nstart == 0) ? 0 : hs_start[idim]; cur[idim] = start[idim]; /* Get end */ if (ncount!=0) end[idim] = start[idim]+hs_count[idim]; else if (nstart!=0) end[idim] = start[idim]+1; else (void) ncdiminq(mincid, dims[idim], NULL, &end[idim]); /* Compare start and end */ if (start[idim] >= end[idim]) { (void) fprintf(stderr, "start or count out of range\n"); exit(EXIT_FAILURE); } /* Get count and nelements */ if (idim < ndims-2) count[idim] = 1; else count[idim] = end[idim] - start[idim]; nelements *= count[idim]; } element_size = nctypelen(output_datatype); /* Allocate space */ data = malloc(element_size*nelements); swap_fn = minc_get_swap_function(output_endian, element_size); /* Loop over input slices */ while (cur[0] < end[0]) { /* Read in the slice */ (void) miicv_get(icvid, cur, count, data); /* Write out the slice */ if (arg_odatatype == TYPE_ASCII) { dbl_data = data; for (ielement=0; ielement0) && (cur[idim] >= end[idim])) { cur[idim] = start[idim]; idim--; cur[idim] += count[idim]; } } /* End loop over slices */ /* Clean up */ (void) miclose(mincid); (void) miicv_free(icvid); free(data); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_arg_vector @INPUT : key - argv key string (-start, -count) nextArg - string from which vector should be read @OUTPUT : dst - pointer to vector of longs into which values should be written (padded with LONG_MIN) @RETURNS : TRUE, since nextArg is used (unless it is NULL) @DESCRIPTION: Parses a command-line argument into a vector of longs. The string should contain at most MAX_VAR_DIMS comma separated integer values (spaces are skipped). @METHOD : @GLOBALS : @CALLS : @CREATED : June 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_arg_vector(char *dst, char *key, char *nextArg) /* ARGSUSED */ { long *vector; int nvals, i; char *cur, *end, *prev; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); return FALSE; } /* Get pointer to vector of longs */ vector = (long *) dst; /* Set up pointers to end of string and first non-space character */ end = nextArg + strlen(nextArg); cur = nextArg; while (isspace(*cur)) cur++; nvals = 0; /* Loop through string looking for integers */ while ((nvals < MAX_VAR_DIMS) && (cur!=end)) { /* Get integer */ prev = cur; vector[nvals] = ParseLong(prev, &cur); if (cur == prev) { (void) fprintf(stderr, "expected vector of integers for \"%s\", but got \"%s\"\n", key, nextArg); exit(EXIT_FAILURE); } nvals++; /* Skip any spaces */ while (isspace(*cur)) cur++; /* Skip an optional comma */ if (*cur == VECTOR_SEPARATOR) cur++; } /* Pad with LONG_MIN */ for (i=nvals; i < MAX_VAR_DIMS; i++) { vector[i] = LONG_MIN; } return TRUE; } minc-tools-2.3.00+dfsg/progs/mincedit/0002755000175000000620000000000012574624760016552 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincedit/mincedit.man10000644000175000000620000000137512574624760021130 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCEDIT 1 "$Date: 2008-01-09 14:42:24 $" "" "MINC User's Guide" .SH NAME mincedit \- edit a MINC file header .SH SYNOPSIS .B mincedit .BI mincfile .BI [editor] .SH DESCRIPTION \fBmincedit\fR converts the MINC file header to a text version which you can edit in your favourite editor. If any changes are made, the MINC file is regenerated and the image data is copied. The header is presented in netCDF's common data form language (CDL). .TP \fB\-help\fR Print summary of command-line options and exit. .SH AUTHOR Peter Neelin and Andrew Janke .SH COPYRIGHT Copyright \(co 1993 by Peter Neelin rewrite 2008 Andrew Janke - a.janke@gmail.com .SH "SEE ALSO" .IR mincdump (1), mincgen (1) minc-tools-2.3.00+dfsg/progs/mincedit/mincedit0000755000175000000620000001155712574624760020303 0ustar stevestaff#! /bin/sh # # Script to allow editing of minc files # # Usage: mincedit [] # # Modifications: # $Log: mincedit,v $ # Revision 6.9 2008-06-18 03:55:25 stever # Remove illegitimate escape sequences (\n) from echo statements. # See Debian bug 486048 for details. # # Revision 6.8 2008/02/05 13:48:45 rotor # * added code for checking VISUAL and EDITOR before falling back to emacs # # Revision 6.7 2008/01/16 00:45:34 rotor # * replaced mincdump -h with mincheader to preserver image ranges # # Revision 6.6 2008/01/09 14:42:24 rotor # * tidied up the man page and removed problematic errexit option # # Revision 6.5 2008/01/09 12:39:46 rotor # * added errexit option # # Revision 6.4 2008/01/09 12:31:08 rotor # * complete rewrite in sh # # Revision 6.3 2004/06/16 16:27:27 bert # Use mincgen instead of ncgen # # Revision 6.2 2000/09/12 15:43:37 neelin # Changed default TMPDIR to look for /var/tmp, /usr/tmp, /tmp. # # Revision 6.1 1999/10/19 14:45:21 neelin # Fixed Log subsitutions for CVS # # Revision 6.0 1997/09/12 13:23:34 neelin # Release of minc version 0.6 # # Revision 5.0 1997/08/21 13:24:35 neelin # Release of minc version 0.5 # # Revision 4.0 1997/05/07 20:00:36 neelin # Release of minc version 0.4 # # Revision 3.0 1995/05/15 19:31:09 neelin # Release of minc version 0.3 # # Revision 2.0 1994/09/28 10:33:58 neelin # Release of minc version 0.2 # # Revision 1.6 94/09/28 10:33:55 neelin # Pre-release # # Revision 1.5 93/08/25 11:24:48 neelin # Added checking for -h or -help options. # # Revision 1.4 93/08/11 15:19:09 neelin # Added RCS logging in source. # # # # Copyright 2008 Andrew Janke # Based upon original version of Peter Neelins (1993) # # 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. # simple little function to emulate perl's die(); die () { echo >&2 $@ echo "" exit 1 } me=`basename $0` usage="Usage: $me []" # create tmpdir tmpdir=${TMPDIR:-/tmp}/mincedit.$$ trap 'rm -rf "$tmpdir"' 0 trap ' exit ' 0 1 2 3 15 (umask 077 && mkdir $tmpdir) || { echo "$me: Could not create temporary directory! Exiting." 1>&2 exit 1 } # check arguments case $# in 1) if [ "$1" = "-help" -o "$1" = "-h" -o "$1" = "" ] then echo $usage exit 0 fi # if no editor is specified, find a suitable one if [ -n "$VISUAL" ] then editor=${VISUAL} else if [ -n "$EDITOR" ] then editor=${EDITOR} else editor="emacs" fi fi ;; 2) editor="$2" ;; *) echo $usage exit 0 esac # only allow one input file and check for it [ $# -eq 1 -o $# -eq 2 ] || die $usage [ -f $1 ] || die "$me: no such file $1" # set up the file names infile="$1" backup_infile=`echo ${infile} | sed -e "s/\.mnc/\.bu-$$\.mnc/"` cdl_prefix=mincedit-$$ cdl_orig=${tmpdir}/${cdl_prefix}-orig.cdl cdl_edit=${tmpdir}/${cdl_prefix}-edit.cdl # dump the file mincheader $infile > $cdl_orig status=$? if [ $status -ne 0 ] then echo "${0}: Error reading file '$infile'" exit 1 fi # make a copy to fiddle with cp $cdl_orig $cdl_edit # loop until successful file generation do_edit=1 while [ $do_edit -ne 0 ] do # edit the cdl file $editor $cdl_edit status=$? if [ $status -ne 0 ] then echo "$me: Error editing with editor '$editor'" exit 1 fi do_edit=0 # compare the files for difference diff $cdl_orig $cdl_edit > /dev/null 2>&1 status=$? if [ $status -eq 0 ] then echo "$me: File $infile not modified" exit 0 fi # rename the original file and generate a new one mv "$infile" "$backup_infile" status=$? mincgen -o $infile $cdl_edit status=$? if [ $status -ne 0 ] then mv $backup_infile $infile echo -n "$me: Error generating new file. Redit the file (y/n)? (def=n):" read answer case "$answer" in y*) do_edit=1 continue ;; esac exit 0 fi # copy the data back in minccopy $backup_infile $infile status=$? if [ $status -ne 0 ] then mv $backup_infile $infile echo -n "Error copying image data. Redit the file (y/n)? (def=n):" read answer case "$answer" in y*) do_edit=1 continue ;; esac exit 0 fi done minc-tools-2.3.00+dfsg/progs/mincreshape/0002755000175000000620000000000012574624760017254 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincreshape/mincreshape.c0000644000175000000620000020135512574624760021722 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincreshape @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Program to allow reshaping of minc lattices: selecting a a subrange (or superrange) of dimension indices, eliminating dimensions, or re-ordering axes. As well, all icv conversions are made accessible on the command line. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1994 (Peter Neelin) @MODIFIED : * $Log: mincreshape.c,v $ * Revision 6.14 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.13 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.12 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.11 2005/08/26 21:07:18 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.10 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.9 2004/04/27 15:30:02 bert * Add -2 flag * * Revision 6.8 2001/11/13 14:16:50 neelin * Get correct valid range for conversion from int to float types * * Revision 6.7 2001/09/18 15:32:53 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:36 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/04/24 13:38:45 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.4 2001/04/17 18:40:24 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.3 1999/10/19 14:45:29 neelin * Fixed Log subsitutions for CVS * * Revision 6.2 1998/08/19 13:57:23 neelin * Fixed argument parsing to detect errors such as octal numbers * containing illegal digits (08) and extraneous characters on the end of * arguments. * * Revision 6.0 1997/09/12 13:24:12 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:10 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:44 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:32:36 neelin * Release of minc version 0.3 * * Revision 1.6 1995/02/09 14:08:24 neelin * Mods to make irix 5 lint happy. * * Revision 1.5 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 1.4 1994/11/23 11:47:05 neelin * Handle image-min/max properly when using icv for normalization. * * Revision 1.3 94/11/22 08:46:09 neelin * Fixed handling of normalization for number of image dimensions > 2. * Added appropriate default values of image-max and image-min. * * Revision 1.2 94/11/03 08:48:20 neelin * Allow chunk_count to have sizes less than full block size. * * Revision 1.1 94/11/02 16:21:24 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "mincreshape.h" static void get_arginfo(int argc, char *argv[], Reshape_info *reshape_info); static int get_fillvalue(char *dst, char *key, char *nextArg); static int get_dimsize(char *dst, char *key, char *nextArg); static int get_axis_order(char *dst, char *key, char *nextArg); static int get_axis_range(char *dst, char *key, char *nextArg); static int get_arg_vector(char *dst, char *key, char *nextArg); static void get_default_datatype(int mincid, nc_type *datatype, int *is_signed, double valid_range[2]); static void setup_dim_sizes(int icvid, int mincid, Dimsize_list *dimsize_list); static void setup_reshaping_info(int icvid, int mincid, int do_norm, double fillvalue, int do_scalar, char *axis_order[], Axis_ranges *axis_ranges, long hs_start[], long hs_count[], int max_chunk_size_in_kb, Reshape_info *reshape_info); static void setup_output_file(int mincid, char *history, Reshape_info *reshape_info); static void create_dim_var(int outmincid, int outdimid, int inicvid, int cur_image_dim, int inmincid, long input_start, long input_count); static void copy_dimension_values(int outmincid, int outdimid, int inmincid, long input_start, long input_count); static void copy_dim_var_values(int outmincid, char *dimname, char *varname, int inmincid, long input_start, long input_count); /* Main program */ int main(int argc, char *argv[]) { Reshape_info reshape_info; /* Get argument information and create the output file */ get_arginfo(argc, argv, &reshape_info); /* Copy the data */ copy_data(&reshape_info); /* Close the output file */ (void) miattputstr(reshape_info.outmincid, reshape_info.outimgid, MIcomplete, MI_TRUE); (void) miclose(reshape_info.outmincid); (void) miclose(reshape_info.inmincid); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_arginfo @INPUT : argc - number of command-line arguments argv - command-line arguments @OUTPUT : reshape_info - information for reshaping file @RETURNS : (nothing) @DESCRIPTION: Routine to get information from arguments about input and output files and reshaping. @METHOD : @GLOBALS : @CALLS : @CREATED : March 11, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_arginfo(int argc, char *argv[], Reshape_info *reshape_info) { /* Argument variables */ static int clobber = FALSE; static int verbose = TRUE; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = INT_MIN; static double valid_range[2] = {DBL_MAX,DBL_MAX}; static double image_range[2] = {DBL_MAX,DBL_MAX}; static int do_norm = FALSE; static double pixfillvalue = FILL; static int do_scalar= FALSE; static int direction = MI_ICV_ANYDIR; static int xdirection = INT_MIN; static int ydirection = INT_MIN; static int zdirection = INT_MIN; static int keep_aspect = FALSE; static int image_size = MI_ICV_ANYSIZE; static int row_size = MI_ICV_ANYSIZE; static int col_size = MI_ICV_ANYSIZE; static Dimsize_list dimsize_list = {0, {NULL}, {0}}; static char *axis_order[MAX_VAR_DIMS+1]; static Axis_ranges axis_ranges = {0, {NULL}, {0}, {0}}; static long hs_start[MAX_VAR_DIMS] = {LONG_MIN}; static long hs_count[MAX_VAR_DIMS] = {LONG_MIN}; static double fillvalue = NOFILL; static int max_chunk_size_in_kb = DEFAULT_MAX_CHUNK_SIZE_IN_KB; #if MINC2 static int minc2_format = 0; #endif /* MINC2 */ int cflags; /* File creation flags */ /* Argument table */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "General options:"}, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Do not overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages as processing is being done (default).\n"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out any log messages."}, {"-max_chunk_size_in_kb", ARGV_INT, (char *) 0, (char *) &max_chunk_size_in_kb, "Specify the maximum size of the copy buffer (in kbytes)."}, #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *)&minc2_format, "Produce a MINC 2.0 format output file."}, #endif /* MINC2 */ {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Image conversion options (pixel type and range):"}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Don't do any type conversion (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Convert to byte data"}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Convert to short integer data"}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Convert to 32-bit integer data"}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int"}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Convert to single-precision floating-point data"}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Convert to double-precision floating-point data"}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Convert to signed integer data"}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Convert to unsigned integer data"}, {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data (pixel values)"}, {"-image_range", ARGV_FLOAT, (char *) 2, (char *) image_range, "Normalize images to a given minimum and maximum"}, {"-normalize", ARGV_CONSTANT, (char *) TRUE, (char *) &do_norm, "Normalize images to file minimum and maximum."}, {"-nonormalize", ARGV_CONSTANT, (char *) FALSE, (char *) &do_norm, "Do not normalize images (default)."}, {"-nopixfill", ARGV_FUNC, (char *) get_fillvalue, (char *) &pixfillvalue, "Do not convert out-of-range values in input file."}, {"-pixfill", ARGV_FUNC, (char *) get_fillvalue, (char *) &pixfillvalue, "Replace out-of-range values in input file by smallest value (default)."}, {"-pixfillvalue", ARGV_FLOAT, (char *) 0, (char *) &pixfillvalue, "Specify new value to replace out-of-range values in input file."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Image conversion options (dimension direction and size):"}, {"-scalar", ARGV_CONSTANT, (char *) TRUE, (char *) &do_scalar, "Convert vector images to scalar images."}, {"-noscalar", ARGV_CONSTANT, (char *) FALSE, (char *) &do_scalar, "Do not convert vector images to scalar images (default)."}, {"+direction", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &direction, "Flip images to give positive step value for spatial axes."}, {"-direction", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &direction, "Flip images to give negative step value for spatial axes."}, {"-anydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &direction, "Don't flip images along spatial axes (default)."}, {"+xdirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &xdirection, "Flip images to give positive xspace:step value (left-to-right)."}, {"-xdirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &xdirection, "Flip images to give negative xspace:step value (right-to-left)."}, {"-xanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &xdirection, "Don't flip images along x-axis."}, {"+ydirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &ydirection, "Flip images to give positive yspace:step value (post-to-ant)."}, {"-ydirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &ydirection, "Flip images to give negative yspace:step value (ant-to-post)."}, {"-yanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &ydirection, "Don't flip images along y-axis."}, {"+zdirection", ARGV_CONSTANT, (char *) MI_ICV_POSITIVE, (char *) &zdirection, "Flip images to give positive zspace:step value (inf-to-sup)."}, {"-zdirection", ARGV_CONSTANT, (char *) MI_ICV_NEGATIVE, (char *) &zdirection, "Flip images to give negative zspace:step value (sup-to-inf)."}, {"-zanydirection", ARGV_CONSTANT, (char *) MI_ICV_ANYDIR, (char *) &zdirection, "Don't flip images along z-axis."}, {"-keepaspect", ARGV_CONSTANT, (char *) TRUE, (char *) &keep_aspect, "Preserve aspect ratio when resizing images."}, {"-nokeepaspect", ARGV_CONSTANT, (char *) FALSE, (char *) &keep_aspect, "Do not preserve aspect ratio when resizing images (default)."}, {"-imgsize", ARGV_INT, (char *) 0, (char *) &image_size, "Specify the desired image size."}, {"-rowsize", ARGV_INT, (char *) 0, (char *) &row_size, "Specify the desired number of rows in the image."}, {"-colsize", ARGV_INT, (char *) 0, (char *) &col_size, "Specify the desired number of columns in the image."}, {"-dimsize", ARGV_FUNC, (char *) get_dimsize, (char *) &dimsize_list, "Specify the size of a named dimension (=)."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Reshaping options:"}, {"-transverse", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Write out transverse slices"}, {"-sagittal", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Write out sagittal slices"}, {"-coronal", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Write out coronal slices"}, {"-dimorder", ARGV_FUNC, (char *) get_axis_order, (char *) axis_order, "Specify dimension order (,,,...)."}, {"-dimrange", ARGV_FUNC, (char *) get_axis_range, (char *) &axis_ranges, "Specify range of dimension subscripts (=[,])."}, {"-start", ARGV_FUNC, (char *) get_arg_vector, (char *) hs_start, "Specifies corner of hyperslab (C conventions for indices)"}, {"-count", ARGV_FUNC, (char *) get_arg_vector, (char *) hs_count, "Specifies edge lengths of hyperslab to read"}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Missing data options:"}, {"-nofill", ARGV_FUNC, (char *) get_fillvalue, (char *) &fillvalue, "Use value zero for points outside of input volume (default)"}, {"-fill", ARGV_FUNC, (char *) get_fillvalue, (char *) &fillvalue, "Use a fill value for points outside of input volume"}, {"-fillvalue", ARGV_FLOAT, (char *) 0, (char *) &fillvalue, "Specify a fill value for points outside of input volume"}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Other variables */ char *infile, *outfile; char *history, *pname; int icvid; /* Get the history information and program name */ history = time_stamp(argc, argv); pname = argv[0]; /* Call ParseArgv */ if (ParseArgv(&argc, argv, argTable, 0) || (argc!=3)) { (void) fprintf(stderr, "\nUsage: %s [] \n", pname); (void) fprintf(stderr, " %s [-help]\n\n", pname); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* Save verbose setting */ reshape_info->verbose = verbose; /* Check max chunk size value */ if (max_chunk_size_in_kb <= 0) { (void) fprintf(stderr, "Illegal value for max_chunk_size (%d)\n", max_chunk_size_in_kb); exit(EXIT_FAILURE); } /* Check the x, y and z directions */ if (xdirection == INT_MIN) xdirection = direction; if (ydirection == INT_MIN) ydirection = direction; if (zdirection == INT_MIN) zdirection = direction; /* Check the row and column size */ if (row_size == MI_ICV_ANYSIZE) row_size = image_size; if (col_size == MI_ICV_ANYSIZE) col_size = image_size; /* Check for normalization to specified range */ if (image_range[0] != DBL_MAX) do_norm = TRUE; /* Open the input file */ reshape_info->inmincid = miopen(infile, NC_NOWRITE); /* Get the default datatype */ get_default_datatype(reshape_info->inmincid, &datatype, &is_signed, valid_range); reshape_info->output_datatype = datatype; reshape_info->output_is_signed = is_signed; /* Create the icv */ reshape_info->icvid = miicv_create(); icvid = reshape_info->icvid; /* Set the icv properties */ /* Set datatype properties (get min and max for type from icv) */ (void) miicv_setint(icvid, MI_ICV_TYPE, datatype); if (is_signed != INT_MIN) { if (is_signed) (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); else (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); } if (valid_range[0] != DBL_MAX) { (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, valid_range[0]); (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, valid_range[1]); } /* Check for normalization */ (void) miicv_setint(icvid, MI_ICV_DO_NORM, do_norm); (void) miicv_setint(icvid, MI_ICV_USER_NORM, (image_range[0] != DBL_MAX)); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MIN, image_range[0]); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MAX, image_range[1]); /* Check for pixel fill value conversion */ (void) miicv_setint(icvid, MI_ICV_DO_FILLVALUE, (pixfillvalue != NOFILL)); (void) miicv_setdbl(icvid, MI_ICV_FILLVALUE, pixfillvalue); /* Set up for dimension conversion */ (void) miicv_setint(icvid, MI_ICV_DO_DIM_CONV, TRUE); (void) miicv_setint(icvid, MI_ICV_DO_SCALAR, do_scalar); (void) miicv_setint(icvid, MI_ICV_XDIM_DIR, xdirection); (void) miicv_setint(icvid, MI_ICV_YDIM_DIR, ydirection); (void) miicv_setint(icvid, MI_ICV_ZDIM_DIR, zdirection); /* Set up for image resizing */ (void) miicv_setint(icvid, MI_ICV_KEEP_ASPECT, keep_aspect); (void) miicv_setint(icvid, MI_ICV_ADIM_SIZE, col_size); (void) miicv_setint(icvid, MI_ICV_BDIM_SIZE, row_size); setup_dim_sizes(icvid, reshape_info->inmincid, &dimsize_list); /* Save reshaping information */ setup_reshaping_info(icvid, reshape_info->inmincid, do_norm, fillvalue, do_scalar, axis_order, &axis_ranges, hs_start, hs_count, max_chunk_size_in_kb, reshape_info); /* Attach the icv */ (void) miicv_attach(icvid, reshape_info->inmincid, ncvarid(reshape_info->inmincid, MIimage)); /* Create the output file */ if (clobber) { cflags = NC_CLOBBER; } else { cflags = NC_NOCLOBBER; } #if MINC2 if (minc2_format) { cflags |= MI2_CREATE_V2; } #endif /* MINC2 */ reshape_info->outmincid = micreate(outfile, cflags); setup_output_file(reshape_info->outmincid, history, reshape_info); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_fillvalue @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : FALSE so that ParseArgv will not discard nextArg @DESCRIPTION: Routine called by ParseArgv to set the fill value @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_fillvalue(char *dst, char *key, char *nextArg) /* ARGSUSED */ { double *dptr; /* Get pointer to client data */ dptr = (double *) dst; /* Check key for fill value to set */ if ((strcmp(key, "-fill") == 0) || (strcmp(key, "-pixfill") == 0)) { *dptr = FILL; } else if ((strcmp(key, "-nofill") == 0) || (strcmp(key, "-nopixfill") == 0)) { *dptr = NOFILL; } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dimsize @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE so that ParseArgv will discard nextArg @DESCRIPTION: Routine called by ParseArgv to get a dimension size @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_dimsize(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Dimsize_list *dimsize_list; char *size_string; int ientry; char *cur; /* Get pointer to client data */ dimsize_list = (Dimsize_list *) dst; /* Check for next argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Check that we have enough space in the list */ if (dimsize_list->nentries >= MAX_VAR_DIMS) { (void) fprintf(stderr, "Too many \"%s\" options.\n", key); exit(EXIT_FAILURE); } ientry = dimsize_list->nentries; /* Parse the argument (=) */ /* Remove leading space */ while (ISSPACE(*nextArg)) nextArg++; dimsize_list->name[ientry] = nextArg; /* Find the '=' */ size_string = strchr(nextArg, '='); if ((size_string == NULL) || (size_string == nextArg)) { (void) fprintf(stderr, "\"%s\" option requires the argument =\n", key); exit(EXIT_FAILURE); } /* Remove trailing blanks on name */ cur = size_string - 1; while ((cur>=nextArg) && ISSPACE(*cur)) cur--; cur++; *cur = '\0'; /* Get the size */ size_string++; dimsize_list->size[ientry] = ParseLong(size_string, &cur); if (cur == size_string) { (void) fprintf(stderr, "\"%s\" option requires the argument =\n", key); exit(EXIT_FAILURE); } /* Check for extra stuff (spaces are allowed) */ while (ISSPACE(*cur)) cur++; if (*cur != '\0') { (void) fprintf(stderr, "\"%s\" option requires the argument =\n", key); exit(EXIT_FAILURE); } dimsize_list->nentries++; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_axis_order @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE or FALSE (so that ParseArgv will discard nextArg only when needed) @DESCRIPTION: Routine called by ParseArgv to set the axis order @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_axis_order(char *dst, char *key, char *nextArg) /* ARGSUSED */ { char **axis_order; char *cur; int ndims; /* Get pointer to client data */ axis_order = (char **) dst; /* Check key */ if (strcmp(key, "-transverse") == 0) { axis_order[0] = MIzspace; axis_order[1] = MIyspace; axis_order[2] = MIxspace; return FALSE; } if (strcmp(key, "-sagittal") == 0) { axis_order[0] = MIxspace; axis_order[1] = MIzspace; axis_order[2] = MIyspace; return FALSE; } if (strcmp(key, "-coronal") == 0) { axis_order[0] = MIyspace; axis_order[1] = MIzspace; axis_order[2] = MIxspace; return FALSE; } /* Make sure that we have a "-dimorder" argument */ if (strcmp(key, "-dimorder") != 0) { (void) fprintf(stderr, "Unrecognized option \"%s\": internal program error.\n", key); exit(EXIT_FAILURE); } /* Check for next argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Set up pointers to end of string and first non-space character */ cur = nextArg; while (ISSPACE(*cur)) cur++; ndims = 0; /* Loop through string looking for space or comma-separated names */ while ((ndims < MAX_VAR_DIMS) && (*cur!='\0')) { /* Get string */ axis_order[ndims] = cur; /* Search for end of dimension name */ while (!ISSPACE(*cur) && (*cur != ARG_SEPARATOR) && (*cur != '\0')) cur++; if (*cur != '\0') { *cur = '\0'; cur++; } ndims++; /* Skip any spaces */ while (ISSPACE(*cur)) cur++; } /* Terminate list with NULL */ axis_order[ndims] = NULL; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_axis_range @INPUT : dst - Pointer to client data from argument table key - argument key nextArg - argument following key @OUTPUT : (nothing) @RETURNS : TRUE so that ParseArgv will discard nextArg @DESCRIPTION: Routine called by ParseArgv to set the axis range @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_axis_range(char *dst, char *key, char *nextArg) /* ARGSUSED */ { Axis_ranges *axis_ranges; int ientry; char *num_string, *cur; /* Get pointer to client data */ axis_ranges = (Axis_ranges *) dst; /* Check for next argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Check that we have enough space in the list */ if (axis_ranges->nentries >= MAX_VAR_DIMS) { (void) fprintf(stderr, "Too many \"%s\" options.\n", key); exit(EXIT_FAILURE); } ientry = axis_ranges->nentries; /* Parse the argument (=,[]) */ /* Remove leading space */ while (ISSPACE(*nextArg)) nextArg++; axis_ranges->name[ientry] = nextArg; /* Find the '=' */ num_string = strchr(nextArg, '='); if ((num_string == NULL) || (num_string == nextArg)) { (void) fprintf(stderr, "\"%s\" option requires the argument =[,]\n", key); exit(EXIT_FAILURE); } /* Remove trailing blanks on name */ cur = num_string - 1; while ((cur>=nextArg) && ISSPACE(*cur)) cur--; cur++; *cur = '\0'; /* Get the start */ num_string++; axis_ranges->start[ientry] = ParseLong(num_string, &cur); if ((cur == num_string) || !(ISSPACE(*cur) || (*cur == ARG_SEPARATOR) || (*cur == '\0'))) { (void) fprintf(stderr, "\"%s\" option requires the argument =[,]\n", key); exit(EXIT_FAILURE); } /* Skip any spaces */ while (ISSPACE(*cur)) cur++; /* Skip an optional comma */ if (*cur == ARG_SEPARATOR) cur++; /* Look for a count string */ num_string = cur; axis_ranges->count[ientry] = ParseLong(num_string, &cur); if ((cur == num_string) || (axis_ranges->count[ientry] == 0)) { axis_ranges->count[ientry] = 0; } axis_ranges->nentries++; /* Check for extra stuff (spaces are allowed) */ while (ISSPACE(*cur)) cur++; if (*cur != '\0') { (void) fprintf(stderr, "\"%s\" option requires the argument =[,]\n", key); exit(EXIT_FAILURE); } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_arg_vector @INPUT : key - argv key string (-start, -count) nextArg - string from which vector should be read @OUTPUT : dst - pointer to vector of longs into which values should be written (padded with LONG_MIN) @RETURNS : TRUE, since nextArg is used (unless it is NULL) @DESCRIPTION: Parses a command-line argument into a vector of longs. The string should contain at most MAX_VAR_DIMS comma separated integer values (spaces are skipped). @METHOD : @GLOBALS : @CALLS : @CREATED : June 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_arg_vector(char *dst, char *key, char *nextArg) /* ARGSUSED */ { long *vector; int nvals, i; char *cur, *end, *prev; /* Check for following argument */ if (nextArg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); return FALSE; } /* Get pointer to vector of longs */ vector = (long *) dst; /* Set up pointers to end of string and first non-space character */ end = nextArg + strlen(nextArg); cur = nextArg; while (ISSPACE(*cur)) cur++; nvals = 0; /* Loop through string looking for integers */ while ((nvals < MAX_VAR_DIMS) && (cur!=end)) { /* Get integer */ prev = cur; vector[nvals] = ParseLong(prev, &cur); if ((cur == prev) || !(ISSPACE(*cur) || (*cur == VECTOR_SEPARATOR) || (*cur == '\0'))) { (void) fprintf(stderr, "expected vector of integers for \"%s\", but got \"%s\"\n", key, nextArg); exit(EXIT_FAILURE); } nvals++; /* Skip any spaces */ while (ISSPACE(*cur)) cur++; /* Skip an optional comma */ if (*cur == VECTOR_SEPARATOR) cur++; } /* Pad with LONG_MIN */ for (i=nvals; i < MAX_VAR_DIMS; i++) { vector[i] = LONG_MIN; } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_datatype @INPUT : mincid - id of input minc file @OUTPUT : datatype - datatype of file is_signed - TRUE if data is signed, FALSE if not. Defaults to FALSE for byte, TRUE otherwise. valid_range - DBL_MAX if not known @RETURNS : (nothing) @DESCRIPTION: Routine to get the datatype info from a file. If datatype is not MI_ORIGINAL_TYPE, then is_signed only is set to its default. Otherwise, is_signed is only modified if it is set to INT_MIN and valid_range is only modified if it is set to DBL_MAX. @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_default_datatype(int mincid, nc_type *datatype, int *is_signed, double valid_range[2]) { int imgid; int file_is_signed; nc_type file_datatype; int found_range, file_integer_type, integer_type; /* Get the image variable id */ imgid = ncvarid(mincid, MIimage); /* Get the file data type */ (void) miget_datatype(mincid, imgid, &file_datatype, &file_is_signed); /* Get the type and sign. If a type is specified, then the sign should be the default for that type. If it is not specified, then use the default from the file. */ if (*datatype != MI_ORIGINAL_TYPE) { if (*is_signed == INT_MIN) { *is_signed = ((*datatype == NC_BYTE) ? FALSE : TRUE); } } else { *datatype = file_datatype; if (*is_signed == INT_MIN) { *is_signed = file_is_signed; } } /* Set the valid_range if needed. For integer types, just get the default for the type. For float, get the valid range of the input if it is also float and is specified, otherwise get the image range. */ if (valid_range[0] == DBL_MAX) { /* Test for integer types */ integer_type = (*datatype != NC_FLOAT && *datatype != NC_DOUBLE); file_integer_type = (file_datatype != NC_FLOAT && file_datatype != NC_DOUBLE); /* Integer type */ if (integer_type) { (void) miget_default_range(*datatype, *is_signed, valid_range); } /* Float type */ else { found_range = FALSE; /* Just get input valid_range for float input */ if (!file_integer_type) { (void) miget_valid_range(mincid, imgid, valid_range); if (file_datatype == NC_FLOAT) found_range = (valid_range[1] != FLT_MAX); else if (file_datatype == NC_DOUBLE) found_range = (valid_range[1] != DBL_MAX); } /* If not float input, or did not find valid_range, then get it from the image range (image-max/min) */ if (!found_range) { /* Get the range if the variables exist, otherwise use the default */ if (mivar_exists(mincid, MIimagemax) && mivar_exists(mincid, MIimagemin)) { (void) miget_image_range(mincid, valid_range); } else if (!file_integer_type) { (void) miget_default_range(*datatype, *is_signed, valid_range); } else { valid_range[0] = MI_DEFAULT_MIN; valid_range[1] = MI_DEFAULT_MAX; } } } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_dim_sizes @INPUT : mincid - id of input minc file dimsize_list - list of dimension names and sizes @OUTPUT : icvid - icvid to modify @RETURNS : (nothing) @DESCRIPTION: Routine to modify an icv so that the appropriate dimensions have given sizes @METHOD : @GLOBALS : @CALLS : @CREATED : May 18, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_dim_sizes(int icvid, int mincid, Dimsize_list *dimsize_list) { int ientry, idim; int imgid, dimid; int ndims; int dim[MAX_VAR_DIMS]; char dimname[MAX_NC_NAME]; int image_dim, n_image_dims; /* Get image dimension info */ imgid = ncvarid(mincid, MIimage); (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); if (ndims > 0) { (void) ncdiminq(mincid, dim[ndims-1], dimname, NULL); if (strcmp(dimname, MIvector_dimension) == 0) ndims--; } /* Get default number of image dimensions */ (void) miicv_inqint(icvid, MI_ICV_NUM_IMGDIMS, &n_image_dims); /* Loop through list of names, looking for dimensions */ for (ientry=0; ientry < dimsize_list->nentries; ientry++) { ncopts = 0; dimid = ncdimid(mincid, dimsize_list->name[ientry]); ncopts = NCOPTS_DEFAULT; for (idim=0; idim < ndims; idim++) { if (dim[idim] == dimid) break; } if (idim < ndims) { image_dim = ndims - idim - 1; (void) miicv_setint(icvid, MI_ICV_DIM_SIZE+image_dim, dimsize_list->size[ientry]); if (n_image_dims < image_dim+1) n_image_dims = image_dim+1; } else { (void) fprintf(stderr, "Unable to set size of dimension \"%s\"\n", dimsize_list->name[ientry]); exit(EXIT_FAILURE); } } /* Update number of image dimensions, if needed */ if (n_image_dims > 2) { (void) miicv_setint(icvid, MI_ICV_NUM_IMGDIMS, n_image_dims); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_reshaping_info @INPUT : mincid - id of input minc file do_norm - indicates if normalization is being done already through the icv fillvalue - value to use where there is no input value do_scalar - TRUE if vector image should be converted to scalar axis_order - order of dimensions by name axis_ranges - range of subscripts for each axis hs_start - starting coordinate of hyperslab to read hs_count - edge lengths of hyperslab to read max_chunk_size_in_kb - maximum size of copy buffer in kbytes. @OUTPUT : reshape_info - information describing the reshaping @RETURNS : (nothing) @DESCRIPTION: Routine to set up reshaping information. @METHOD : @GLOBALS : @CALLS : @CREATED : May 26, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_reshaping_info(int icvid, int mincid, int do_norm, double fillvalue, int do_scalar, char *axis_order[], Axis_ranges *axis_ranges, long hs_start[], long hs_count[], int max_chunk_size_in_kb, Reshape_info *reshape_info) { int input_ndims, input_dim[MAX_VAR_DIMS], order_dim[MAX_VAR_DIMS]; int output_ndims, norder; int idim, jdim, ientry, iloop, order_idim; int input_dim_used[MAX_VAR_DIMS]; char name[MAX_NC_NAME]; int *o2i, *i2o; nc_type datatype; int min_ndims, max_ndims, min_dim[MAX_VAR_DIMS], max_dim[MAX_VAR_DIMS]; int minid, maxid, dimid; long total_size, size; int nstart, ncount; int has_vector_dimension; int num_imgdims; int fastest_input_img_dim, fastest_output_img_dim; long length; long first, last; /* Get input file dimension info */ (void) ncvarinq(mincid, ncvarid(mincid, MIimage), NULL, NULL, &input_ndims, input_dim, NULL); (void) ncdiminq(mincid, input_dim[input_ndims-1], name, NULL); has_vector_dimension = (strcmp(name, MIvector_dimension) == 0); fastest_input_img_dim = (has_vector_dimension ? input_ndims-2 : input_ndims-1); if (do_scalar && has_vector_dimension) input_ndims--; reshape_info->input_ndims = input_ndims; /* Check length of hs_start and hs_count vectors */ for (nstart=0; (nstart input_ndims) || (nstart > input_ndims)) { (void) fprintf(stderr, "Start and/or count vectors are too long.\n"); exit(EXIT_FAILURE); } /* Get start and count from file info and from hs_start and hs_count */ (void) miicv_inqint(icvid, MI_ICV_NUM_IMGDIMS, &num_imgdims); for (idim=0; idim < input_ndims; idim++) { (void) ncdiminq(mincid, input_dim[idim], NULL, &reshape_info->input_size[idim]); if ((idim > fastest_input_img_dim-num_imgdims) && (idim <= fastest_input_img_dim)) { (void) miicv_inqlong(icvid, MI_ICV_DIM_SIZE+fastest_input_img_dim-idim, &length); if (length > 0) { reshape_info->input_size[idim] = length; } } if (idim < nstart) reshape_info->input_start[idim] = hs_start[idim]; else reshape_info->input_start[idim] = 0; if (idim < ncount) reshape_info->input_count[idim] = hs_count[idim]; else reshape_info->input_count[idim] = reshape_info->input_size[idim]; } /* Get input dimension start and count from axis_ranges variable */ for (ientry=0; ientry < axis_ranges->nentries; ientry++) { dimid = ncdimid(mincid, axis_ranges->name[ientry]); for (idim=0; idim < input_ndims; idim++) { if (dimid == input_dim[idim]) break; } if (idim >= input_ndims) { (void) fprintf(stderr, "Unknown image dimension \"%s\"\n", axis_ranges->name[ientry]); exit(EXIT_FAILURE); } reshape_info->input_start[idim] = axis_ranges->start[ientry]; reshape_info->input_count[idim] = axis_ranges->count[ientry]; } /* Check to see if we will need a fill value */ reshape_info->need_fillvalue = FALSE; for (idim=0; idim < input_ndims; idim++) { first = reshape_info->input_start[idim]; last = first; if (reshape_info->input_count[idim] > 0) last += reshape_info->input_count[idim] - 1; else if (reshape_info->input_count[idim] < 0) last += reshape_info->input_count[idim] + 1; if ((first < 0) || (first >= reshape_info->input_size[idim]) || (last < 0) || (last >= reshape_info->input_size[idim])) reshape_info->need_fillvalue = TRUE; } /* Get output dimensions in terms of input */ /* Add up number of output dimensions */ output_ndims = 0; for (idim=0; idim < input_ndims; idim++) { if (reshape_info->input_count[idim] != 0) output_ndims++; } reshape_info->output_ndims = output_ndims; /* Get dim ids for specified order */ for (norder=0; norder < MAX_VAR_DIMS+1; norder++) { if (axis_order[norder] == NULL) break; order_dim[norder] = ncdimid(mincid, axis_order[norder]); } if (norder > output_ndims) { for (idim=0; idim < output_ndims; idim++) { order_dim[idim] = order_dim[idim + norder - output_ndims]; } norder = output_ndims; } /* Keep track of input dims already used in output (dimensions that are disappearing are considered used) */ for (idim=0; idim < input_ndims; idim++) { input_dim_used[idim] = (reshape_info->input_count[idim] == 0); } /* Re-order dimensions */ for (idim=output_ndims-1; idim >= 0; idim--) { /* Output dim loop */ order_idim = idim - output_ndims + norder; for (jdim=input_ndims-1; jdim >= 0; jdim--) { /* Input dim loop */ /* For specified dimensions, look for corresponding input dim */ if (order_idim >= 0) { if (input_dim[jdim] == order_dim[order_idim]) break; } /* For remaining dims, take next available */ else { if (!input_dim_used[jdim]) break; } } /* Check for error */ if ((jdim < 0) || input_dim_used[jdim]) { if (order_idim >= 0) { (void) fprintf(stderr, "Cannot re-order dimension \"%s\" (not found, repeated or removed).\n", axis_order[order_idim]); } else { (void) fprintf(stderr, "Program error in re-ordering axes.\n"); } exit(EXIT_FAILURE); } /* Save dimension mapping */ input_dim_used[jdim] = TRUE; reshape_info->map_out_to_in[idim] = jdim; } /* Get mapping from input to output (-1 means no mapping) */ for (idim=0; idim < input_ndims; idim++) { reshape_info->map_in_to_out[idim] = -1; } for (idim=0; idim < output_ndims; idim++) { reshape_info->map_in_to_out[reshape_info->map_out_to_in[idim]] = idim; } /* Get fastest varying output image dimension (excluding vector dim) */ idim = input_dim[reshape_info->map_out_to_in[output_ndims-1]]; (void) ncdiminq(mincid, idim, name, NULL); fastest_output_img_dim = ((strcmp(name, MIvector_dimension) == 0) ? output_ndims-2 : output_ndims-1); /* Save dimensions used in blocks and chunks */ o2i = reshape_info->map_out_to_in; i2o = reshape_info->map_in_to_out; (void) miicv_inqint(reshape_info->icvid, MI_ICV_TYPE, (int *) &datatype); total_size = nctypelen(datatype); for (idim=0; idim < output_ndims; idim++) { reshape_info->dim_used_in_block[idim] = FALSE; reshape_info->chunk_count[idim] = 1; } for (iloop=0; iloop < 6; iloop++) { /* Go through possible dimensions in descending order of priority. We start with the fastest varying dimension, but allow for the possibility of vector dimensions in either volume (looping twice on the same dimension is not a problem). Note that idim refers to an output dimension. */ switch (iloop) { case 0: idim = output_ndims-1; break; case 1: idim = i2o[input_ndims-1]; break; case 2: idim = fastest_output_img_dim; break; case 3: idim = i2o[fastest_input_img_dim]; break; case 4: idim = fastest_output_img_dim-1; break; case 5: idim = i2o[fastest_input_img_dim-1]; break; default: idim = -1; } if (idim != -1) size = ABS(reshape_info->input_count[o2i[idim]]); else size = 0; if (size == 0) idim = -1; if ((idim != -1) && !reshape_info->dim_used_in_block[idim]) { reshape_info->dim_used_in_block[idim] = TRUE; if ((total_size * size) > (max_chunk_size_in_kb * 1024)) size = max_chunk_size_in_kb * 1024 / total_size; if (size < 1) size = 1; reshape_info->chunk_count[idim] = size; total_size *= size; } } /* Make sure that all input image dimensions are considered used in the block */ for (iloop=2; iloop < num_imgdims; iloop++) { idim = fastest_input_img_dim - iloop; if (idim >= 0) reshape_info->dim_used_in_block[i2o[idim]] = TRUE; } /* If we are doing icv normalization, then all dimensions are used in the block */ if (do_norm) { for (idim=0; idim < output_ndims; idim++) { reshape_info->dim_used_in_block[idim] = TRUE; } } /* Save fillvalue */ reshape_info->fillvalue = fillvalue; /* Are we doing normalization through the icv? */ reshape_info->do_icv_normalization = do_norm; /* Do we need to normalize to slices to a block min and max? */ if (do_norm) { reshape_info->do_block_normalization = FALSE; } else { reshape_info->do_block_normalization = FALSE; /* Loop through block dimensions and check if image-min/max varies on the dimension */ ncopts = 0; minid = ncvarid(mincid, MIimagemin); maxid = ncvarid(mincid, MIimagemax); ncopts = NCOPTS_DEFAULT; if ((minid != MI_ERROR) && (maxid != MI_ERROR)) { (void) ncvarinq(mincid, minid, NULL, NULL, &min_ndims, min_dim, NULL); (void) ncvarinq(mincid, maxid, NULL, NULL, &max_ndims, max_dim, NULL); for (idim=0; idim < input_ndims; idim++) { jdim = reshape_info->map_in_to_out[idim]; if ((jdim>=0) && reshape_info->dim_used_in_block[jdim]) { dimid = input_dim[idim]; for (jdim=0; jdim < min_ndims; jdim++) { if (min_dim[jdim] == dimid) { reshape_info->do_block_normalization = TRUE; } } for (jdim=0; jdim < max_ndims; jdim++) { if (max_dim[jdim] == dimid) { reshape_info->do_block_normalization = TRUE; } } } } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_output_file @INPUT : mincid - id of output minc file history - string to be added to history list reshape_info - information describing the reshaping @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set up the output file @METHOD : @GLOBALS : @CALLS : @CREATED : June 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_output_file(int mincid, char *history, Reshape_info *reshape_info) { int output_ndims, output_dim[MAX_VAR_DIMS]; int input_ndims, input_dim[MAX_VAR_DIMS]; int minmax_ndims, minmax_dim[MAX_VAR_DIMS]; int idim, odim, iloop; int varid, icvid, imgid, varid2; char dimname[MAX_NC_NAME]; long length; int excluded_vars[2*MAX_VAR_DIMS + 10]; int nexcluded; nc_type datatype; char signtype[MI_MAX_ATTSTR_LEN]; double valid_range[2]; char *string; int att_length; int has_vector_dimension; int fastest_img_dim; /* Get useful info */ output_ndims = reshape_info->output_ndims; (void) ncvarinq(reshape_info->inmincid, ncvarid(reshape_info->inmincid, MIimage), NULL, NULL, &input_ndims, input_dim, NULL); input_ndims = reshape_info->input_ndims; /* Check for vector dimension */ (void) ncdiminq(reshape_info->inmincid, input_dim[input_ndims-1], dimname, NULL); has_vector_dimension = (strcmp(dimname, MIvector_dimension) == 0); fastest_img_dim = (has_vector_dimension ? input_ndims-2 : input_ndims-1); /* Create image dimensions */ for (odim=0; odim < output_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; length = ABS(reshape_info->input_count[idim]); (void) ncdiminq(reshape_info->inmincid, input_dim[idim], dimname, NULL); output_dim[odim] = ncdimdef(mincid, dimname, length); } /* Copy all variables except dimensions and dimension widths */ ncopts = 0; nexcluded = 0; for (idim = 0; idim < input_ndims; idim++) { (void) ncdiminq(reshape_info->inmincid, input_dim[idim], dimname, NULL); if ((varid=ncvarid(reshape_info->inmincid, dimname)) != MI_ERROR) excluded_vars[nexcluded++] = varid; (void) strncat(dimname, DIM_WIDTH_SUFFIX, sizeof(dimname)-strlen(dimname)-1); if ((varid=ncvarid(reshape_info->inmincid, dimname)) != MI_ERROR) excluded_vars[nexcluded++] = varid; } if ((varid=ncvarid(reshape_info->inmincid, MIimage)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(reshape_info->inmincid, MIimagemax)) != MI_ERROR) excluded_vars[nexcluded++] = varid; if ((varid=ncvarid(reshape_info->inmincid, MIimagemin)) != MI_ERROR) excluded_vars[nexcluded++] = varid; (void) micopy_all_var_defs(reshape_info->inmincid, mincid, nexcluded, excluded_vars); ncopts = NCOPTS_DEFAULT; /* Create image dimension variables */ for (odim=0; odim < output_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; create_dim_var(mincid, output_dim[odim], reshape_info->icvid, fastest_img_dim - idim, reshape_info->inmincid, reshape_info->input_start[idim], reshape_info->input_count[idim]); } /* Get basic image variable info */ icvid = reshape_info->icvid; (void) miicv_inqint(icvid, MI_ICV_TYPE, (int *) &datatype); (void) miicv_inqstr(icvid, MI_ICV_SIGN, signtype); (void) miicv_inqdbl(icvid, MI_ICV_VALID_MIN, &valid_range[0]); (void) miicv_inqdbl(icvid, MI_ICV_VALID_MAX, &valid_range[1]); /* Set the valid range to include 0.0 for floating point if needed */ if (reshape_info->need_fillvalue && ((datatype == NC_FLOAT) || (datatype == NC_DOUBLE)) && (reshape_info->fillvalue == NOFILL)) { if (0.0 < valid_range[0]) valid_range[0] = 0.0; if (0.0 > valid_range[1]) valid_range[1] = 0.0; } /* Create the imagemax/min variables */ minmax_ndims = 0; for (odim=0; odim < output_ndims; odim++) { if (!reshape_info->dim_used_in_block[odim]) { minmax_dim[minmax_ndims++] = output_dim[odim]; } } for (iloop=0; iloop < 2; iloop++) { if (iloop == 0) string = MIimagemin; else string = MIimagemax; varid = micreate_std_variable(mincid, string, NC_DOUBLE, minmax_ndims, minmax_dim); ncopts = 0; varid2 = ncvarid(reshape_info->inmincid, string); ncopts = NCOPTS_DEFAULT; if (varid2 != MI_ERROR) (void) micopy_all_atts(reshape_info->inmincid, varid2, mincid, varid); } /* Create the image variable */ imgid = micreate_std_variable(mincid, MIimage, datatype, output_ndims, output_dim); reshape_info->outimgid = imgid; (void) micopy_all_atts(reshape_info->inmincid, ncvarid(reshape_info->inmincid, MIimage), mincid, imgid); (void) miattputstr(mincid, imgid, MIsigntype, signtype); (void) miset_valid_range(mincid, imgid, valid_range); (void) miattputstr(mincid, imgid, MIcomplete, MI_FALSE); /* Add history */ ncopts=0; if ((ncattinq(mincid, NC_GLOBAL, MIhistory, &datatype, &att_length) == MI_ERROR) || (datatype != NC_CHAR)) att_length = 0; att_length += strlen(history) + 1; string = malloc(att_length); string[0] = '\0'; (void) miattgetstr(mincid, NC_GLOBAL, MIhistory, att_length, string); ncopts = NCOPTS_DEFAULT; (void) strcat(string, history); (void) miattputstr(mincid, NC_GLOBAL, MIhistory, string); free(string); /* Get into data mode */ (void) ncsetfill(mincid, NC_NOFILL); (void) ncendef(mincid); /* Copy all the other data */ (void) micopy_all_var_values(reshape_info->inmincid, mincid, nexcluded, excluded_vars); /* Copy the dimension variable values, if needed */ for (odim=0; odim < output_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; copy_dimension_values(mincid, output_dim[odim], reshape_info->inmincid, reshape_info->input_start[idim], reshape_info->input_count[idim]); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_dim_var @INPUT : outmincid - id of output minc file outdimid - id of output dimension inicvid - id of input icv cur_image_dim - image dim number of current dimension in input icv (0 is fastest varying dimension) inmincid - id of input minc file input_start - start index of input dimension input_count - count for input dimension (may be negative) @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Creates a dimension variable and sets attributes properly. @METHOD : @GLOBALS : @CALLS : @CREATED : June 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void create_dim_var(int outmincid, int outdimid, int inicvid, int cur_image_dim, int inmincid, long input_start, long input_count) { int invarid, outvarid; int num_image_dims, var_ndims; int is_regular, step_found, start_found, changed_spacing; double dim_start, dim_step, icv_start, icv_step; char dimname[MAX_NC_NAME]; char spacing[MAX_NC_NAME]; /* Get the dimension name */ (void) ncdiminq(outmincid, outdimid, dimname, NULL); /* Get number of image dimensions */ (void) miicv_inqint(inicvid, MI_ICV_NUM_IMGDIMS, &num_image_dims); /* Get step and start and spacing for dimension */ dim_step = 1.0; dim_start = 0.0; is_regular = TRUE; changed_spacing = ((input_start != 0) || (input_count <= 0)); ncopts = 0; invarid = ncvarid(inmincid, dimname); if (invarid != MI_ERROR) { step_found = (miattget1(inmincid, invarid, MIstep, NC_DOUBLE, &dim_step) != MI_ERROR); start_found = (miattget1(inmincid, invarid, MIstart, NC_DOUBLE, &dim_start) != MI_ERROR); (void) ncvarinq(inmincid, invarid, NULL, NULL, &var_ndims, NULL, NULL); if (var_ndims > 0) { (void) strcpy(spacing, MI_IRREGULAR); (void) miattgetstr(inmincid, invarid, MIspacing, sizeof(spacing), spacing); is_regular = (strcmp(spacing, MI_REGULAR) == 0); } } ncopts = NCOPTS_DEFAULT; /* Is the sampling changed because of the icv? */ if ((cur_image_dim < num_image_dims) && (cur_image_dim >= 0)) { (void) miicv_inqdbl(inicvid, MI_ICV_DIM_STEP+cur_image_dim, &icv_step); (void) miicv_inqdbl(inicvid, MI_ICV_DIM_START+cur_image_dim, &icv_start); if ((icv_step != dim_step) || (icv_start != dim_start)) { dim_step = icv_step; dim_start = icv_start; is_regular = TRUE; changed_spacing = TRUE; } } /* If spacing is not changed and the input variable does not exist don't create the variable */ if (!changed_spacing && (invarid == MI_ERROR)) return; /* Calculate the new dim_start and dim_step (if needed) */ dim_start += input_start * dim_step; if (input_count < 0) dim_step = -dim_step; /* Create the variable */ var_ndims = (is_regular ? 0 : 1); ncopts = 0; outvarid = micreate_std_variable(outmincid, dimname, NC_DOUBLE, var_ndims, &outdimid); ncopts = NCOPTS_DEFAULT; if (outvarid == MI_ERROR) { outvarid = ncvardef(outmincid, dimname, NC_DOUBLE, var_ndims, &outdimid); } if (invarid != MI_ERROR) (void) micopy_all_atts(inmincid, invarid, outmincid, outvarid); if (is_regular || step_found) (void) miattputdbl(outmincid, outvarid, MIstep, dim_step); if (is_regular || start_found) (void) miattputdbl(outmincid, outvarid, MIstart, dim_start); /* Create width variable if needed */ ncopts = 0; (void) strncat(dimname, DIM_WIDTH_SUFFIX, sizeof(dimname)-strlen(dimname)-1); invarid = ncvarid(inmincid, dimname); if (invarid != MI_ERROR) { (void) ncvarinq(inmincid, invarid, NULL, NULL, &var_ndims, NULL, NULL); if (var_ndims > 0) var_ndims = 1; outvarid = micreate_std_variable(outmincid, dimname, NC_DOUBLE, var_ndims, &outdimid); if (outvarid == MI_ERROR) { outvarid = ncvardef(outmincid, dimname, NC_DOUBLE, var_ndims, &outdimid); } if (invarid != MI_ERROR) (void) micopy_all_atts(inmincid, invarid, outmincid, outvarid); } ncopts = NCOPTS_DEFAULT; } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_dimension_values @INPUT : outmincid - id of output minc file outdimid - id of output dimension inmincid - id of input minc file input_start - start index of input dimension input_count - length of input dimension @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Copies the data for a dimension variable. @METHOD : @GLOBALS : @CALLS : @CREATED : June 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void copy_dimension_values(int outmincid, int outdimid, int inmincid, long input_start, long input_count) { char dimname[MAX_NC_NAME]; char varname[MAX_NC_NAME]; /* Get the dimension name */ (void) ncdiminq(outmincid, outdimid, dimname, NULL); (void) strcpy(varname, dimname); /* Copy the dimension coorindates */ copy_dim_var_values(outmincid, dimname, varname, inmincid, input_start, input_count); /* Copy the dimension widths */ (void) strncat(varname, DIM_WIDTH_SUFFIX, sizeof(varname)-strlen(varname)-1); copy_dim_var_values(outmincid, dimname, varname, inmincid, input_start, input_count); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_dim_var_values @INPUT : outmincid - id of output minc file dimname - name of dimension varname - name of variable to copy inmincid - id of input minc file input_start - start index of input dimension input_count - length of input dimension @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Copies the data for a dimension variable. @METHOD : @GLOBALS : @CALLS : @CREATED : June 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void copy_dim_var_values(int outmincid, char *dimname, char *varname, int inmincid, long input_start, long input_count) { int invarid, outvarid; int in_ndims, in_dim[MAX_VAR_DIMS], out_ndims; char string[MAX_NC_NAME]; int good_data, is_width, flip_dimension; long output_index, input_index, index, input_length; double value, dim_width, dim_step, dim_start; /* Do we need to copy data? */ ncopts = 0; outvarid = ncvarid(outmincid, varname); ncopts = NCOPTS_DEFAULT; if (outvarid == MI_ERROR) return; (void) ncvarinq(outmincid, outvarid, NULL, NULL, &out_ndims, NULL, NULL); if (out_ndims != 1) return; /* Is this a width variable? */ index = strlen(varname) - strlen(DIM_WIDTH_SUFFIX); if (index < 0) is_width = FALSE; else is_width = (strcmp(&varname[index], DIM_WIDTH_SUFFIX)); /* Check if there is a valid dimension variable from which to copy */ ncopts = 0; invarid = ncvarid(inmincid, varname); ncopts = NCOPTS_DEFAULT; good_data = (invarid != MI_ERROR); if (good_data) { (void) ncvarinq(inmincid, invarid, NULL, NULL, &in_ndims, in_dim, NULL); good_data = (in_ndims == 1); } if (good_data) { (void) ncdiminq(inmincid, in_dim[0], string, &input_length); good_data = (strcmp(string, dimname) == 0); } /* Get data from input file for estimating unknown values */ if (is_width) { /* Get width for width variables */ dim_width = 0.0; ncopts = 0; (void) miattget1(inmincid, invarid, MIwidth, NC_DOUBLE, &dim_width); ncopts = NCOPTS_DEFAULT; } else { /* Get step and start for coordinate variables */ dim_step = 1.0; dim_start = 0.0; if (good_data) { input_index = 0; (void) mivarget1(inmincid, invarid, &input_index, NC_DOUBLE, NULL, &dim_start); input_index = input_length - 1; if (input_length <= 1) { ncopts = 0; (void) miattget1(inmincid, invarid, MIstep, NC_DOUBLE, &dim_step); ncopts = NCOPTS_DEFAULT; } else { (void) mivarget1(inmincid, invarid, &input_index, NC_DOUBLE, NULL, &value); dim_step = (value - dim_start) / ((double) input_length - 1); } } else { ncopts = 0; (void) miattget1(inmincid, invarid, MIstep, NC_DOUBLE, &dim_step); (void) miattget1(inmincid, invarid, MIstart, NC_DOUBLE, &dim_start); ncopts = NCOPTS_DEFAULT; } if (dim_step == 0.0) dim_step = 1.0; } /* Loop through output values */ flip_dimension = (input_count < 0); input_count = ABS(input_count); for (output_index=0; output_index < input_count; output_index++) { /* Get input value */ if (!flip_dimension) { input_index = input_start + output_index; } else { input_index = input_start - output_index; } if (good_data && (input_index >= 0) && (input_index < input_length)) { (void) mivarget1(inmincid, invarid, &input_index, NC_DOUBLE, NULL, &value); } else { if (is_width) { value = dim_width; } else { value = input_index * dim_step + dim_start; } } (void) mivarput1(outmincid, outvarid, &output_index, NC_DOUBLE, NULL, &value); } } minc-tools-2.3.00+dfsg/progs/mincreshape/mincreshape.h0000644000175000000620000001113212574624760021717 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincreshape.h @DESCRIPTION: Header file for mincreshape.c @METHOD : @GLOBALS : @CALLS : @CREATED : March 11, 1994 (Peter Neelin) @MODIFIED : * $Log: mincreshape.h,v $ * Revision 6.5 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.4 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.3 2001/12/06 14:12:45 neelin * Trivial change to definition of NCOPTS_DEFAULT. * * Revision 6.2 1999/10/19 14:45:29 neelin * Fixed Log subsitutions for CVS * * Revision 6.1 1998/08/19 13:57:50 neelin * Added ARG_SEPARATOR to replace string literal for comma. * * Revision 6.0 1997/09/12 13:24:12 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:10 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:44 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:32:36 neelin * Release of minc version 0.3 * * Revision 1.3 1994/12/02 09:08:57 neelin * Moved nd_loop to proglib. * * Revision 1.2 94/11/23 11:47:08 neelin * Handle image-min/max properly when using icv for normalization. * * Revision 1.1 94/11/02 16:22:00 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. ---------------------------------------------------------------------------- */ /* Constants used in program */ #define NOFILL DBL_MAX /* Fillvalue indicating -nofill */ #define FILL -DBL_MAX /* Fillvalue for -fill */ #define NCOPTS_DEFAULT (NC_VERBOSE | NC_FATAL) #define DEFAULT_MAX_CHUNK_SIZE_IN_KB (1024*4) #define DIM_WIDTH_SUFFIX "-width" #define ARG_SEPARATOR ',' #define VECTOR_SEPARATOR ',' #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Types used in program */ typedef struct { int verbose; int icvid, inmincid, outmincid, outimgid; nc_type output_datatype; int output_is_signed; int input_ndims; /* Number of input dimensions */ int output_ndims; /* Number of output dimensions */ long input_size[MAX_VAR_DIMS]; /* Size of input volume */ long input_start[MAX_VAR_DIMS]; /* Start of desired hyperslab */ long input_count[MAX_VAR_DIMS]; /* Size of desired hyperslab (<0 means 1 and remove dimension) */ int map_out_to_in[MAX_VAR_DIMS]; /* Map output dimension index to input */ int map_in_to_out[MAX_VAR_DIMS]; /* Map input dimension index to output (-1 means no mapping) */ int dim_used_in_block[MAX_VAR_DIMS]; /* TRUE if output dim used in block */ int chunk_count[MAX_VAR_DIMS]; /* Specifies count for chunk hyperslab */ int need_fillvalue; /* TRUE if we will need a fill value */ double fillvalue; /* Value to fill with (FILL_DEFAULT means fill with real value zero) */ int do_block_normalization; /* Normalize slices to block max/min */ int do_icv_normalization; /* Use icv for normalization */ /* Note that a block is a hyperslab of the output volume in which all values are normalized the same way. A chunk is a hyperslab that is copied in one piece (smaller than or equal to a block). */ } Reshape_info; typedef struct { int nentries; char *name[MAX_VAR_DIMS]; long size[MAX_VAR_DIMS]; } Dimsize_list; typedef struct { int nentries; char *name[MAX_VAR_DIMS]; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; } Axis_ranges; /* Macros used in program */ #define ISSPACE(ch) (isspace((int)ch)) #define ABS(x) (((x) >= 0) ? (x) : (-(x))) #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) /* Function prototypes */ extern void copy_data(Reshape_info *reshape_info); minc-tools-2.3.00+dfsg/progs/mincreshape/mincreshape.man10000644000175000000620000003166112574624760022335 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincreshape/mincreshape.man1,v 6.2 2004-05-20 21:52:09 bert Exp $ .\" .TH MINCRESHAPE 1 "$Date: 2004-05-20 21:52:09 $" "" "MINC User's Guide" .SH NAME mincreshape - cuts a hyperslab out of a minc file (with dimension re-ordering) .SH SYNOPSIS .B mincreshape [] .mnc .mnc .SH DESCRIPTION \fIMincreshape's\fR main job is to chop a hyperslab out of a minc file and put it into a new minc file. "What is a hyperslab?", you ask. It is simply a multi-dimensional box specified with a starting index (a vector giving a voxel coordinate) and a count vector (a number of voxels along each axis). A single slice out of a volume is a hyperslab (with a count of 1 in the slice direction), a small block pulled out of a large volume is a hyperslab, a single echo volume out of a multi-echo MRI dataset is a hyperslab, one time point out of a dynamic acquisition is a hyperslab - you get the idea. Check out the \fB-start\fR, \fB-count\fR and \fB-dimrange\fR options for more details on how to do this (and look at the examples!). If you are pulling out only one point along a dimension, you have the option of making the dimension disappear, so \fImincreshape\fR gives you the ability to reduce the dimensionality of a minc file. As well, you aren't constrained to specify a hyperslab that is only within the input file, you can extend beyond the bounds of the dimensions in the input file, and furthermore you can give a count that will flip the data along a dimension. As if all that is not enough, \fImincreshape\fR has the ability to re-order dimensions. The most obvious case is converting a transverse image into a coronal image. But you can type a list of dimension names to get an arbitrary order of dimensions. You want more!?! Okay, okay. \fIMincreshape\fR makes all of the minc library ICV operations available on the command line. For those who like things defined, an ICV is an image conversion variable (don't ask me why I called it that) which basically lets you tell the data what it's going to look like. In other words, it does a bunch of conversions for you. These conversions include changing type, range and normalization of the voxel values, expanding or contracting images (by voxel duplication or averaging) to give a specified image size, and converting vector images to scalar. Just so you don't get confused let me tell you clearly here: \fImincreshape\fR does all of the ICV conversions first and then the hyperslab and dimension re-ordering stuff is applied to the result of that. So if you want to mix them together (like \fB-imgsize\fR, \fB-start\fR, \fB-count\fR), get it clear in your head first. Okay, hold on to your seat: here's a list of options. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create MINC 2.0 format output files. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB-verbose\fR Print out progress information for each chunk of data copied (default). A chunk varies in size depending mostly on whether you're re-ordering dimensions or not and how big the internal buffer is allowed to be. .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-max_chunk_size_in_kb\fR\ \fIsize\fR Specify the maximum size of the copy buffer (in kbytes). Default is 4096 kbytes (4meg). .SH Image conversion options (pixel type and range): The default for type, sign and valid range is to use those of the input file. If type is specified, then both sign and valid range are set to the default for that type. If sign is specified, then valid range is set to the default for the type and sign. .TP \fB\-filetype\fR Don't do any type conversion (default). .TP \fB\-byte\fR Store output voxels in 8-bit integer format. .TP \fB\-short\fR Store output voxels in 16-bit integer format. .TP \fB\-int\fR Store output voxels in 32-bit integer format. .TP \fB\-long\fR Superseded by \fB\-int\fR. .TP \fB-float\fR Store output voxels in 32-bit floating point format. .TP \fB-double\fR Store output voxels in 64-bit floating point format. .TP \fB\-signed\fR Write out values as signed integers (default for short and long). Ignored for floating point types. .TP \fB\-unsigned\fR Write out values as unsigned integers (default for byte). Ignored for floating point types. .TP \fB\-valid_range\fR\ \fImin\ max\fR specifies the valid range of output voxel values in their integer representation. Default is the full range for the type and sign. This option is ignored for floating point values. .TP \fB\-image_range\fR\ \fImin\ max\fR Normalize images to a given minimum and maximum real value (not voxel value). .TP \fB\-normalize\fR Normalize images to real minimum and maximum for the entire input file. .TP \fB\-nonormalize\fR Do not normalize images (default). .TP \fB\-nopixfill\fR Do not convert out-of-range values in input file, just copy them through. .TP \fB\-pixfill\fR Replace out-of-range values in input file by the smallest possible value (default). .TP \fB\-pixfillvalue\fR\ \fIvalue\fR Specify a new pixel value to replace out-of-range values in the input file. .SH Image conversion options (dimension direction and size): .TP \fB\-scalar\fR Convert vector images to scalar images (a vector image is one with \fIvector_dimension\fR as the fastest varying dimension). The vector dimension is removed and values are averaged. .TP \fB\-noscalar\fR Do not convert vector images to scalar images (default). .TP \fB\+direction\fR Flip images to give positive step value for spatial axes. Note that the flipping of spatial axes only applies to "image dimensions". These are the two fastest varying (non-vector) dimensions in the file. If you want to flip a non-image dimension, you can convert it to an image dimension with \fB\-dimsize\fR \fIdimname\fR\fB=\-1\fR (the \-1 means don't really change the size). Check out the examples. .TP \fB\-direction\fR Flip images to give negative step value for spatial axes. .TP \fB-anydirection\fR Don't flip images along spatial axes (default). .TP \fB+xdirection\fR Flip images to give positive xspace:step value (left-to-right). .TP \fB\-xdirection\fR Flip images to give negative xspace:step value (right-to-left). .TP \fB\-xanydirection\fR Don't flip images along x-axis. .TP \fB\+ydirection\fR Flip images to give positive yspace:step value (posterior-to-anterior). .TP \fB\-ydirection\fR Flip images to give negative yspace:step value (anterior-to-posterior). .TP \fB\-yanydirection\fR Don't flip images along y-axis. .TP \fB\+zdirection\fR Flip images to give positive zspace:step value (inferior-to-superior). .TP \fB\-zdirection\fR Flip images to give negative zspace:step value (superior-to-inferior). .TP \fB\-zanydirection\fR Don't flip images along z-axis. .TP \fB\-keepaspect\fR Preserve aspect ratio when resizing images. This means that voxels are replicated (or averaged) the same number of times along each image dimension. .TP \fB\-nokeepaspect\fR Do not force preservation of aspect ratio when resizing images (default). .TP \fB\-imgsize\fR\ \fIsize\fR Specify the desired image size (used if \fB\-rowsize\fR or \fB\-colsize\fR not given). .TP \fB\-rowsize\fR\ \fIsize\fR Specify the desired number of rows in the image. .TP \fB\-colsize\fR\ \fIsize\fR Specify the desired number of columns in the image. .TP \fB\-dimsize\fR\ \fIdimension\fR=\fIsize\fR Specify the size of a named dimension (\fIdimension\fR=\fIsize\fR). Note that the resizing only applies to "image dimensions" - usually the two fastest-varying (non-vector) dimensions. To do dimension resizing, all fastest-varying dimensions up to the named dimension are turned into image dimensions, and these are then affected by the direction options. The dimension name and size must be in one command-line argument, so if you use spaces (which is okay), remember to use quotes to hide them from the shell. .SH Reshaping options: .TP \fB\-transverse\fR Write out transverse slices. .TP \fB\-sagittal\fR Write out sagittal slices. .TP \fB-coronal\fR Write out coronal slices. .TP \fB\-dimorder\fR\ \fIdim1\fR,\fIdim2\fR,\fIdim3\fR,... Specify dimension order, where \fIdim?\fR are the names of the dimensions. You can give fewer dimensions than exist in the file: they are assumed to be the fastest varying dimensions in the output file (so \fB\-transverse\fR is exactly equivalent to \fB-dimorder zspace,yspace,xspace\fR). Again, spaces are allowed between names, but remember to hide them from the shell with quotes. .TP \fB\-dimrange\fR\ \fIdim\fR=\fIstart\fR[,\fIcount\fR] Specify the range of dimension subscripts for dimension \fIdim\fR. If \fIcount\fR is missing or 0, then it is taken to mean 1, but remove the dimension from the output file (a count of 1 will keep a dimension of size 1). A negative \fIcount\fR means flip the data along that dimension - in this case \fIstart\fR specifies the highest voxel coordinate for the dimension (\fB\-dimrange\ xspace=3,\-3\fR gives a flipped version of \fB\-dimrange\ xspace=1,3\fR). The options \fB\-start\fR and \fB\-count\fR provide an alternative way to specify the same information. .TP \fB\-start\fR\ \fIcoord0\fR,\fIcoord1\fR,\fIcoord2\fR,... Specifies the starting corner of the hyperslab (coordinates go from slowest varying dimension to fastest). If fewer coordinates are given than dimensions exist in the file, then they are assumed to apply to the slowest varying dimensions and the remaining coordinates are set to 0. See \fB\-dimrange\fR for more details. Both \fB\-start\fR and \fB\-count\fR give vectors that correspond to input file dimensions after the image conversion (ICV) options have been applied. .TP \fB\-count\fR\ \fIsize0\fR,\fIsize1\fR,\fIsize2\fR,... Specifies edge lengths of hyperslab to read (coordinates go from slowest varying dimension to fastest). If fewer sizes are given than dimensions exist in the file, then they are assumed to apply to the slowest varying dimensions and the remaining sizes are set to the full size of the dimension. See \fB\-dimrange\fR for more details. Both \fB\-start\fR and \fB-count\fR give vectors that correspond to input file dimensions after the image conversion (ICV) options have been applied. .SH Missing data options: .TP \fB\-nofill\fR Use value zero for points outside of the input volume (default). .TP \fB\-fill\fR Use a fill value for points outside of input volume (minimum possible value). .TP \fB\-fillvalue\fR\ \fIvalue\fR Specify a fill value for points outside of the input volume (this is a real value, not a pixel value). .SH Generic options for all commands: .TP \fB\-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES: Assume that we have a volume with dimensions zspace, yspace, xspace (that's transverse) and sizes 128, 256, 256. If we want to get slice 40 out of it (keeping the coordinate information for the zspace dimension), then we can use mincreshape original.mnc new.mnc -dimrange zspace=40,1 Alternatively, we could use mincreshape original.mnc new.mnc -start 40,0,0 -count 1,256,256 Or simply mincreshape original.mnc new.mnc -start 40 -count 1 If we wanted to get rid of the zspace dimension, we could use mincreshape original.mnc new.mnc -dimrange zspace=40,0 Let's get a block out of the middle and flip it along xspace: mincreshape original.mnc new.mnc \\ -start 40,10,240 -count 1,200,-200 But why restrain outselves? Let's go out of bounds: mincreshape original.mnc new.mnc \\ -start 40,-100,340 -count 1,200,-200 If you want a sagittal volume, use this: mincreshape original.mnc new.mnc -sagittal How about some sideways heads - flip x and y. And convert to byte to save space while we're at it: mincreshape original.mnc new.mnc -dimorder xspace,yspace -byte You like to store volumes in x,y,z order (that's z varying fastest! I know some people who do it!)? Okay. mincreshape original.mnc new.mnc -dimorder xspace,yspace,zspace But you're a minimalist (and don't mind taking a chance). Here's the same thing (but it might break for another file): mincreshape original.mnc new.mnc -dimorder zspace Let's make sure that all dimensions have a negative step attribute (see option +direction for some details): mincreshape original.mnc new.mnc -direction -dimsize zspace=-1 .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1994 by Peter Neelin .SH "SEE ALSO" .LP .BR mincresample (1) minc-tools-2.3.00+dfsg/progs/mincreshape/copy_data.c0000644000175000000620000010013512574624760021361 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : copy_data @DESCRIPTION: File containing routines to copy data when reshaping. @METHOD : @GLOBALS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : * $Log: copy_data.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/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.6 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.5 2006/05/19 00:35:58 bert * Add config.h to several files that might need it * * Revision 6.4 2004/11/01 22:38:39 bert * Eliminate all references to minc_def.h * * Revision 6.3 2001/04/17 18:40:24 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 1999/10/19 14:45:28 neelin * Fixed Log subsitutions for CVS * * Revision 6.1 1998/08/19 13:05:28 neelin * Added code to free minmax buffer. * * Revision 6.0 1997/09/12 13:24:12 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:10 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:44 neelin * Release of minc version 0.4 * * Revision 3.1 1995/10/03 13:34:14 neelin * Fixed bug in truncate_input_vectors - was not handling out-of-range * start values properly. * * Revision 3.0 1995/05/15 19:32:36 neelin * Release of minc version 0.3 * * Revision 1.5 1995/03/20 13:32:03 neelin * Fixed -normalize option. * * Revision 1.4 1994/12/02 09:08:56 neelin * Moved nd_loop to proglib. * * Revision 1.3 94/11/23 11:46:38 neelin * Handle image-min/max properly when using icv for normalization. * * Revision 1.2 94/11/22 08:45:11 neelin * Fixed handling of normalization for number of image dimensions > 2. * Added appropriate default values of image-max and image-min. * * Revision 1.1 94/11/02 16:21:09 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "mincreshape.h" #define VIO_ROUND( x ) ((long) ((x) + ( ((x) >= 0) ? 0.5 : (-0.5) ) )) static void get_num_minmax_values(Reshape_info *reshape_info, long *block_start, long *block_count, long *num_min_values, long *num_max_values); static void handle_normalization(Reshape_info *reshape_info, long *block_start, long *block_count, double *minmax_buffer, double *fillvalue); static void get_block_min_and_max(Reshape_info *reshape_info, long *block_start, long *block_count, double *minmax_buffer, double *minimum, double *maximum); static void truncate_input_vectors(Reshape_info *reshape_info, long *input_start, long *input_count); static void translate_output_to_input(Reshape_info *reshape_info, long *output_start, long *output_count, long *input_start, long *input_count); static void translate_input_to_output(Reshape_info *reshape_info, long *input_start, long *input_count, long *output_start, long *output_count); static void copy_the_chunk(Reshape_info *reshape_info, long chunk_start[], long chunk_count[], void *chunk_data, double fillvalue); static void convert_value_from_double(double dvalue, nc_type datatype, int is_signed, void *ptr); /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_data @INPUT : reshape_info - information for reshaping volume @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Copies data from one input volume to another, reorganizing it according to the reshaping info. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void copy_data(Reshape_info *reshape_info) { int idim, odim, out_ndims; long block_begin[MAX_VAR_DIMS], block_end[MAX_VAR_DIMS]; long block_count[MAX_VAR_DIMS]; long block_cur_start[MAX_VAR_DIMS], block_cur_count[MAX_VAR_DIMS]; long chunk_begin[MAX_VAR_DIMS], chunk_end[MAX_VAR_DIMS]; long chunk_count[MAX_VAR_DIMS]; long chunk_cur_start[MAX_VAR_DIMS], chunk_cur_count[MAX_VAR_DIMS]; long total_size; long num_min_values, num_max_values, num_values; double fillvalue, *minmax_buffer; void *chunk_data; /* Get number of dimensions */ out_ndims = reshape_info->output_ndims; /* Set up variables for looping through blocks */ for (odim=0; odim < out_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; block_begin[odim] = 0; block_end[odim] = ABS(reshape_info->input_count[idim]); if (reshape_info->dim_used_in_block[odim]) block_count[odim] = ABS(reshape_info->input_count[idim]); else block_count[odim] = 1; } /* Figure out size of chunks and allocate space */ total_size = nctypelen(reshape_info->output_datatype); for (odim=0; odim < out_ndims; odim++) { total_size *= reshape_info->chunk_count[odim]; } chunk_data = malloc(total_size); /* Get enough space for image-min and max values for a block */ get_num_minmax_values(reshape_info, NULL, block_count, &num_min_values, &num_max_values); num_values = ((num_min_values > num_max_values) ? num_min_values : num_max_values); if (num_values > 0) minmax_buffer = malloc(num_values * sizeof(double)); else minmax_buffer = NULL; /* Print log message */ if (reshape_info->verbose) { (void) fprintf(stderr, "Copying chunks:"); (void) fflush(stderr); } /* Loop through blocks */ nd_begin_looping(block_begin, block_cur_start, out_ndims); while (!nd_end_of_loop(block_cur_start, block_end, out_ndims)) { /* Set up count for current block */ nd_update_current_count(block_cur_start, block_count, block_end, block_cur_count, out_ndims); /* Set up chunk begin, end and count */ for (odim=0; odim < out_ndims; odim++) { chunk_begin[odim] = block_cur_start[odim]; chunk_end[odim] = chunk_begin[odim] + block_cur_count[odim]; chunk_count[odim] = reshape_info->chunk_count[odim]; } /* Set up icv for normalization, set output image-max/min and calculate pixel fill value to use for current block */ handle_normalization(reshape_info, block_cur_start, block_cur_count, minmax_buffer, &fillvalue); /* Loop through chunks */ nd_begin_looping(chunk_begin, chunk_cur_start, out_ndims); while (!nd_end_of_loop(chunk_cur_start, chunk_end, out_ndims)) { /* Set up count for current chunk */ nd_update_current_count(chunk_cur_start, chunk_count, chunk_end, chunk_cur_count, out_ndims); /* Print log message for chunk */ if (reshape_info->verbose) { (void) fprintf(stderr, "."); (void) fflush(stderr); } /* Copy the chunk */ copy_the_chunk(reshape_info, chunk_cur_start, chunk_cur_count, chunk_data, fillvalue); /* Increment chunk loop count */ nd_increment_loop(chunk_cur_start, chunk_begin, chunk_count, chunk_end, out_ndims); } /* Increment block loop count */ nd_increment_loop(block_cur_start, block_begin, block_count, block_end, out_ndims); } /* Free the chunk space */ free(chunk_data); /* Free minmax buffer */ if (minmax_buffer != NULL) { free(minmax_buffer); } /* Print ending log message */ if (reshape_info->verbose) { (void) fprintf(stderr, "Done.\n"); (void) fflush(stderr); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_num_minmax_values @INPUT : reshape_info - information for reshaping volume block_start - start for a block (or NULL) block_count - count for a block @OUTPUT : num_min_values - number of image-min values to be read num_max_values - number of image-max values to be read @RETURNS : (nothing) @DESCRIPTION: Gets the number of image-min and image-max values that correspond to a block. If block_start is NULL, then it is assumed to translate to an input start of [0,0,...]. Note that only the true number of values for the specified block is computed (specifying a hyperslab that goes beyond file extents does not give a bigger number of values). @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_num_minmax_values(Reshape_info *reshape_info, long *block_start, long *block_count, long *num_min_values, long *num_max_values) { int iloop, idim, ndims; int varid, inimgid; long size; long minmax_count[MAX_VAR_DIMS]; long input_block_start[MAX_VAR_DIMS]; long input_block_count[MAX_VAR_DIMS]; long *num_values; /* Check for icv normalization */ if (reshape_info->do_icv_normalization) { *num_min_values = 0; *num_max_values = 0; return; } /* Translate output block count to input count */ translate_output_to_input(reshape_info, block_start, block_count, input_block_start, input_block_count); if (block_start != NULL) { truncate_input_vectors(reshape_info, input_block_start, input_block_count); } inimgid = ncvarid(reshape_info->inmincid, MIimage); /* Loop over image-min and image-max */ for (iloop=0; iloop < 2; iloop++) { /* Get varid and pointer to return value */ ncopts = 0; switch (iloop) { case 0: varid = ncvarid(reshape_info->inmincid, MIimagemin); num_values = num_min_values; /* Pointer to long */ break; case 1: varid = ncvarid(reshape_info->inmincid, MIimagemax); num_values = num_max_values; /* Pointer to long */ break; } ncopts = NCOPTS_DEFAULT; /* Translate block count to min or max count and work out the total number of values. */ size = 0; if (varid != MI_ERROR) { (void) ncvarinq(reshape_info->inmincid, varid, NULL, NULL, &ndims, NULL, NULL); (void) mitranslate_coords(reshape_info->inmincid, inimgid, input_block_count, varid, minmax_count); size = 1; for (idim=0; idim < ndims; idim++) { size *= minmax_count[idim]; } } *num_values = size; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : handle_normalization @INPUT : reshape_info - information for reshaping volume block_start - start of current block block_count - count for current block minmax_buffer - buffer space for getting min and max values @OUTPUT : fillvalue - pixel fill value to use for this block @RETURNS : (none) @DESCRIPTION: Sets up icv for normalization to ensure that block is internally normalized. Output image-max and min are set. The appropriate pixel fill value is calculated for this min and max (applies to the whole block). @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void handle_normalization(Reshape_info *reshape_info, long *block_start, long *block_count, double *minmax_buffer, double *fillvalue) { int iloop; int inmincid, inimgid, varid, icvid; long minmax_start[MAX_VAR_DIMS]; double minimum, maximum, *extreme, valid_min, valid_max, denom; char *varname; /* Get input minc id, image id and icv id*/ inmincid = reshape_info->inmincid; inimgid = ncvarid(inmincid, MIimage); icvid = reshape_info->icvid; /* Get input min and max for block */ get_block_min_and_max(reshape_info, block_start, block_count, minmax_buffer, &minimum, &maximum); /* Calculate the pixel fill value */ *fillvalue = ((reshape_info->fillvalue == NOFILL) ? 0.0 : reshape_info->fillvalue); /* If the fillvalue for uninitialized data lies outside the * pre-existing data range, we need to adjust the range * accordingly, which implies renormalization. */ if (*fillvalue < minimum) { minimum = *fillvalue; reshape_info->do_block_normalization = TRUE; } if (*fillvalue > maximum) { maximum = *fillvalue; reshape_info->do_block_normalization = TRUE; } /* Modify the icv if necessary */ if (reshape_info->do_block_normalization) { (void) miicv_detach(icvid); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MIN, minimum); (void) miicv_setdbl(icvid, MI_ICV_IMAGE_MAX, maximum); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_attach(icvid, inmincid, inimgid); } /* Save the image max and min for the block */ for (iloop=0; iloop < 2; iloop++) { /* Get varid and pointer to min or max value */ switch (iloop) { case 0: varname = MIimagemin; extreme = &minimum; break; case 1: varname = MIimagemax; extreme = &maximum; break; } /* Save the value */ ncopts = 0; varid = ncvarid(reshape_info->outmincid, varname); ncopts = NCOPTS_DEFAULT; if (varid != MI_ERROR) { (void) mitranslate_coords(reshape_info->outmincid, reshape_info->outimgid, block_start, varid, minmax_start); (void) mivarput1(reshape_info->outmincid, varid, minmax_start, NC_DOUBLE, NULL, extreme); } } if ((reshape_info->output_datatype != NC_FLOAT) && (reshape_info->output_datatype != NC_DOUBLE) && (*fillvalue != FILL)) { (void) miicv_inqdbl(icvid, MI_ICV_VALID_MIN, &valid_min); (void) miicv_inqdbl(icvid, MI_ICV_VALID_MAX, &valid_max); denom = maximum - minimum; if (denom == 0.0) { *fillvalue = valid_min; } else { *fillvalue = (*fillvalue - minimum) * (valid_max - valid_min) / denom + valid_min; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_block_min_and_max @INPUT : reshape_info - information for reshaping volume block_start - start of current block block_count - count for current block minmax_buffer - buffer space for getting min and max values @OUTPUT : minimum - input minimum for block maximum - input maximum for block @RETURNS : (none) @DESCRIPTION: Gets the min and max for the input file for a given output block. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_block_min_and_max(Reshape_info *reshape_info, long *block_start, long *block_count, double *minmax_buffer, double *minimum, double *maximum) { int iloop; long num_min_values, num_max_values, ivalue; int inmincid, inimgid, varid, icvid; long minmax_start[MAX_VAR_DIMS], minmax_count[MAX_VAR_DIMS]; long input_block_start[MAX_VAR_DIMS], input_block_count[MAX_VAR_DIMS]; double *extreme; long num_values; char *varname; double sign, default_extreme; /* Get input minc id, image id and icv id*/ inmincid = reshape_info->inmincid; inimgid = ncvarid(inmincid, MIimage); icvid = reshape_info->icvid; /* Is the icv doing the normalization? */ if (reshape_info->do_icv_normalization) { (void) miicv_inqdbl(icvid, MI_ICV_NORM_MIN, minimum); (void) miicv_inqdbl(icvid, MI_ICV_NORM_MAX, maximum); return; } /* Translate output block count to input count */ translate_output_to_input(reshape_info, block_start, block_count, input_block_start, input_block_count); truncate_input_vectors(reshape_info, input_block_start, input_block_count); /* Get number of min and max values */ get_num_minmax_values(reshape_info, block_start, block_count, &num_min_values, &num_max_values); /* Loop over image-min and image-max getting block min and max */ for (iloop=0; iloop < 2; iloop++) { /* Get varid and pointer to min or max value */ switch (iloop) { case 0: num_values = num_min_values; varname = MIimagemin; extreme = minimum; sign = -1.0; default_extreme = 0.0; break; case 1: num_values = num_max_values; varname = MIimagemax; extreme = maximum; sign = +1.0; default_extreme = 1.0; break; } /* Get values from file */ if (num_values > 0) { varid = ncvarid(inmincid, varname); (void) mitranslate_coords(inmincid, inimgid, input_block_start, varid, minmax_start); (void) mitranslate_coords(inmincid, inimgid, input_block_count, varid, minmax_count); (void) mivarget(reshape_info->inmincid, varid, minmax_start, minmax_count, NC_DOUBLE, NULL, minmax_buffer); *extreme = minmax_buffer[0]; for (ivalue=1; ivalue < num_values; ivalue++) { if ((minmax_buffer[ivalue] * sign) > (*extreme * sign)) *extreme = minmax_buffer[ivalue]; } } else { *extreme = default_extreme; } if (reshape_info->need_fillvalue && (reshape_info->fillvalue == NOFILL) && (0.0 > (*extreme * sign))) *extreme = 0.0; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : truncate_input_vectors @INPUT : reshape_info - information for reshaping volume input_start - start of input hyperslab (or NULL) input_count - count for input hyperslab @OUTPUT : input_start - start of input hyperslab (or NULL) input_count - count for input hyperslab @RETURNS : (nothing) @DESCRIPTION: Input_start and input_count are truncated to specify a legal hyperslab for the input file (if not specified, input_start is assumed to be [0,0,...]). @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void truncate_input_vectors(Reshape_info *reshape_info, long *input_start, long *input_count) { int idim; long first, last; /* last is actually last_index+1 */ /* Check for NULL vectors */ if (input_count == NULL) return; /* Loop through input dimensions */ for (idim=0; idim < reshape_info->input_ndims; idim++) { first = ( (input_start != NULL) ? input_start[idim] : 0 ); last = first + input_count[idim]; if (first < 0) first = 0; else if (first >= reshape_info->input_size[idim]) first = reshape_info->input_size[idim] - 1; if (last < 0) last = 0; else if (last >= reshape_info->input_size[idim]) last = reshape_info->input_size[idim]; if (input_start != NULL) input_start[idim] = first; input_count[idim] = last - first; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : translate_output_to_input @INPUT : reshape_info - information for reshaping volume output_start - start of output hyperslab (or NULL) output_count - count for output hyperslab @OUTPUT : input_start - start of input hyperslab (or NULL) input_count - count for input hyperslab @RETURNS : (nothing) @DESCRIPTION: Translates an output start and count to an input start and count. If output_start or input_start are NULL, then only the count is translated. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void translate_output_to_input(Reshape_info *reshape_info, long *output_start, long *output_count, long *input_start, long *input_count) { int idim, odim; /* Check for NULL vectors */ if ((input_count == NULL) || (output_count == NULL)) return; /* Loop through input dimensions */ for (idim=0; idim < reshape_info->input_ndims; idim++) { odim = reshape_info->map_in_to_out[idim]; input_count[idim] = ((odim >= 0) ? output_count[odim] : 1); if ((input_start != NULL) && (output_start != NULL)) { input_start[idim] = reshape_info->input_start[idim]; if (odim >= 0) { if (reshape_info->input_count[idim] > 0) input_start[idim] += output_start[odim]; else input_start[idim] -= (output_start[odim] + output_count[odim] - 1); } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : translate_input_to_output @INPUT : reshape_info - information for reshaping volume input_start - start of output hyperslab (or NULL) input_count - count for output hyperslab @OUTPUT : output_start - start of input hyperslab (or NULL) output_count - count for input hyperslab @RETURNS : (nothing) @DESCRIPTION: Translates an input start and count to an output start and count. If output_start or input_start are NULL, then only the count is translated. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void translate_input_to_output(Reshape_info *reshape_info, long *input_start, long *input_count, long *output_start, long *output_count) { int idim, odim; /* Check for NULL vectors */ if ((input_count == NULL) || (output_count == NULL)) return; /* Loop through output dimensions */ for (odim=0; odim < reshape_info->output_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; output_count[odim] = input_count[idim]; if ((input_start != NULL) && (output_start != NULL)) { if (reshape_info->input_count[idim] > 0) output_start[odim] = input_start[idim] - reshape_info->input_start[idim]; else output_start[odim] = reshape_info->input_start[idim] - (input_start[idim] + input_count[idim] - 1); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_the_chunk @INPUT : reshape_info - information for reshaping volume chunk_start - start of current block chunk_count - count for current block chunk_data - pointer to enough space for chunk fillvalue - pixel value to zero volume, if necessary. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Copies the chunk from the input file to the output file. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void copy_the_chunk(Reshape_info *reshape_info, long chunk_start[], long chunk_count[], void *chunk_data, double fillvalue) { int idim, odim, in_ndims, out_ndims; long input_start[MAX_VAR_DIMS], input_count[MAX_VAR_DIMS]; long output_start[MAX_VAR_DIMS], output_count[MAX_VAR_DIMS]; long input_imap[MAX_VAR_DIMS], output_imap[MAX_VAR_DIMS]; void *output_origin; int datatype_size; long total_size, ipix, first, last; int zero_data, really_copy_the_data; union { char c; short s; long l; float f; double d; } value_buffer; /* Get number of dimensions */ out_ndims = reshape_info->output_ndims; in_ndims = reshape_info->input_ndims; /* Get size of output datatype */ datatype_size = nctypelen(reshape_info->output_datatype); /* Create input start and count */ translate_output_to_input(reshape_info, chunk_start, chunk_count, input_start, input_count); /* Find out if we need to zero the volume and if we need to copy any data */ zero_data = FALSE; really_copy_the_data = TRUE; total_size = 1; for (idim=0; idim < in_ndims; idim++) { first = input_start[idim]; last = input_start[idim] + input_count[idim] - 1; if ((first < 0) || (last >= reshape_info->input_size[idim])) zero_data = TRUE; if ((last < 0) || (first >= reshape_info->input_size[idim])) really_copy_the_data = FALSE; total_size *= input_count[idim]; } /* Make sure that input vectors are legal and translate them back to output */ truncate_input_vectors(reshape_info, input_start, input_count); translate_input_to_output(reshape_info, input_start, input_count, output_start, output_count); /* Write out zero data if needed */ if (zero_data) { convert_value_from_double(fillvalue, reshape_info->output_datatype, reshape_info->output_is_signed, &value_buffer); for (ipix=0; ipix < total_size; ipix++) { (void) memcpy((char *)chunk_data + ipix*datatype_size, &value_buffer, datatype_size); } (void) ncvarput(reshape_info->outmincid, reshape_info->outimgid, chunk_start, chunk_count, chunk_data); } /* Set up hypothetical imap variable for input */ for (idim=in_ndims-1; idim >= 0; idim--) { input_imap[idim] = ((idim == in_ndims-1) ? datatype_size : input_imap[idim+1] * input_count[idim+1]); } /* Create output imap variable from input one (re-ordering dimensions and flipping). Also work out the chunk origin (point to byte for output [0,0,0...]). */ output_origin = chunk_data; for (odim=0; odim < out_ndims; odim++) { idim = reshape_info->map_out_to_in[odim]; if (reshape_info->input_count[idim] > 0) { output_imap[odim] = input_imap[idim]; } else { output_imap[odim] = -input_imap[idim]; output_origin = (void *) ((char *)output_origin - (output_count[odim] - 1) * output_imap[odim]); } } /* Should we really copy the data? */ if (really_copy_the_data) { /* Read in the data */ (void) miicv_get(reshape_info->icvid, input_start, input_count, chunk_data); /* Write it out */ (void) ncvarputg(reshape_info->outmincid, reshape_info->outimgid, output_start, output_count, NULL, output_imap, output_origin); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_value_from_double @INPUT : dvalue - double value to convert datatype - type of desired value is_signed - TRUE if desired value is signed @OUTPUT : ptr - pointer to converted value @RETURNS : (nothing) @DESCRIPTION: Converts a value from double to some other value. @METHOD : @GLOBALS : @CALLS : @CREATED : October 25, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void convert_value_from_double(double dvalue, nc_type datatype, int is_signed, void *ptr) { switch (datatype) { case NC_BYTE : if (!is_signed) { dvalue = MAX(0, dvalue); dvalue = MIN(UCHAR_MAX, dvalue); *((unsigned char *) ptr) = VIO_ROUND(dvalue); } else { dvalue = MAX(SCHAR_MIN, dvalue); dvalue = MIN(SCHAR_MAX, dvalue); *((signed char *) ptr) = VIO_ROUND(dvalue); } break; case NC_SHORT : if (!is_signed) { dvalue = MAX(0, dvalue); dvalue = MIN(USHRT_MAX, dvalue); *((unsigned short *) ptr) = VIO_ROUND(dvalue); } else { dvalue = MAX(SHRT_MIN, dvalue); dvalue = MIN(SHRT_MAX, dvalue); *((signed short *) ptr) = VIO_ROUND(dvalue); } break; case NC_INT : if (!is_signed) { dvalue = MAX(0, dvalue); dvalue = MIN(UINT_MAX, dvalue); *((unsigned int *) ptr) = VIO_ROUND(dvalue); } else { dvalue = MAX(INT_MIN, dvalue); dvalue = MIN(INT_MAX, dvalue); *((signed int *) ptr) = VIO_ROUND(dvalue); } break; case NC_FLOAT : dvalue = MAX(-FLT_MAX,dvalue); *((float *) ptr) = MIN(FLT_MAX,dvalue); break; case NC_DOUBLE : *((double *) ptr) = dvalue; break; } } minc-tools-2.3.00+dfsg/progs/mincconvert/0002755000175000000620000000000012574624760017305 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincconvert/mincconvert.c0000644000175000000620000001467412574624760022012 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincconvert @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Program to convert between MINC 1.0 and MINC 2.0 formats. @METHOD : @GLOBALS : @CALLS : @CREATED : 2003-12-17 @MODIFIED : * $Log: mincconvert.c,v $ * Revision 1.10 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 1.9 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.8 2007/12/10 13:25:12 rotor * * few more fixes for CMake build * * started adding static to globals for a wierd zlib bug * * Revision 1.7 2007/08/09 17:05:25 rotor * * added some fixes of Claudes for chunking and internal compression * * Revision 1.6 2006/07/28 16:49:55 baghdadi * added message to disallow conversion to itself and exit with success * * Revision 1.6 2006/06/21 11:30:00 Leila * added return value for main function * Revision 1.5 2006/04/10 11:30:00 Leila * check the version of file and Abort if converting to itself! * Revision 1.4 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 1.3 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 1.2 2004/09/09 19:25:32 bert * Force V1 file format creation if -2 not specified * * Revision 1.1 2004/04/27 15:27:57 bert * Initial checkin, MINC 1 <-> MINC 2 converter * * @COPYRIGHT : 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 #include #include static int clobber = 0; static int v2format = 0; static int do_template = 0; static int compress = -1; static int chunking = -1; ArgvInfo argTable[] = { {"-clobber", ARGV_CONSTANT, (char *) 1, (char *) &clobber, "Overwrite existing file."}, {"-2", ARGV_CONSTANT, (char *) 1, (char *) &v2format, "Create a MINC 2 output file."}, {"-template", ARGV_CONSTANT, (char *) 1, (char *)&do_template, "Create a template file."}, {"-compress", ARGV_INT, (char *) 1, (char *)&compress, "Set the compression level, from 0 (disabled) to 9 (maximum)."}, {"-chunk", ARGV_INT, (char *) 1, (char *)&chunking, "Set the target block size for chunking (-1 unknown, 0 default, >1 block size)."}, {NULL, ARGV_END, NULL, NULL, NULL} }; int micopy(int old_fd, int new_fd, char *new_history, int is_template) { if (is_template) { /* Tell NetCDF that we don't want to allocate the data until written. */ ncsetfill(new_fd, NC_NOFILL); } /* Copy all variable definitions (and global attributes). */ micopy_all_var_defs(old_fd, new_fd, 0, NULL); /* Append the updated history. */ miappend_history(new_fd, new_history); if (!is_template) { ncendef(new_fd); micopy_all_var_values(old_fd, new_fd, 0, NULL); } else { /* This isn't really standard, but flag this as a template file. */ miattputstr(new_fd, NC_GLOBAL, "class", "template"); } return (MI_NOERROR); } /* Main program */ int main(int argc, char **argv) { char *old_fname; char *new_fname; int old_fd; int new_fd; int flags; #if MINC2 struct mi2opts opts; #endif /* MINC2 */ char *new_history = time_stamp(argc, argv); /* Check arguments */ if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { fprintf(stderr, "\nUsage: %s [] \n", argv[0]); fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } old_fname = argv[1]; new_fname = argv[2]; old_fd = miopen(old_fname, NC_NOWRITE); if (old_fd < 0) { perror(old_fname); exit(EXIT_FAILURE); } /* check the version of file and Abort if converting to itself */ if (MI2_ISH5OBJ(old_fd)) { if (v2format) { /* If already a minc2 file, allow to do compression if requested by the user. Ideally, one should call the HDF5 function H5Pget_deflate to query the compression level of the input image, but this function does not seem to be supported anymore in HDF5. */ fprintf(stderr,"Warning: Converting Version 2 (HDF5) to itself!! \n"); } } else { if (!v2format) { fprintf(stderr,"Abort: Converting Version 1 (netCDF) to itself!! \n"); exit(EXIT_FAILURE); } } flags = 0; if (clobber) { flags |= NC_CLOBBER; } else { flags |= NC_NOCLOBBER; } #if MINC2 if (v2format) { flags |= MI2_CREATE_V2; } else { flags |= MI2_CREATE_V1; /* Force V1 format */ } opts.struct_version = MI2_OPTS_V1; if (compress == -1) { opts.comp_type = MI2_COMP_UNKNOWN; } else if (compress == 0) { opts.comp_type = MI2_COMP_NONE; } else { opts.comp_type = MI2_COMP_ZLIB; opts.comp_param = compress; } if (chunking == -1) { opts.chunk_type = MI2_CHUNK_UNKNOWN; } else if (chunking == 0) { opts.chunk_type = MI2_CHUNK_OFF; } else { opts.chunk_type = MI2_CHUNK_ON; opts.chunk_param = chunking; } new_fd = micreatex(new_fname, flags, &opts); if (new_fd < 0) { perror(new_fname); exit(EXIT_FAILURE); } #else new_fd = micreate(new_fname, flags); #endif /* not MINC2 */ micopy(old_fd, new_fd, new_history, do_template); miclose(old_fd); miclose(new_fd); free(new_history); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/progs/mincconvert/mincconvert.man10000644000175000000620000000367312574624760022421 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCCONVERT 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincconvert \- convert between MINC 1 to MINC 2 format. .SH SYNOPSIS .B mincconvert .BI [-clobber] .BI [-2] .BI infile .BI outfile .P .B mincconvert .BI -help .SH DESCRIPTION \fImincconvert\fR copies the input file to the output file, possibly converting the file from MINC 1 to MINC 2 format, or vice versa. With the \fB\-template\fR flag, \fImincconvert\fR creates a "template" volume from the input MINC volume. The template volume preserves all of the structure (dimensions, variables, and attributes) of the input MINC volume but omits all data. Any attempt to read data will return zeroes. The resulting file is typically much shorter than a normal MINC volume, and may be useful for scripts which want to carry such structural information forward into their output files. As a hint to future programmers and users, this program places a special global attribute in the file, with the name \fBclass\fR and the value \fBtemplate\fR. .SH OPTIONS .TP \fB\-2\fR Create a MINC 2 format file .TP \fB\-clobber\fR Overwrite a pre-existing output file. \fB\-help\fR Print a summary of command line options and exit .TP \fB\-template\fR Create a template file. .TP \fB-compress\fR \fIN\fR Compress file with compression level \fIN\fR. Valid compression levels are 0 for no compression to 9 for maximum compression. The option has no effect if the output file is a MINC 1 file. .TP \fB\-chunk\fR \fIM\fR Store file in a block-structured arrangement, using hypercubes of edge length \fIM\fR. The option has no effect if the output file is a MINC 1 file. .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH AUTHOR Bert Vincent - bert@bic.mni.mcgill.ca .SH COPYRIGHTS Copyright \(co 2003 by Robert Vincent and the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/progs/mincmorph/0002755000175000000620000000000012574624760016752 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmorph/make_mincmorph_kernel.pl0000755000175000000620000000503212574624760023641 0ustar stevestaff#! /usr/bin/env perl # # Script to produce mincmorph structuring elements # # Alex Zidjdenbos # # 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 makes no representations about the suitability of this software # for any purpose. It is provided "as is" without express or implied warranty. use strict; use warnings "all"; use Cwd qw/ abs_path getcwd /; use File::Basename; use File::Temp qw/ tempdir /; use Getopt::Tabular; use IO::File; use POSIX qw/floor ceil/; my $Clobber = 0; my $Execute = 1; my $Verbose = 0; my $R = 2; my @R3; my $Me = fileparse($0); my $Usage = < USAGE my $Help = <"], ["-radius3", "float", 3, \@R3, "Radii of ellipsoidal kernel (in voxels). Forces", " "], ); my @LeftOverArgs; &GetOptions(\@ArgTable, \@ARGV, \@LeftOverArgs) || exit 1; die $Usage if (@LeftOverArgs != 1); my $File = shift @LeftOverArgs; die "$File exists; use -clobber to overwrite\n" if (! $Clobber && -e $File); my $fh = new IO::File ">$File"; die "Unable to open file $File\n" if (! defined $fh); @R3 = ($R, $R, $R) if ! @R3; my $Hdr = <close(); minc-tools-2.3.00+dfsg/progs/mincmorph/ChangeLog0000644000175000000620000000126412574624760020525 0ustar stevestaff2009-11-10 Andrew L Janke * Added local correlation option (I[]) * changed to VIO_ code for clean MINC2 build 2005-09-23 Bert Vincent * Fix reading of kernel files * Force calc_volume_range() to return with a max value strictly greater than the min. 2004-10-08 Andrew L. Janke * Added 4 inbuilt kernels 2004-05-25 Andrew L. Janke * Added -median_dilate option for mark 2004-04-14 Andrew L. Janke * Fixed bug in get_string_from_string() mallocing 2003-12-17 Andrew L. Janke * Added support for grey level dilation and erosion minc-tools-2.3.00+dfsg/progs/mincmorph/kernel_io.h0000644000175000000620000000171512574624760021074 0ustar stevestaff/* kernel_io.h */ #ifndef KERNEL_IO #define KERNEL_IO #define KERNEL_DIMS 5 /* inbuilt kernels */ extern int n_inbuilt_kern; typedef enum { K_NULL = 0, K_2D04, K_2D08, K_3D06, K_3D26 } kern_types; /* Structure for Kernel information */ typedef struct { int nelems; int pre_pad[KERNEL_DIMS]; int post_pad[KERNEL_DIMS]; VIO_Real **K; } Kernel; /* returns a new B_Matrix struct (pointer) */ Kernel *new_kernel(int nelems); void delete_kernel(Kernel * kernel); /* reads in a B_Matrix from a file (pointer) */ VIO_Status input_kernel(const char *kernel_file, Kernel * kernel); /* pretty print a kernel */ int print_kernel(Kernel * kernel); /* calculate start and step offsets for this kernel */ int setup_pad_values(Kernel * kernel); /* return the default kernel(s) */ Kernel *get_2D04_kernel(void); Kernel *get_2D08_kernel(void); Kernel *get_3D06_kernel(void); Kernel *get_3D26_kernel(void); #endif minc-tools-2.3.00+dfsg/progs/mincmorph/kernel_ops.h0000644000175000000620000000152412574624760021264 0ustar stevestaff/* kernel_ops.h */ #ifndef KERNEL_OPS #define KERNEL_OPS #include #include "kernel_io.h" /* kernel functions */ VIO_Volume binarise(VIO_Volume vol, double floor, double ceil, double fg, double bg); VIO_Volume clamp(VIO_Volume vol, double floor, double ceil, double bg); VIO_Volume pad(Kernel * K, VIO_Volume vol, double bg); VIO_Volume erosion_kernel(Kernel * K, VIO_Volume vol); VIO_Volume dilation_kernel(Kernel * K, VIO_Volume vol); VIO_Volume median_dilation_kernel(Kernel * K, VIO_Volume vol); VIO_Volume zero_dilation_kernel(Kernel * K, VIO_Volume vol); VIO_Volume convolve_kernel(Kernel * K, VIO_Volume vol); VIO_Volume distance_kernel(Kernel * K, VIO_Volume vol, double bg); VIO_Volume group_kernel(Kernel * K, VIO_Volume vol, double bg); VIO_Volume lcorr_kernel(Kernel * K, VIO_Volume vol, VIO_Volume cmp); #endif minc-tools-2.3.00+dfsg/progs/mincmorph/kernels/0002755000175000000620000000000012574624760020415 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincmorph/kernels/3x3x3_26-conn.kern0000644000175000000620000000237012574624760023430 0ustar stevestaffMNI Morphology Kernel File % % 3D 26-connectivity ie: (in 2D) % % 1 1 1 % 1 0 1 % 1 1 1 % % 0 - center voxel % Format: vector for voxel in 5 dimensions (x,y,z,t,v) then multiplier. Kernel_Type = Normal_Kernel; Kernel = % x y z t v coeff % ----------------------------------- 1.0 1.0 1.0 0.0 0.0 1.0 1.0 1.0 0.0 0.0 0.0 1.0 1.0 1.0 -1.0 0.0 0.0 1.0 % 1.0 0.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 -1.0 0.0 0.0 1.0 % 1.0 -1.0 1.0 0.0 0.0 1.0 1.0 -1.0 0.0 0.0 0.0 1.0 1.0 -1.0 -1.0 0.0 0.0 1.0 % % 0.0 1.0 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 1.0 -1.0 0.0 0.0 1.0 % 0.0 0.0 1.0 0.0 0.0 1.0 %% 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 -1.0 0.0 0.0 1.0 % 0.0 -1.0 1.0 0.0 0.0 1.0 0.0 -1.0 0.0 0.0 0.0 1.0 0.0 -1.0 -1.0 0.0 0.0 1.0 % % -1.0 1.0 1.0 0.0 0.0 1.0 -1.0 1.0 0.0 0.0 0.0 1.0 -1.0 1.0 -1.0 0.0 0.0 1.0 % -1.0 0.0 1.0 0.0 0.0 1.0 -1.0 0.0 0.0 0.0 0.0 1.0 -1.0 0.0 -1.0 0.0 0.0 1.0 % -1.0 -1.0 1.0 0.0 0.0 1.0 -1.0 -1.0 0.0 0.0 0.0 1.0 -1.0 -1.0 -1.0 0.0 0.0 1.0; minc-tools-2.3.00+dfsg/progs/mincmorph/kernels/3x3_4-conn.kern0000644000175000000620000000066212574624760023073 0ustar stevestaffMNI Morphology Kernel File % % 2D 4-connectivity ie: % % 1 % 1 0 1 % 1 % % 0 - center voxel % Format: vector for voxel in 5 dimensions (x,y,z,t,v) then multiplier. Kernel_Type = Normal_Kernel; Kernel = % x y z t v coeff % ----------------------------------- 1.0 0.0 0.0 0.0 0.0 1.0 -1.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 -1.0 0.0 0.0 0.0 1.0; minc-tools-2.3.00+dfsg/progs/mincmorph/kernels/3x3x3_6-conn.kern0000644000175000000620000000100012574624760023333 0ustar stevestaffMNI Morphology Kernel File % % 3D 6-connectivity ie: (in 2D) % % 1 % 1 0 1 % 1 % % 0 - center voxel % Format: vector for voxel in 5 dimensions (x,y,z,t,v) then multiplier. Kernel_Type = Normal_Kernel; Kernel = % x y z t v coeff % ----------------------------------- 1.0 0.0 0.0 0.0 0.0 1.0 -1.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 -1.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 -1.0 0.0 0.0 1.0; minc-tools-2.3.00+dfsg/progs/mincmorph/kernels/3x3_8-conn.kern0000644000175000000620000000115112574624760023071 0ustar stevestaffMNI Morphology Kernel File % % 2D 8-connectivity ie: % % 1 1 1 % 1 0 1 % 1 1 1 % % 0 - center voxel % Format: vector for voxel in 5 dimensions (x,y,z,t,v) then multiplier. Kernel_Type = Normal_Kernel; Kernel = % x y z t v coeff % ----------------------------------- 1.0 1.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 -1.0 0.0 0.0 0.0 1.0 % 0.0 1.0 0.0 0.0 0.0 1.0 %% 0.0 0.0 0.0 0.0 0.0 1.0 0.0 -1.0 0.0 0.0 0.0 1.0 % -1.0 1.0 0.0 0.0 0.0 1.0 -1.0 0.0 0.0 0.0 0.0 1.0 -1.0 -1.0 0.0 0.0 0.0 1.0; minc-tools-2.3.00+dfsg/progs/mincmorph/kernel_ops.c0000644000175000000620000006153212574624760021264 0ustar stevestaff/* kernel_ops.c */ #include #include #include "kernel_ops.h" extern int verbose; /* function prototypes */ void split_kernel(Kernel * K, Kernel * k1, Kernel * k2); int compare_ints(const void *a, const void *b); int compare_groups(const void *a, const void *b); /* structure for group information */ typedef struct { unsigned int orig_label; unsigned int count; } group_info_struct; typedef group_info_struct *Group_info; int compare_ints(const void *a, const void *b) { return (*(const int *)a - *(const int *)b); } int compare_groups(const void *a, const void *b) { return (*(const Group_info *) b)->count - (*(const Group_info *) a)->count; } void split_kernel(Kernel * K, Kernel * k1, Kernel * k2) { int c, k1c, k2c; /* fill the two sub kernels */ k1c = k2c = 0; for(c = 0; c < K->nelems; c++){ if((K->K[c][2] < 0) || (K->K[c][1] < 0 && K->K[c][2] <= 0) || (K->K[c][0] < 0 && K->K[c][1] <= 0 && K->K[c][2] <= 0)){ k1->K[k1c] = K->K[c]; k1c++; } else { k2->K[k2c] = K->K[c]; k2c++; } } k1->nelems = k1c; k2->nelems = k2c; } /* binarise a volume between a range */ VIO_Volume binarise(VIO_Volume vol, double floor, double ceil, double fg, double bg) { int x, y, z; int sizes[MAX_VAR_DIMS]; double value; VIO_progress_struct progress; if(verbose){ fprintf(stdout, "Binarising, range: [%g:%g] fg/bg: [%g:%g]\n", floor, ceil, fg, bg); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Binarise"); for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ value = get_volume_voxel_value(vol, z, y, x, 0, 0); if((value >= floor) && (value <= ceil)){ set_volume_voxel_value(vol, z, y, x, 0, 0, fg); } else { set_volume_voxel_value(vol, z, y, x, 0, 0, bg); } } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); return (vol); } /* clamp a volume between a range */ VIO_Volume clamp(VIO_Volume vol, double floor, double ceil, double bg) { int x, y, z; int sizes[MAX_VAR_DIMS]; double value; VIO_progress_struct progress; if(verbose){ fprintf(stdout, "Clamping, range: [%g:%g] bg: %g\n", floor, ceil, bg); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Clamping"); for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ value = get_volume_voxel_value(vol, z, y, x, 0, 0); if((value < floor) || (value > ceil)){ set_volume_voxel_value(vol, z, y, x, 0, 0, bg); } } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); return (vol); } /* pad a volume using the background value */ VIO_Volume pad(Kernel * K, VIO_Volume vol, double bg) { int x, y, z; int sizes[MAX_VAR_DIMS]; get_volume_sizes(vol, sizes); /* z */ for(y = 0; y < sizes[1]; y++){ for(x = 0; x < sizes[2]; x++){ for(z = 0; z < -K->pre_pad[2]; z++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } for(z = sizes[0] - K->post_pad[2]; z < sizes[0]; z++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } } } /* y */ for(z = 0; z < sizes[0]; z++){ for(x = 0; x < sizes[2]; x++){ for(y = 0; y < -K->pre_pad[1]; y++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } for(y = sizes[1] - K->post_pad[1]; y < sizes[1]; y++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } } } /* x */ for(z = 0; z < sizes[0]; z++){ for(y = 0; y < sizes[1]; y++){ for(x = 0; x < -K->pre_pad[0]; x++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } for(x = sizes[2] - K->post_pad[0]; x < sizes[2]; x++){ set_volume_real_value(vol, z, y, x, 0, 0, bg); } } } return (vol); } /* perform a dilation on a volume */ VIO_Volume dilation_kernel(Kernel * K, VIO_Volume vol) { int x, y, z, c; double value; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; if(verbose){ fprintf(stdout, "Dilation kernel\n"); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Dilation"); /* copy the volume */ tmp_vol = copy_volume(vol); for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ value = get_volume_real_value(tmp_vol, z, y, x, 0, 0); for(c = 0; c < K->nelems; c++){ if(get_volume_real_value(vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]) < value){ set_volume_real_value(vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4], value * K->K[c][5]); } } } } update_progress_report(&progress, z + 1); } delete_volume(tmp_vol); terminate_progress_report(&progress); return (vol); } /* perform a median kernel operation on a volume */ VIO_Volume median_dilation_kernel(Kernel * K, VIO_Volume vol) { int x, y, z, c, i; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; double value; unsigned int kvalue; unsigned int neighbours[K->nelems]; if(verbose){ fprintf(stdout, "Median Dilation kernel\n"); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Median Dilation"); /* copy the volume */ tmp_vol = copy_volume(vol); for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ /* only modify background voxels */ value = get_volume_voxel_value(tmp_vol, z, y, x, 0, 0); if(value == 0.0){ i = 0; for(c = 0; c < K->nelems; c++){ kvalue = (unsigned int)get_volume_voxel_value(tmp_vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]); if(kvalue != 0){ neighbours[i] = kvalue; i++; } } /* only run this for adjacent voxels */ if(i > 0){ /* find the median of our little array */ qsort(&neighbours[0], (size_t) i, sizeof(unsigned int), &compare_ints); /* store the median value */ set_volume_voxel_value(vol, z, y, x, 0, 0, (double)neighbours[(int)floor((i - 1) / 2)]); } } /* else just copy the original value over */ else { set_volume_voxel_value(vol, z, y, x, 0, 0, value); } } } update_progress_report(&progress, z + 1); } delete_volume(tmp_vol); terminate_progress_report(&progress); return (vol); } /* perform an erosion on a volume */ VIO_Volume erosion_kernel(Kernel * K, VIO_Volume vol) { int x, y, z, c; double value; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; if(verbose){ fprintf(stdout, "Erosion kernel\n"); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Erosion"); /* copy the volume */ tmp_vol = copy_volume(vol); for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ value = get_volume_real_value(tmp_vol, z, y, x, 0, 0); for(c = 0; c < K->nelems; c++){ if(get_volume_real_value(vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]) > value){ set_volume_real_value(vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4], value * K->K[c][5]); } } value = get_volume_real_value(tmp_vol, z, y, x, 0, 0); } } update_progress_report(&progress, z + 1); } delete_volume(tmp_vol); terminate_progress_report(&progress); return (vol); } /* convolve a volume with a input kernel */ VIO_Volume convolve_kernel(Kernel * K, VIO_Volume vol) { int x, y, z, c; double value; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; if(verbose){ fprintf(stdout, "Convolve kernel\n"); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Convolve"); /* copy the volume */ tmp_vol = copy_volume(vol); for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ value = 0; for(c = 0; c < K->nelems; c++){ value += get_volume_real_value(tmp_vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]) * K->K[c][5]; } set_volume_real_value(vol, z, y, x, 0, 0, value); } } update_progress_report(&progress, z + 1); } delete_volume(tmp_vol); terminate_progress_report(&progress); return (vol); } /* should really only work on binary images */ /* from the original 2 pass Borgefors alg */ VIO_Volume distance_kernel(Kernel * K, VIO_Volume vol, double bg) { int x, y, z, c; double value, min; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; Kernel *k1, *k2; /* split the Kernel */ k1 = new_kernel(K->nelems); k2 = new_kernel(K->nelems); split_kernel(K, k1, k2); setup_pad_values(k1); setup_pad_values(k2); if(verbose){ fprintf(stdout, "Distance kernel - background %g\n", bg); fprintf(stdout, "forward direction kernel:\n"); print_kernel(k1); fprintf(stdout, "\nreverse direction kernel:\n"); print_kernel(k2); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2] * 2, "Distance"); /* forward raster direction */ for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ if(get_volume_real_value(vol, z, y, x, 0, 0) != bg){ /* find the minimum */ min = DBL_MAX; for(c = 0; c < k1->nelems; c++){ value = get_volume_real_value(vol, z + k1->K[c][2], y + k1->K[c][1], x + k1->K[c][0], 0 + k1->K[c][3], 0 + k1->K[c][4]) + 1; if(value < min){ min = value; } } set_volume_real_value(vol, z, y, x, 0, 0, min); } } } update_progress_report(&progress, z + 1); } /* reverse raster direction */ for(z = sizes[0] - k2->post_pad[2] - 1; z >= -k2->pre_pad[2]; z--){ for(y = sizes[1] - k2->post_pad[1] - 1; y >= -k2->pre_pad[1]; y--){ for(x = sizes[2] - k2->post_pad[0] - 1; x >= -k2->pre_pad[0]; x--){ min = get_volume_real_value(vol, z, y, x, 0, 0); if(min != bg){ /* find the minimum distance to bg in the neighbouring vectors */ for(c = 0; c < k2->nelems; c++){ value = get_volume_real_value(vol, z + k2->K[c][2], y + k2->K[c][1], x + k2->K[c][0], 0 + k2->K[c][3], 0 + k2->K[c][4]) + 1; if(value < min){ min = value; } } set_volume_real_value(vol, z, y, x, 0, 0, min); } } } update_progress_report(&progress, sizes[2] + z + 1); } free(k1); free(k2); terminate_progress_report(&progress); return (vol); } /* do connected components labelling on a volume */ /* resulting groups are sorted WRT size */ VIO_Volume group_kernel(Kernel * K, VIO_Volume vol, double bg) { int x, y, z; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; Kernel *k1, *k2; unsigned int *equiv; unsigned int *counts; unsigned int *trans; unsigned int neighbours[K->nelems]; /* counters */ unsigned int c; unsigned int value; unsigned int group_idx; /* label for the next group */ unsigned int num_groups; unsigned int min_label; unsigned int curr_label; unsigned int prev_label; unsigned int num_matches; /* structure for group data */ Group_info *group_data; /* split the Kernel into forward and backwards kernels */ k1 = new_kernel(K->nelems); k2 = new_kernel(K->nelems); split_kernel(K, k1, k2); setup_pad_values(k1); setup_pad_values(k2); if(verbose){ fprintf(stdout, "Group kernel - background %g\n", bg); fprintf(stdout, "forward direction kernel:\n"); print_kernel(k1); fprintf(stdout, "\nreverse direction kernel:\n"); print_kernel(k2); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Groups"); /* copy and then zero out the original volume */ tmp_vol = copy_volume(vol); for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ set_volume_voxel_value(vol, z, y, x, 0, 0, 0); } } } /* pass 1 - forward direction (we assume a symmetric kernel) */ /* our first group is given the label 1 */ group_idx = 1; /* initialise the equiv and counts arrays */ SET_ARRAY_SIZE(equiv, 0, group_idx, 500); equiv[0] = 0; SET_ARRAY_SIZE(counts, 0, group_idx, 500); counts[0] = 0; for(z = -k1->pre_pad[2]; z < sizes[0] - k1->post_pad[2]; z++){ for(y = -k1->pre_pad[1]; y < sizes[1] - k1->post_pad[1]; y++){ for(x = -k1->pre_pad[0]; x < sizes[2] - k1->post_pad[0]; x++){ if(get_volume_voxel_value(tmp_vol, z, y, x, 0, 0) != bg){ /* search this voxels neighbours */ num_matches = 0; min_label = INT_MAX; for(c = 0; c < (unsigned) k1->nelems; c++){ value = (unsigned int)get_volume_voxel_value(vol, z + k1->K[c][2], y + k1->K[c][1], x + k1->K[c][0], 0 + k1->K[c][3], 0 + k1->K[c][4]); if(value != 0){ if(value < min_label){ min_label = value; } neighbours[num_matches] = value; num_matches++; } } switch (num_matches){ case 0: /* no neighbours, make a new label and increment */ set_volume_voxel_value(vol, z, y, x, 0, 0, (VIO_Real) group_idx); SET_ARRAY_SIZE(equiv, group_idx, group_idx + 1, 500); equiv[group_idx] = group_idx; SET_ARRAY_SIZE(counts, group_idx, group_idx + 1, 500); counts[group_idx] = 1; group_idx++; break; case 1: /* only one neighbour, no equivalences needed */ set_volume_voxel_value(vol, z, y, x, 0, 0, (VIO_Real) min_label); counts[min_label]++; break; default: /* more than one neighbour */ /* first sort the neighbours array */ qsort(&neighbours[0], (size_t) num_matches, sizeof(unsigned int), &compare_ints); /* find the minimum possible label for this voxel, */ /* this is done by descending through each neighbours */ /* equivalences until an equivalence equal to itself */ /* is found */ prev_label = -1; for(c = 0; c < num_matches; c++){ curr_label = neighbours[c]; /* recurse this label if we haven't yet */ if(curr_label != prev_label){ while(equiv[curr_label] != equiv[equiv[curr_label]]){ curr_label = equiv[curr_label]; } /* check against the current minimum value */ if(equiv[curr_label] < min_label){ min_label = equiv[curr_label]; } } prev_label = neighbours[c]; } /* repeat, setting equivalences to the min_label */ prev_label = -1; for(c = 0; c < num_matches; c++){ curr_label = neighbours[c]; if(curr_label != prev_label){ while(equiv[curr_label] != equiv[equiv[curr_label]]){ curr_label = equiv[curr_label]; equiv[curr_label] = min_label; } /* set the label itself */ if(equiv[neighbours[c]] != min_label){ equiv[neighbours[c]] = min_label; } } prev_label = neighbours[c]; } /* finally set the voxel in question to the minimum value */ set_volume_voxel_value(vol, z, y, x, 0, 0, (VIO_Real) min_label); counts[min_label]++; break; } /* end case */ } } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); /* reduce the equiv and counts array */ num_groups = 0; for(c = 0; c < group_idx; c++){ /* if this equivalence is not resolved yet */ if(c != equiv[c]){ /* find the min label value */ min_label = equiv[c]; while(min_label != equiv[min_label]){ min_label = equiv[min_label]; } /* update the label and its counters */ equiv[c] = min_label; counts[min_label] += counts[c]; counts[c] = 0; } else { num_groups++; } } /* Allocate space for the array of groups */ group_data = (Group_info *) malloc(num_groups * sizeof(Group_info)); num_groups = 0; for(c = 0; c < group_idx; c++){ if(counts[c] > 0){ /* allocate space for this element */ group_data[num_groups] = malloc(sizeof(group_info_struct)); group_data[num_groups]->orig_label = equiv[c]; group_data[num_groups]->count = counts[c]; num_groups++; } } /* sort the groups by the count size */ if(verbose){ fprintf(stdout, "Found %d unique groups from %d, sorting...\n", num_groups, group_idx); } qsort(group_data, num_groups, sizeof(Group_info), &compare_groups); /* set up the transpose array */ trans = (unsigned int *)malloc(sizeof(unsigned int) * group_idx); for(c = 0; c < num_groups; c++){ trans[group_data[c]->orig_label] = c + 1; /* +1 to bump past 0 */ } /* pass 2 - resolve equivalences in the output data */ if(verbose){ fprintf(stdout, "Resolving equivalences...\n"); } for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ value = (unsigned int)get_volume_voxel_value(vol, z, y, x, 0, 0); if(value != 0){ value = trans[equiv[value]]; set_volume_voxel_value(vol, z, y, x, 0, 0, (VIO_Real) value); } } } } /* tidy up */ delete_volume(tmp_vol); for(c = 0; c < num_groups; c++){ free(group_data[c]); } free(group_data); free(trans); free(k1); free(k2); return (vol); } /* do local correlation to another volume */ /* xcorr = sum((a*b)^2) / (sqrt(sum(a^2)) * sqrt(sum(b^2)) */ VIO_Volume lcorr_kernel(Kernel * K, VIO_Volume vol, VIO_Volume cmp) { int x, y, z, c; double value, v1, v2; double ssum_v1, ssum_v2, sum_prd, denom; int sizes[MAX_VAR_DIMS]; VIO_progress_struct progress; VIO_Volume tmp_vol; if(verbose){ fprintf(stdout, "Local Correlation kernel\n"); } get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Local Correlation"); /* copy the volume */ tmp_vol = copy_volume(vol); /* zero the output volume */ for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ set_volume_voxel_value(vol, z, y, x, 0, 0, 0); } } } /* set output range */ set_volume_real_range(vol, 0.0, 1.0); for(z = -K->pre_pad[2]; z < sizes[0] - K->post_pad[2]; z++){ for(y = -K->pre_pad[1]; y < sizes[1] - K->post_pad[1]; y++){ for(x = -K->pre_pad[0]; x < sizes[2] - K->post_pad[0]; x++){ /* init counters */ ssum_v1 = ssum_v2 = sum_prd = 0; for(c = 0; c < K->nelems; c++){ v1 = get_volume_real_value(tmp_vol, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]) * K->K[c][5]; v2 = get_volume_real_value(cmp, z + K->K[c][2], y + K->K[c][1], x + K->K[c][0], 0 + K->K[c][3], 0 + K->K[c][4]) * K->K[c][5]; /* increment counters */ ssum_v1 += v1*v1; ssum_v2 += v2*v2; sum_prd += v1*v2; } denom = sqrt(ssum_v1 * ssum_v2); value = (denom == 0.0) ? 0.0 : sum_prd / denom; set_volume_real_value(vol, z, y, x, 0, 0, value); } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); /* tidy up */ delete_volume(tmp_vol); return (vol); } minc-tools-2.3.00+dfsg/progs/mincmorph/kernel_io.c0000644000175000000620000002566412574624760021100 0ustar stevestaff/* kernel_io.c - reads kernel files */ #include #include "kernel_io.h" #define MAX_KERNEL_ELEMS 1000 extern int verbose; static const VIO_STR KERNEL_FILE_HEADER = "MNI Morphology Kernel File"; static const VIO_STR KERNEL_TYPE = "Kernel_Type"; static const VIO_STR NORMAL_KERNEL = "Normal_Kernel"; static const VIO_STR KERNEL = "Kernel"; /* inbuilt kernels */ int n_inbuilt_kern = 4; /* returns a new Kernel struct (pointer) */ Kernel *new_kernel(int nelems) { int i, j; Kernel *tmp; ALLOC(tmp, 1); if (nelems != 0) { /* allocate and initialise the Kernel Array */ SET_ARRAY_SIZE(tmp->K, 0, nelems, 10); for(i = 0; i < nelems; i++){ ALLOC(tmp->K[i], KERNEL_DIMS + 1); for(j = 0; j < KERNEL_DIMS; j++){ tmp->K[i][j] = 0.0; } tmp->K[i][KERNEL_DIMS] = 1.0; } } else { tmp->K = NULL; } tmp->nelems = nelems; return tmp; } /* delete a kernel structure properly */ void delete_kernel(Kernel *kernel) { int i; if (kernel->K != NULL) { for (i = 0; i < kernel->nelems; i++) { FREE(kernel->K[i]); } FREE(kernel->K); } FREE(kernel); } /* reads in a Kernel from a file */ VIO_Status input_kernel(const char *kernel_file, Kernel * kernel) { int i, j; VIO_STR line; VIO_STR type_name; VIO_STR str; VIO_Real tmp_real; FILE *file; /* parameter checking */ if(kernel_file == NULL){ print_error("input_kernel(): passed NULL FILE.\n"); return (VIO_ERROR); } file = fopen(kernel_file, "r"); if(file == NULL){ print_error("input_kernel(): error opening Kernel file.\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_kernel(): could not read header in file.\n"); return (VIO_ERROR); } if(!equal_strings(line, KERNEL_FILE_HEADER)){ delete_string(line); print_error("input_kernel(): invalid header in file.\n"); return (VIO_ERROR); } /* --- read the type of Kernel */ if(mni_input_keyword_and_equal_sign(file, KERNEL_TYPE, FALSE) != VIO_OK){ return (VIO_ERROR); } if(mni_input_string(file, &type_name, (char)';', (char)0) != VIO_OK){ print_error("input_kernel(): missing kernel type.\n"); return (VIO_ERROR); } if(mni_skip_expected_character(file, (char)';') != VIO_OK){ return (VIO_ERROR); } if(!equal_strings(type_name, NORMAL_KERNEL)){ print_error("input_kernel(): invalid kernel type.\n"); delete_string(type_name); 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, KERNEL)){ print_error("Expected %s =\n", KERNEL); delete_string(str); return (VIO_ERROR); } delete_string(str); if(mni_skip_expected_character(file, (char)'=') != VIO_OK){ return (VIO_ERROR); } /* now read the elements (lines) of the kernel */ if(verbose){ fprintf(stderr, "Reading [%s]", kernel_file); } for(i = 0; i < MAX_KERNEL_ELEMS; i++){ /* allocate a bit of memory */ SET_ARRAY_SIZE(kernel->K, kernel->nelems, kernel->nelems + 1, 10); ALLOC(kernel->K[i], KERNEL_DIMS + 1); /* get the 5 dimension vectors and the coefficient */ for(j = 0; j < 6; j++){ if(mni_input_real(file, &tmp_real) == VIO_OK){ kernel->K[i][j] = tmp_real; } else { /* check for end */ if(mni_skip_expected_character(file, (char)';') == VIO_OK){ kernel->nelems = i; if(verbose){ fprintf(stderr, " %dx%d Kernel elements read\n", i, kernel->nelems); } return (VIO_OK); } else { print_error("input_kernel(): error reading kernel [%d,%d]\n", i + 1, j + 1); return (VIO_ERROR); } } } kernel->nelems++; if(verbose){ fprintf(stderr, "."); fflush(stderr); } } /* SHOLDN'T BE REACHED */ print_error("input_kernel(): Glark! Something is amiss in the State of Kansas\n"); return (VIO_ERROR); } /* pretty print a kernel */ int print_kernel(Kernel * kernel) { int i, j; fprintf(stderr, " x y z t v coeff\n"); fprintf(stderr, " -----------------------------------------------\n"); for(i = 0; i < kernel->nelems; i++){ fprintf(stderr, "[%02d]", i); for(j = 0; j < KERNEL_DIMS + 1; j++){ fprintf(stderr, "%8.02f", kernel->K[i][j]); } fprintf(stderr, "\n"); } return (TRUE); } int setup_pad_values(Kernel * kernel) { int c, n; /* init padding values */ for(n = 0; n < KERNEL_DIMS; n++){ kernel->pre_pad[n] = 0; kernel->post_pad[n] = 0; } /* find the padding sizes */ for(c = 0; c < kernel->nelems; c++){ for(n = 0; n < KERNEL_DIMS; n++){ if(kernel->K[c][n] < kernel->pre_pad[n]){ kernel->pre_pad[n] = kernel->K[c][n]; } if(kernel->K[c][n] > kernel->post_pad[n]){ kernel->post_pad[n] = kernel->K[c][n]; } } } return (TRUE); } /* 2D 4 connectivity kernel */ /* x y z t v coeff */ /* ----------------------------------------------- */ /* [00] 1.00 0.00 0.00 0.00 0.00 1.00 */ /* [01] -1.00 0.00 0.00 0.00 0.00 1.00 */ /* [02] 0.00 1.00 0.00 0.00 0.00 1.00 */ /* [03] 0.00 -1.00 0.00 0.00 0.00 1.00 */ Kernel *get_2D04_kernel(void) { Kernel *K = new_kernel(4); K->K[0][0] = 1.0; K->K[1][0] = -1.0; K->K[2][1] = 1.0; K->K[3][1] = -1.0; return K; } /* 2D 8 connectivity kernel */ /* x y z t v coeff */ /* ----------------------------------------------- */ /* [00] 1.00 1.00 0.00 0.00 0.00 1.00 */ /* [01] 1.00 0.00 0.00 0.00 0.00 1.00 */ /* [02] 1.00 -1.00 0.00 0.00 0.00 1.00 */ /* */ /* [03] 0.00 1.00 0.00 0.00 0.00 1.00 */ /* [04] 0.00 -1.00 0.00 0.00 0.00 1.00 */ /* */ /* [05] -1.00 1.00 0.00 0.00 0.00 1.00 */ /* [06] -1.00 0.00 0.00 0.00 0.00 1.00 */ /* [07] -1.00 -1.00 0.00 0.00 0.00 1.00 */ Kernel *get_2D08_kernel(void) { Kernel *K = new_kernel(8); K->K[0][0] = 1.0; K->K[0][1] = 1.0; K->K[1][0] = 1.0; K->K[2][0] = 1.0; K->K[2][1] = -1.0; K->K[3][1] = 1.0; K->K[4][1] = -1.0; K->K[5][0] = -1.0; K->K[5][1] = 1.0; K->K[6][0] = -1.0; K->K[7][0] = -1.0; K->K[7][1] = -1.0; return K; } /* 3D 6 connectivity kernel */ /* x y z t v coeff */ /* ----------------------------------------------- */ /* [00] 1.00 0.00 0.00 0.00 0.00 1.00 */ /* [01] -1.00 0.00 0.00 0.00 0.00 1.00 */ /* [02] 0.00 1.00 0.00 0.00 0.00 1.00 */ /* [03] 0.00 -1.00 0.00 0.00 0.00 1.00 */ /* [04] 0.00 0.00 1.00 0.00 0.00 1.00 */ /* [05] 0.00 0.00 -1.00 0.00 0.00 1.00 */ Kernel *get_3D06_kernel(void) { Kernel *K = new_kernel(6); K->K[0][0] = 1.0; K->K[1][0] = -1.0; K->K[2][1] = 1.0; K->K[3][1] = -1.0; K->K[4][2] = 1.0; K->K[5][2] = -1.0; return K; } /* 3D 26 connectivity kernel */ /* x y z t v coeff */ /* ----------------------------------------------- */ /* [00] 1.00 1.00 1.00 0.00 0.00 1.00 */ /* [01] 1.00 1.00 0.00 0.00 0.00 1.00 */ /* [02] 1.00 1.00 -1.00 0.00 0.00 1.00 */ /* [03] 1.00 0.00 1.00 0.00 0.00 1.00 */ /* [04] 1.00 0.00 0.00 0.00 0.00 1.00 */ /* [05] 1.00 0.00 -1.00 0.00 0.00 1.00 */ /* [06] 1.00 -1.00 1.00 0.00 0.00 1.00 */ /* [07] 1.00 -1.00 0.00 0.00 0.00 1.00 */ /* [08] 1.00 -1.00 -1.00 0.00 0.00 1.00 */ /* [09] 0.00 1.00 1.00 0.00 0.00 1.00 */ /* [10] 0.00 1.00 0.00 0.00 0.00 1.00 */ /* [11] 0.00 1.00 -1.00 0.00 0.00 1.00 */ /* [12] 0.00 0.00 1.00 0.00 0.00 1.00 */ /* ---- 0.00 0.00 0.00 0.00 0.00 ---- */ /* [13] 0.00 0.00 -1.00 0.00 0.00 1.00 */ /* [14] 0.00 -1.00 1.00 0.00 0.00 1.00 */ /* [15] 0.00 -1.00 0.00 0.00 0.00 1.00 */ /* [16] 0.00 -1.00 -1.00 0.00 0.00 1.00 */ /* [17] -1.00 1.00 1.00 0.00 0.00 1.00 */ /* [18] -1.00 1.00 0.00 0.00 0.00 1.00 */ /* [19] -1.00 1.00 -1.00 0.00 0.00 1.00 */ /* [20] -1.00 0.00 1.00 0.00 0.00 1.00 */ /* [21] -1.00 0.00 0.00 0.00 0.00 1.00 */ /* [22] -1.00 0.00 -1.00 0.00 0.00 1.00 */ /* [23] -1.00 -1.00 1.00 0.00 0.00 1.00 */ /* [24] -1.00 -1.00 0.00 0.00 0.00 1.00 */ /* [25] -1.00 -1.00 -1.00 0.00 0.00 1.00 */ Kernel *get_3D26_kernel(void) { Kernel *K = new_kernel(26); K->K[0][0] = 1.0; K->K[0][1] = 1.0; K->K[0][2] = 1.0; K->K[1][0] = 1.0; K->K[1][1] = 1.0; K->K[2][0] = 1.0; K->K[2][1] = 1.0; K->K[2][2] = -1.0; K->K[3][0] = 1.0; K->K[3][2] = 1.0; K->K[4][0] = 1.0; K->K[5][0] = 1.0; K->K[5][2] = -1.0; K->K[6][0] = 1.0; K->K[6][1] = -1.0; K->K[6][2] = 1.0; K->K[7][0] = 1.0; K->K[7][1] = -1.0; K->K[8][0] = 1.0; K->K[8][1] = -1.0; K->K[8][2] = -1.0; K->K[9][1] = 1.0; K->K[9][2] = 1.0; K->K[10][1] = 1.0; K->K[11][1] = 1.0; K->K[11][2] = -1.0; K->K[12][2] = 1.0; K->K[13][2] = -1.0; K->K[14][1] = -1.0; K->K[14][2] = 1.0; K->K[15][1] = -1.0; K->K[16][1] = -1.0; K->K[16][2] = -1.0; K->K[17][0] = -1.0; K->K[17][1] = 1.0; K->K[17][2] = 1.0; K->K[18][0] = -1.0; K->K[18][1] = 1.0; K->K[19][0] = -1.0; K->K[19][1] = 1.0; K->K[19][2] = -1.0; K->K[20][0] = -1.0; K->K[20][2] = 1.0; K->K[21][0] = -1.0; K->K[22][0] = -1.0; K->K[22][2] = -1.0; K->K[23][0] = -1.0; K->K[23][1] = -1.0; K->K[23][2] = 1.0; K->K[24][0] = -1.0; K->K[24][1] = -1.0; K->K[25][0] = -1.0; K->K[25][1] = -1.0; K->K[25][2] = -1.0; return K; } minc-tools-2.3.00+dfsg/progs/mincmorph/mincmorph.c0000644000175000000620000005414212574624760021116 0ustar stevestaff/* mincmorph.c Copyright 2006-2012 Andrew Janke - a.janke@gmail.com 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 the 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 #include #include #include #include #include #include #include "kernel_io.h" #include "kernel_ops.h" #define INTERNAL_PREC NC_FLOAT /* should be NC_FLOAT or NC_DOUBLE */ #define DEF_DOUBLE -DBL_MAX #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif /* function prototypes */ char *get_real_from_string(char *string, double *value); char *get_string_from_string(char *string, char **value); void calc_volume_range(VIO_Volume vol, double *min, double *max); /* kernel names for pretty output */ char *KERN_names[] = { "NULL", "2D04", "2D08", "3D06", "3D26" }; /* typedefs */ typedef enum { UNDEF = 0, BINARISE, CLAMP, PAD, ERODE, DILATE, MDILATE, OPEN, CLOSE, LPASS, HPASS, CONVOLVE, DISTANCE, GROUP, READ_KERNEL, WRITE, LCORR } op_types; typedef struct { op_types type; char op_c; char *kernel_fn; kern_types kernel_id; char *cmpfile; char *outfile; double range[2]; double foreground; double background; } Operation; /* Argument variables */ int verbose = FALSE; int clobber = FALSE; int is_signed = FALSE; nc_type dtype = NC_SHORT; double range[2] = { -DBL_MAX, DBL_MAX }; double foreground = 1.0; double background = 0.0; kern_types kernel_id = K_NULL; char *kernel_fn = NULL; char *succ_txt = "B"; char successive_help[] = "Successive operations (Maximum: 100) \ \n\tB[floor:ceil:fg:bg] - binarise in the range, using foreground and background \ \n\tK[floor:ceil:bg] - clamp betwen the specified range. Set other voxels to 'bg' (default: 0) \ \n\tP[bg] - pad volume with respect to the current kernel using 'bg' (default: 0)\ \n\tE - erosion \ \n\tD - dilation \ \n\tM - median dilation \ \n\tO - open \ \n\tC - close \ \n\tL - lowpass filter \ \n\tH - highpass filter \ \n\tX - convolve \ \n\tF - distance transform (binary input only - not checked) \ \n\tG - Label the groups in the volume in ascending order \ \n\tR[TYPE|file.kern] - (2D04|2D08|3D06|3D26) or read in a kernel file \ \n\tW[file.mnc] - write out current results \ \n\tI[cmp.mnc] - local xcorr between current file and cmp.mnc \ \n\tDefault: "; /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "General options:"}, {"-verbose", ARGV_CONSTANT, (char *)TRUE, (char *)&verbose, "be verbose"}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "clobber existing files"}, {NULL, ARGV_HELP, NULL, NULL, "\nOutfile Options"}, {"-filetype", ARGV_CONSTANT, (char *)NC_UNSPECIFIED, (char *)&dtype, "Use data type of the input file."}, {"-byte", ARGV_CONSTANT, (char *)NC_BYTE, (char *)&dtype, "Write out byte data."}, {"-short", ARGV_CONSTANT, (char *)NC_SHORT, (char *)&dtype, "Write out short integer data. (Default)"}, {"-int", ARGV_CONSTANT, (char *)NC_INT, (char *)&dtype, "Write out long integer data."}, {"-float", ARGV_CONSTANT, (char *)NC_FLOAT, (char *)&dtype, "Write out single-precision data."}, {"-double", ARGV_CONSTANT, (char *)NC_DOUBLE, (char *)&dtype, "Write out double-precision data."}, {"-signed", ARGV_CONSTANT, (char *)TRUE, (char *)&is_signed, "Write signed integer data."}, {"-unsigned", ARGV_CONSTANT, (char *)FALSE, (char *)&is_signed, "Write unsigned integer data."}, {NULL, ARGV_HELP, NULL, NULL, "\nKernel Options"}, {"-2D04", ARGV_CONSTANT, (char *)K_2D04, (char *)&kernel_id, "Use a 2D 4-connectivity kernel."}, {"-2D08", ARGV_CONSTANT, (char *)K_2D08, (char *)&kernel_id, "Use a 2D 8-connectivity kernel."}, {"-3D06", ARGV_CONSTANT, (char *)K_3D06, (char *)&kernel_id, "Use a 3D 6-connectivity kernel. (default)"}, {"-3D26", ARGV_CONSTANT, (char *)K_3D26, (char *)&kernel_id, "Use a 3D 26-connectivity kernel."}, {"-kernel", ARGV_STRING, (char *)1, (char *)&kernel_fn, " read in a custom kernel file"}, {NULL, ARGV_HELP, NULL, NULL, "\nMorphology Options"}, {"-floor", ARGV_FLOAT, (char *)1, (char *)&range[0], "lowwer value for binarising or clamping"}, {"-ceil", ARGV_FLOAT, (char *)1, (char *)&range[1], "upper value for binarising or clamping (incl)"}, {"-range", ARGV_FLOAT, (char *)2, (char *)range, "range for binarising or clamping (incl)"}, {"-foreground", ARGV_FLOAT, (char *)1, (char *)&foreground, "foreground value"}, {"-background", ARGV_FLOAT, (char *)1, (char *)&background, "background value"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nSingle morphological operations:"}, {"-binarise", ARGV_CONSTANT, (char *)"B", (char *)&succ_txt, "binarise volume using the input range"}, {"-clamp", ARGV_CONSTANT, (char *)"K", (char *)&succ_txt, "clamp volume using the input range"}, {"-pad", ARGV_CONSTANT, (char *)"P", (char *)&succ_txt, "pad volume with respect to the current kernel (-background to specify value)"}, {"-erosion", ARGV_CONSTANT, (char *)"E", (char *)&succ_txt, "do a single erosion"}, {"-dilation", ARGV_CONSTANT, (char *)"D", (char *)&succ_txt, "do a single dilation"}, {"-median_dilation", ARGV_CONSTANT, (char *)"M", (char *)&succ_txt, "do a single median dilation (note: this is not a median filter!)"}, {"-open", ARGV_CONSTANT, (char *)"O", (char *)&succ_txt, "open: dilation(erosion(X))"}, {"-close", ARGV_CONSTANT, (char *)"C", (char *)&succ_txt, "close: erosion(dilation(X))"}, {"-lowpass", ARGV_CONSTANT, (char *)"L", (char *)&succ_txt, "lowpass filter: close(open(X))"}, {"-highpass", ARGV_CONSTANT, (char *)"H", (char *)&succ_txt, "highpass filter: X - lowpass(X)"}, {"-convolve", ARGV_CONSTANT, (char *)"X", (char *)&succ_txt, "convolve file with kernel"}, {"-distance", ARGV_CONSTANT, (char *)"F", (char *)&succ_txt, "distance transform"}, {"-group", ARGV_CONSTANT, (char *)"G", (char *)&succ_txt, "label groups in ascending order"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nSuccessive morphological operations:"}, {"-successive", ARGV_STRING, (char *)1, (char *)&succ_txt, successive_help}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { int c; char *arg_string; char *infile; char *outfile; VIO_Volume volume; VIO_Volume cmpvol; Kernel *kernel; int num_ops; Operation operation[100]; Operation *op; char *tmp_str; char ext_txt[256]; char tmp_filename[MAXPATHLEN]; double tmp_double[4]; double min, max; char *ptr; char *axis_order[VIO_MAX_DIMENSIONS] = { MIzspace, MIyspace, MIxspace, MItime, MIvector_dimension }; /* Save time stamp and args */ arg_string = time_stamp(argc, argv); /* Get arguments */ if(ParseArgv(&argc, argv, argTable, 0) || (argc < 2)){ fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* check for the infile */ if(access(infile, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find %s\n\n", argv[0], infile); exit(EXIT_FAILURE); } /* check for the outfile */ if(access(outfile, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists! (use -clobber to overwrite)\n\n", argv[0], outfile); exit(EXIT_FAILURE); } /* check kernel args */ if(kernel_fn != NULL && kernel_id != K_NULL){ fprintf(stderr, "%s: specify either a kernel file or a set kernel (not both)\n\n", argv[0]); exit(EXIT_FAILURE); } /* set the default kernel */ if(kernel_fn == NULL && kernel_id == K_NULL){ kernel_id = K_3D06; } /* add the implicit read kernel operation */ num_ops = 0; op = &operation[num_ops++]; op->type = READ_KERNEL; op->kernel_fn = kernel_fn; op->kernel_id = kernel_id; /* setup operations and check them... */ if(verbose){ fprintf(stdout, "---Checking Operation(s): %s---\n", succ_txt); } ptr = succ_txt; while(ptr[0] != '\0'){ /* set up counters and extra text */ strcpy(ext_txt, ""); op = &operation[num_ops++]; /* get the operation type */ op->op_c = ptr[0]; ptr++; switch (op->op_c){ case 'B': op->type = BINARISE; /* get 4 possible values */ ptr = get_real_from_string(ptr, &tmp_double[0]); ptr = get_real_from_string(ptr, &tmp_double[1]); ptr = get_real_from_string(ptr, &tmp_double[2]); ptr = get_real_from_string(ptr, &tmp_double[3]); op->range[0] = (tmp_double[0] == DEF_DOUBLE) ? range[0] : tmp_double[0]; op->range[1] = (tmp_double[1] == DEF_DOUBLE) ? range[1] : tmp_double[1]; op->foreground = (tmp_double[2] == DEF_DOUBLE) ? foreground : tmp_double[2]; op->background = (tmp_double[3] == DEF_DOUBLE) ? background : tmp_double[3]; sprintf(ext_txt, "range: [%g:%g] fg/bg: [%g:%g]", op->range[0], op->range[1], op->foreground, op->background); break; case 'K': op->type = CLAMP; /* get 3 possible values */ ptr = get_real_from_string(ptr, &tmp_double[0]); ptr = get_real_from_string(ptr, &tmp_double[1]); ptr = get_real_from_string(ptr, &tmp_double[2]); op->range[0] = (tmp_double[0] == DEF_DOUBLE) ? range[0] : tmp_double[0]; op->range[1] = (tmp_double[1] == DEF_DOUBLE) ? range[1] : tmp_double[1]; op->background = (tmp_double[2] == DEF_DOUBLE) ? background : tmp_double[2]; sprintf(ext_txt, "range: [%g:%g] fg/bg: [%g:%g]", op->range[0], op->range[1], op->foreground, op->background); break; case 'P': op->type = PAD; /* get 1 possible value */ ptr = get_real_from_string(ptr, &tmp_double[0]); op->background = (tmp_double[0] == DEF_DOUBLE) ? background : tmp_double[0]; sprintf(ext_txt, "fill value: %g", op->background); break; case 'E': op->type = ERODE; break; case 'D': op->type = DILATE; break; case 'M': op->type = MDILATE; break; case 'O': op->type = OPEN; break; case 'C': op->type = CLOSE; break; case 'L': op->type = LPASS; break; case 'H': op->type = HPASS; break; case 'X': op->type = CONVOLVE; break; case 'F': op->type = DISTANCE; break; case 'G': op->type = GROUP; break; case 'R': op->type = READ_KERNEL; /* get the filename */ ptr = get_string_from_string(ptr, &tmp_str); if(tmp_str == NULL){ fprintf(stderr, "%s: R[TYPE|file.kern] requires a file or kernel name\n\n", argv[0]); exit(EXIT_FAILURE); } /* check if the input kernel is an inbuilt one */ op->kernel_id = K_NULL; for(c = 1; c <= n_inbuilt_kern; c++){ if(strcmp(tmp_str, KERN_names[c]) == 0){ op->kernel_id = (kern_types) c; } } /* if no inbuilt found, assume it's a file */ if(op->kernel_id == K_NULL){ /* set up and check for the real filename */ if(!realpath(tmp_str, tmp_filename) || access(tmp_filename, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find kernel file: %s\n\n", argv[0], tmp_filename); exit(EXIT_FAILURE); } op->kernel_fn = strdup(tmp_filename); sprintf(ext_txt, "kernel_fn: %s", op->kernel_fn); } else { sprintf(ext_txt, "inbuilt_kernel[%d]: %s", op->kernel_id, KERN_names[op->kernel_id]); } break; case 'W': op->type = WRITE; /* get the filename */ ptr = get_string_from_string(ptr, &op->outfile); if(op->outfile == NULL){ fprintf(stderr, "%s: W[file.mnc] _requires_ a filename\n\n", argv[0]); exit(EXIT_FAILURE); } /* check for the outfile */ if(access(op->outfile, F_OK) == 0 && !clobber){ fprintf(stderr, "%s: %s exists! (use -clobber to overwrite)\n\n", argv[0], op->outfile); exit(EXIT_FAILURE); } sprintf(ext_txt, "filename: %s", op->outfile); break; case 'I': op->type = LCORR; /* get the filenames */ ptr = get_string_from_string(ptr, &op->cmpfile); if(op->cmpfile == NULL){ fprintf(stderr, "%s: I[cmp.mnc] requires a filename\n\n", argv[0]); exit(EXIT_FAILURE); } /* check for cmpfile */ if(access(op->cmpfile, F_OK) != 0){ fprintf(stderr, "%s: Couldn't find compare file: %s\n\n", argv[0], op->cmpfile); exit(EXIT_FAILURE); } sprintf(ext_txt, "compare filename: %s", op->cmpfile); break; default: fprintf(stderr, "\nUnknown op: %c\n\n %s -help for operations\n\n", op->op_c, argv[0]); exit(EXIT_FAILURE); } if(verbose){ fprintf(stdout, " Op[%02d] %c = %d\t\t%s\n", num_ops, op->op_c, op->type, ext_txt); } } /* add an implicit write statment to the end if needed */ if(operation[num_ops - 1].type != WRITE){ operation[num_ops].type = WRITE; operation[num_ops].outfile = outfile; num_ops++; } /* read input */ if (input_volume(infile, VIO_MAX_DIMENSIONS, axis_order, INTERNAL_PREC, TRUE, 0.0, 0.0, TRUE, &volume, NULL) != VIO_OK){ exit(EXIT_FAILURE); } get_type_range(get_volume_data_type(volume), &min, &max); set_volume_real_range(volume, min, max); /* init and then do some operations */ kernel = new_kernel(0); if(verbose){ fprintf(stdout, "\n---Doing %d Operation(s)---\n", num_ops); } for(c = 0; c < num_ops; c++){ op = &operation[c]; switch (op->type){ case BINARISE: volume = binarise(volume, op->range[0], op->range[1], op->foreground, op->background); break; case CLAMP: volume = clamp(volume, op->range[0], op->range[1], op->background); break; case PAD: volume = pad(kernel, volume, op->background); break; case ERODE: volume = erosion_kernel(kernel, volume); break; case DILATE: volume = dilation_kernel(kernel, volume); break; case MDILATE: volume = median_dilation_kernel(kernel, volume); break; case OPEN: volume = erosion_kernel(kernel, volume); volume = dilation_kernel(kernel, volume); break; case CLOSE: volume = dilation_kernel(kernel, volume); volume = erosion_kernel(kernel, volume); break; case LPASS: volume = erosion_kernel(kernel, volume); volume = dilation_kernel(kernel, volume); volume = dilation_kernel(kernel, volume); volume = erosion_kernel(kernel, volume); break; case HPASS: fprintf(stderr, "%s: GNFARK! Highpass Not implemented yet..\n\n", argv[0]); break; case CONVOLVE: volume = convolve_kernel(kernel, volume); break; case DISTANCE: volume = distance_kernel(kernel, volume, background); break; case GROUP: volume = group_kernel(kernel, volume, background); break; case READ_KERNEL: /* free the existing kernel then set the pointer to the new one */ delete_kernel(kernel); /* read in the kernel or set the kernel to an inbuilt one */ if(op->kernel_id == K_NULL){ kernel = new_kernel(0); if(input_kernel(op->kernel_fn, kernel) != VIO_OK){ fprintf(stderr, "%s: Died reading in kernel file: %s\n\n", argv[0], op->kernel_fn); exit(EXIT_FAILURE); } } else { switch (op->kernel_id){ case K_2D04: kernel = get_2D04_kernel(); break; case K_2D08: kernel = get_2D08_kernel(); break; case K_3D06: kernel = get_3D06_kernel(); break; case K_3D26: kernel = get_3D26_kernel(); break; default: fprintf(stderr, "%s: This shouldn't happen -- much bad\n\n", argv[0]); exit(EXIT_FAILURE); } } setup_pad_values(kernel); if(verbose){ fprintf(stdout, "Input kernel:\n"); print_kernel(kernel); } break; case WRITE: if(op->outfile == NULL){ fprintf(stdout, "%s: WRITE passed a NULL pointer! - this is bad\n\n", argv[0]); exit(EXIT_FAILURE); } if(verbose){ fprintf(stdout, "Outputting to %s\n", op->outfile); } /* get the resulting range */ calc_volume_range(volume, &min, &max); /* set the range to something sensible (if possible) */ if(dtype == NC_BYTE && is_signed == FALSE){ if(min >= 0 && max < 255){ fprintf(stdout, "BYTE data, setting 1:1 mapping (0-256)\n"); min = 0; max = 255; } } set_volume_real_range(volume, min, max); output_modified_volume(op->outfile, dtype, is_signed, 0.0, 0.0, volume, infile, arg_string, NULL); free(arg_string); arg_string = NULL; break; case LCORR: if(op->cmpfile == NULL){ fprintf(stdout, "%s: LCORR passed a NULL pointer! - this is bad\n\n", argv[0]); exit(EXIT_FAILURE); } if(verbose){ fprintf(stdout, "Comparing to %s\n", op->cmpfile); } /* read cmpfile */ if (input_volume(op->cmpfile, VIO_MAX_DIMENSIONS, axis_order, INTERNAL_PREC, TRUE, 0.0, 0.0, TRUE, &cmpvol, NULL) != VIO_OK) { exit(EXIT_FAILURE); } /* run the local correlation */ volume = lcorr_kernel(kernel, volume, cmpvol); /* clean up */ delete_volume(cmpvol); break; default: fprintf(stderr, "\n%s: Unknown operation (This is very bad, call Houston)\n\n", argv[0]); exit(EXIT_FAILURE); } } /* jump through operations freeing stuff */ delete_kernel(kernel); delete_volume(volume); return (EXIT_SUCCESS); } /* get a real from a char* stream */ /* with possible trailing or leading square bracket */ /* and possible leading ':' */ /* return the string advanced to the next token or */ /* as it was input if nothing found */ char *get_real_from_string(char *string, double *value) { char *ptr; /* skip a [ or : else we probably don't belong here */ if(string[0] == '[' || string[0] == ':'){ string++; } /* get a double */ *value = strtod(&string[0], &ptr); /* if nothing found return a default value */ if(&string[0] == ptr){ *value = DEF_DOUBLE; } /* skip over a possible ']' */ if(ptr[0] == ']'){ ptr++; } return ptr; } /* get a copy of the string between [ and ] from a char* */ /* return the string advanced to the next token or */ /* as it was input if nothing found */ char *get_string_from_string(char *string, char **value) { int offset; char *malloc_string; /* initialise the return values first */ *value = NULL; /* get the string if there is one */ if(string[0] == '['){ string++; /* get the length of the string in question */ offset = strcspn(string, "]"); /* alloc some space for the string */ malloc_string = (char *)malloc((offset + 1) * sizeof(char)); /* get a copy of it and append the null charater */ strncpy(malloc_string, string, offset); malloc_string[offset] = '\0'; /* store the result */ *value = malloc_string; /* increment the pointer and skip over a possible '[' */ string += offset; if(string[0] == ']'){ string++; } } return string; } void calc_volume_range(VIO_Volume vol, double *min, double *max) { int x, y, z; int sizes[VIO_MAX_DIMENSIONS]; double value; VIO_progress_struct progress; *min = DBL_MAX; *max = -DBL_MIN; get_volume_sizes(vol, sizes); initialize_progress_report(&progress, FALSE, sizes[2], "Finding Range"); for(z = sizes[0]; z--;){ for(y = sizes[1]; y--;){ for(x = sizes[2]; x--;){ value = get_volume_voxel_value(vol, z, y, x, 0, 0); if(value < *min){ *min = value; } else if(value > *max){ *max = value; } } } update_progress_report(&progress, z + 1); } terminate_progress_report(&progress); if (*min == *max) { *max = *min + 1.0; } if(verbose){ fprintf(stdout, "Found range of [%g:%g]\n", *min, *max); } } minc-tools-2.3.00+dfsg/progs/mincmorph/README0000644000175000000620000000102612574624760017627 0ustar stevestaffmincmorph - morphological operations 101 Comments to: Andrew Janke - a.janke@gmail.com Much of the theory behind what this does can be found at these web sites.. http://www.mmorph.com/faq.htm http://www.dai.ed.ac.uk/HIPR2/skeleton.htm SNF - http://www.umiacs.umd.edu/research/EXPAR/papers/3449/node7.html#SECTION00041000000000000000 CC8 - http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/OWENS/LECT3/node2.html - http://www.dai.ed.ac.uk/HIPR2/label.htm HOW TO USE IT: mincmorph -help for more information minc-tools-2.3.00+dfsg/progs/mincconcat/0002755000175000000620000000000012574624760017074 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincconcat/mincconcat.man10000644000175000000620000001321112574624760021764 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .\" Copyright 1995 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. .\" .\" $Header: /private-cvsroot/minc/progs/mincconcat/mincconcat.man1,v 6.4 2005-07-15 17:38:08 bert Exp $ .\" .TH MINCCONCAT 1 "$Date: 2005-07-15 17:38:08 $" "" "MINC User's Guide" .SH NAME mincconcat - concatenate minc files along a specific dimension .SH SYNOPSIS .B mincconcat [] .mnc [.mnc ...] .mnc .SH DESCRIPTION \fIMincconcat\fR will concatenate a number of minc files together, producing a single output file. The concatenation is done along a specified dimension, with the slices being sorted into ascending order. The concatenation dimension can either be a dimension in the file, in which case coordinates for sorting are taken directly from the input files, or it can be a new dimension and the coordinates are specified with a command-line option. .SH OPTIONS Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH General options .TP \fB\-2\fR Create a MINC 2.0 format output file. .TP \fB\-clobber\fR Overwrite an existing file. .TP \fB\-noclobber\fR Don't overwrite an existing file (default). .TP \fB\-verbose\fR Print out progress information for each chunk of data copied (default). .TP \fB\-quiet\fR Do not print out progress information. .TP \fB\-max_chunk_size_in_kb\fR \fIsize\fR Specify the maximum size of the copy buffer (in kbytes). Default is 4096 kbytes. .TP \fB\-filelist\fR \fIfilename\fR Specify a file containing a list of input file names. If "-" is given, then file names are read from stdin. If this option is given, then there should be no input file names specified on the command line. Empty lines in the input file are ignored. .SH Output type options .TP \fB\-filetype\fR Don't do any type conversion (default). .TP \fB\-byte\fR Write out 8-bit integer voxels. .TP \fB\-short\fR Write out 16-bit integer voxels. .TP \fB-int\fR Write out 32-bit integer voxels. .TP \fB\-long\fR Superseded by -int. .TP \fB\-float\fR Write out single-precision floating point values. .TP \fB\-double\fR Write out double-precision floating point values. .TP \fB\-signed\fR Write out values as signed integers (default for short and long). Ignored for floating point types. .TP \fB\-unsigned\fR Write out values as unsigned integers (default for byte). Ignored for floating point types. .TP \fB\-valid_range\fR \fImin max\fR Specifies the valid range of output voxel values in their integer representation. Default is the full range for the type and sign. This option is ignored for floating point values. .SH Concatenation options .TP \fB\-concat_dimension\fR \fIname\fR Specifies the name of concatenation dimension. If the dimension exists in the input files, then coordinates are taken from those files. If not, then a new dimension is created and the coordinate for each input file is taken from command-line options. The default is to use the slowest varying dimension of the first file. .TP \fB\-start\fR \fIstart\fR Specifies the starting coordinate for the new dimension (default = 0.0). .TP \fB\-step\fR \fIstep\fR Specifies the separation between voxels for the new dimension (default = 1.0). .TP \fB\-width\fR \fIwidth\fR Specifies the (constant) width of each sample along the new dimension (default = none). .TP \fB\-coordlist\fR \fIc1,c2,...\fR Specifies a comma-separated list of coordinates along the new dimension. .TP \fB\-widthlist\fR \fIw1,w2,...\fR Specifies a comma-separated list of widths along the new dimension. .TP \fB\-filestarts\fR \fIs1,s2,...\fR Specifies a comma-separated list of offsets to the coordinate origins for each of the files listed on the command line. This option is useful for concatenating files along an existing dimension, for example for concatenating multiple functional runs along a .B time dimension. .TP \fB\-check_dimensions\fR Check that all input files have matching sampling in world dimensions (default). .TP \fB\-nocheck_dimensions\fR Ignore any differences between input files in world dimensions sampling. .TP \fB\-ascending\fR Sort coordinates in ascending order (default). .TP \fB\-descending\fR Sort coordinates in descending order. .TP \fB\-interleaved\fR Sort slabs by their dimension coordinate, interleaving if necessary (default). .TP \fB\-sequential\fR Don't sort slabs, just concatenate them together. WARNING - this will destroy the dimension information along the concatenating dimension, replacing the start and step with zero and one. .SH Generic options for all commands: .TP \fB-help\fR Print summary of command-line options and exit. .TP \fB\-version\fR Print the program's version number and exit. .SH EXAMPLES To concatenate two volumes with dimensions zspace, yspace, xspace, having interleaved slices along zspace, we can simply use mincconcat input1.mnc input2.mnc output.mnc If we have a bunch of compressed (yspace, xspace) images that we wish to concatenate into an evenly spaced volume, then we can type mincconcat input1.mnc.gz input2.mnc.gz input3.mnc.gz \\ input4.mnc.gz output.mnc \\ -concat_dimension zspace -start -23 -step 2 .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1995 by Peter Neelin minc-tools-2.3.00+dfsg/progs/mincconcat/mincconcat.c0000644000175000000620000015016412574624760021363 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincconcat @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Program to allow concatentation of multiple minc files, either adding a new dimension, or expanding a dimension of size 1. @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1995 (Peter Neelin) @MODIFIED : * $Log: mincconcat.c,v $ * Revision 6.15 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.14 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.13 2005/08/26 21:07:17 bert * Use #if rather than #ifdef with MINC2 symbol, and be sure to include config.h whereever MINC2 is used * * Revision 6.12 2005/07/15 17:38:08 bert * Add -filestarts option * * Revision 6.11 2004/12/14 23:52:08 bert * Get rid of compilation warnings w/c99 * * Revision 6.10 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.9 2004/04/27 15:37:13 bert * Added -2 flag * * Revision 6.8 2001/09/18 15:32:39 neelin * Create image variable last to allow big images and to fix compatibility * problems with 2.3 and 3.x. * * Revision 6.7 2001/08/16 16:41:33 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.6 2001/08/16 13:32:33 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.5 2001/04/24 13:38:42 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.4 2001/04/17 18:40:17 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.3 2000/07/07 13:33:34 neelin * Added option -filelist to read file names from a file. This gets around * command-line length limits. * * Revision 6.2 1999/10/19 14:45:19 neelin * Fixed Log subsitutions for CVS * * Revision 6.1 1998/08/13 19:34:37 neelin * Always create concatenation coordinate variable subscription by * dimension. * * Revision 6.0 1997/09/12 13:24:15 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:13 neelin * Release of minc version 0.5 * * Revision 4.1 1997/06/03 14:57:23 neelin * Really fixed dimension width name suffixes. * * Revision 4.0 1997/05/07 20:01:54 neelin * Release of minc version 0.4 * * Revision 3.4 1997/04/21 20:28:45 neelin * Changed width suffix from _width to -width. * * Revision 3.3 1996/04/11 19:31:43 neelin * Added -sequential and -interleaved options. * * Revision 3.2 1995/11/16 13:18:16 neelin * Added include of math.h to get declaration of strtod under SunOs * * Revision 3.1 1995/09/29 12:59:06 neelin * Fixed bug in handling of image-min/max when these variables are not * present in the input file. * * Revision 3.0 1995/05/15 19:32:40 neelin * Release of minc version 0.3 * * Revision 1.1 1995/05/11 12:35:53 neelin * Initial revision * @COPYRIGHT : Copyright 1995 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define COORD_EPSILON (100.0 * FLT_EPSILON) #define DIM_WIDTH_SUFFIX "-width" /* Default ncopts values for error handling */ #define NC_OPTS_VAL NC_VERBOSE | NC_FATAL /* Macros */ #define ABS(x) ( ((x) > 0) ? (x) : (-(x)) ) /* Double_Array structure */ typedef struct { int numvalues; double *values; } Double_Array; /* Concat_info structure */ typedef struct { char *output_file; int num_input_files; char *history; int cflags; int verbose; nc_type output_datatype; int output_is_signed; double output_valid_range[2]; char *dimension_name; int *num_file_coords; int **file_to_dim_order; double **file_coords; double **file_widths; /* Array of NULL pointers if no widths */ double *file_offsets; /* Per-file offsets from start of dimension */ int coords_specified; int dimension_in_input_file; int concat_dimension_length; int regular_spacing; int constant_width; int have_widths; double dim_step; double dim_start; int output_mincid, output_icvid; int is_floating_type; double global_minimum; double global_maximum; long max_memory_use_in_kb; int check_dim_info; } Concat_Info; /* Sort structure */ typedef struct { double coord; int curfile; int curcoord; } Sort_Element; /* Function prototypes */ static void get_arginfo(int argc, char *argv[], int *num_input_files, char ***input_files, Concat_Info *concat_info); static int get_double_list(char *dst, char *key, char *nextarg); static void get_concat_dim_name(Concat_Info *concat_info, char *first_filename, int *first_mincid); static void get_input_file_info(void *caller_data, int input_mincid, int input_curfile, Loop_Info *loop_info); static int get_image_dimension_id(int input_mincid, char *dimension_name); static void do_concat(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); static void sort_coords(Concat_Info *concat_info); static int sort_function(const void *value1, const void *value2); static void create_concat_file(int inmincid, Concat_Info *concat_info); static void update_history(int mincid, char *arg_string); /* Globals */ static int Sort_ascending = TRUE; static int Sort_sequential = FALSE; /* Main program */ int main(int argc, char *argv[]) { Concat_Info *concat_info; Loop_Options *loop_options; int num_input_files; char **input_files; int first_mincid, imgid; double valid_range[2]; /* Allocate the concat_info structure */ concat_info = malloc(sizeof(*concat_info)); /* Get argument information */ get_arginfo(argc, argv, &num_input_files, &input_files, concat_info); /* Look for the dimension in the input file */ get_concat_dim_name(concat_info, input_files[0], &first_mincid); /* Set up loop options */ loop_options = create_loop_options(); set_loop_verbose(loop_options, concat_info->verbose); set_loop_first_input_mincid(loop_options, first_mincid); set_loop_input_file_function(loop_options, get_input_file_info); set_loop_accumulate(loop_options, TRUE, 0, NULL, NULL); if (concat_info->dimension_in_input_file) { set_loop_dimension(loop_options, concat_info->dimension_name); } set_loop_buffer_size(loop_options, 1024 * concat_info->max_memory_use_in_kb); set_loop_check_dim_info(loop_options, concat_info->check_dim_info); /* Initialize global min and max */ concat_info->global_minimum = DBL_MAX; concat_info->global_maximum = -DBL_MAX; /* Loop over files */ voxel_loop(num_input_files, input_files, 0, NULL, NULL, loop_options, do_concat, concat_info); /* Close the output file */ imgid = ncvarid(concat_info->output_mincid, MIimage); (void) miattputstr(concat_info->output_mincid, imgid, MIcomplete, MI_TRUE); if (concat_info->is_floating_type) { if ((concat_info->global_minimum == DBL_MAX) && (concat_info->global_maximum == -DBL_MAX)) { concat_info->global_minimum = 0.0; concat_info->global_maximum = 1.0; } valid_range[0] = concat_info->global_minimum; valid_range[1] = concat_info->global_maximum; (void) miset_valid_range(concat_info->output_mincid, imgid, valid_range); } (void) miclose(concat_info->output_mincid); (void) miicv_free(concat_info->output_icvid); /* Free stuff */ free_loop_options(loop_options); free(concat_info); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_arginfo @INPUT : argc - number of command-line arguments argv - command-line arguments @OUTPUT : num_input_files - number of input files input_files - array of strings containing input file names concat_info - information for concatenating files @RETURNS : (nothing) @DESCRIPTION: Routine to get information from arguments about input and output files and concatenation. @METHOD : @GLOBALS : @CALLS : @CREATED : March 11, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_arginfo(int argc, char *argv[], int *num_input_files, char ***input_files, Concat_Info *concat_info) { /* Argument variables */ static int clobber = FALSE; #if MINC2 static int minc2_format = FALSE; #endif /* MINC2 */ static int verbose = TRUE; static nc_type datatype = MI_ORIGINAL_TYPE; static int is_signed = INT_MIN; static double valid_range[2] = {0.0, 0.0}; static char *dimension_name = NULL; static double dimension_start = DBL_MAX; static double dimension_step = DBL_MAX; static double dimension_width = DBL_MAX; static Double_Array dimension_coords = {0, NULL}; static Double_Array dimension_widths = {0, NULL}; static Double_Array file_offsets = {0, NULL}; static int max_chunk_size_in_kb = 4 * 1024; static int check_dim_info = TRUE; static char *filelist = NULL; /* Argument table */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "General options:"}, #if MINC2 {"-2", ARGV_CONSTANT, (char *) TRUE, (char *) &minc2_format, "Produce a MINC 2.0 format output file"}, #endif /* MINC2 */ {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Do not overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Print out log messages as processing is being done (default).\n"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not print out any log messages."}, {"-max_chunk_size_in_kb", ARGV_INT, (char *) 1, (char *) &max_chunk_size_in_kb, "Specify the maximum size of the copy buffer (in kbytes)."}, {"-filelist", ARGV_STRING, (char *) 1, (char *) &filelist, "Specify the name of a file containing input file names (- for stdin)."}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Output type options:"}, {"-filetype", ARGV_CONSTANT, (char *) MI_ORIGINAL_TYPE, (char *) &datatype, "Don't do any type conversion (default)."}, {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *) &datatype, "Convert to byte data"}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *) &datatype, "Convert to short integer data"}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Convert to 32-bit integer data"}, {"-long", ARGV_CONSTANT, (char *) NC_INT, (char *) &datatype, "Superseded by -int"}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *) &datatype, "Convert to single-precision floating-point data"}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *) &datatype, "Convert to double-precision floating-point data"}, {"-signed", ARGV_CONSTANT, (char *) TRUE, (char *) &is_signed, "Convert to signed integer data"}, {"-unsigned", ARGV_CONSTANT, (char *) FALSE, (char *) &is_signed, "Convert to unsigned integer data"}, {"-valid_range", ARGV_FLOAT, (char *) 2, (char *) valid_range, "Valid range for output data (pixel values)"}, {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Concatenation options:"}, {"-concat_dimension", ARGV_STRING, (char *) 1, (char *) &dimension_name, "Concatenate along a given dimension."}, {"-start", ARGV_FLOAT, (char *) 1, (char *) &dimension_start, "Starting coordinate for new dimension."}, {"-step", ARGV_FLOAT, (char *) 1, (char *) &dimension_step, "Step size for new dimension."}, {"-width", ARGV_FLOAT, (char *) 1, (char *) &dimension_width, "Sample width for new dimension."}, {"-coordlist", ARGV_FUNC, (char *) get_double_list, (char *) &dimension_coords, "Specify the dimension coordinates (\",,...\")."}, {"-widthlist", ARGV_FUNC, (char *) get_double_list, (char *) &dimension_widths, "Specify the dimension widths (\",,...\")."}, {"-filestarts", ARGV_FUNC, (char *) get_double_list, (char *) &file_offsets, "Specify the start offset of each file (\",...\")."}, {"-check_dimensions", ARGV_CONSTANT, (char *) TRUE, (char *) &check_dim_info, "Check that files have matching dimensions (default)."}, {"-nocheck_dimensions", ARGV_CONSTANT, (char *) FALSE, (char *) &check_dim_info, "Do not check that files have matching dimensions."}, {"-ascending", ARGV_CONSTANT, (char *) TRUE, (char *) &Sort_ascending, "Sort coordinates in ascending order (default)."}, {"-descending", ARGV_CONSTANT, (char *) FALSE, (char *) &Sort_ascending, "Sort coordinates in descending order."}, {"-interleaved", ARGV_CONSTANT, (char *) FALSE, (char *) &Sort_sequential, "Sort coordinates in coordinate order, interleaving if necessary (default)."}, {"-sequential", ARGV_CONSTANT, (char *) TRUE, (char *) &Sort_sequential, "Sort coordinates in sequential file order."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Local variables */ char *output_file; char *history; char *pname; char **infiles; int nfiles; int ifile; /* Get the history information and program name */ history = time_stamp(argc, argv); pname = argv[0]; /* Call ParseArgv */ if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { (void) fprintf(stderr, "\nUsage: %s [] [ ...] \n", pname); (void) fprintf(stderr, " %s [-help]\n\n", pname); exit(EXIT_FAILURE); } output_file = argv[argc-1]; /* Get the list of input files either from the command line or from a file, or report an error if both are specified */ nfiles = argc - 2; if (filelist == NULL) { infiles = &argv[1]; } else if (nfiles <= 0) { infiles = read_file_names(filelist, &nfiles); if (infiles == NULL) { (void) fprintf(stderr, "Error reading in file names from file \"%s\"\n", filelist); exit(EXIT_FAILURE); } } else { (void) fprintf(stderr, "Do not specify both -filelist and input file names\n"); exit(EXIT_FAILURE); } /* Make sure that we have something to process */ if (nfiles == 0) { (void) fprintf(stderr, "No input files specified\n"); exit(EXIT_FAILURE); } /* Save the input file names */ *num_input_files = nfiles; *input_files = infiles; /* Check that a dimension name was given if coords are specified */ concat_info->coords_specified = ((dimension_start != DBL_MAX) || (dimension_step != DBL_MAX) || (dimension_width != DBL_MAX) || (dimension_coords.numvalues > 0) || (dimension_widths.numvalues > 0)); if ((dimension_name == NULL) && concat_info->coords_specified) { (void) fprintf(stderr, "A dimension name must be specified if dimension coords are given.\n"); exit(EXIT_FAILURE); } /* Check that step and start are given */ if (((dimension_start != DBL_MAX) && (dimension_step == DBL_MAX)) || ((dimension_start == DBL_MAX) && (dimension_step != DBL_MAX))) { (void) fprintf(stderr, "Both dimension start and step must be given.\n"); exit(EXIT_FAILURE); } /* Check for either regular or irregular spacing of coords */ if ((dimension_step != DBL_MAX) && (dimension_coords.numvalues > 0)) { (void) fprintf(stderr, "Specify either dimension step or coords.\n"); exit(EXIT_FAILURE); } /* Check for either regular or irregular widths */ if ((dimension_width != DBL_MAX) && (dimension_widths.numvalues > 0)) { (void) fprintf(stderr, "Specify either dimension width or width list.\n"); exit(EXIT_FAILURE); } /* Check that we have the same number of coords and widths */ if ((dimension_coords.numvalues > 0) && (dimension_widths.numvalues > 0) && (dimension_coords.numvalues != dimension_widths.numvalues)) { (void) fprintf(stderr, "Specify the same number of coordinates and widths.\n"); exit(EXIT_FAILURE); } /* Check that we have the same number of coordinates and files */ if ((dimension_coords.numvalues > 0) && (dimension_coords.numvalues != *num_input_files)) { (void) fprintf(stderr, "Number of coordinates does not match number of input files.\n"); exit(EXIT_FAILURE); } if ((file_offsets.numvalues > 0) && (file_offsets.numvalues != *num_input_files)) { (void) fprintf(stderr, "Number of file offsets does not match number of input files.\n"); exit(EXIT_FAILURE); } /* Set defaults for start and step */ if (dimension_start == DBL_MAX) dimension_start = 0; if (dimension_step == DBL_MAX) dimension_step = 1; /* Save the appropriate values in the concat_info structure */ concat_info->output_file = output_file; concat_info->num_input_files = *num_input_files; concat_info->history = history; if (clobber) { concat_info->cflags = NC_CLOBBER; } else { concat_info->cflags = NC_NOCLOBBER; } #if MINC2 if (minc2_format) { concat_info->cflags |= MI2_CREATE_V2; } #endif /* MINC2 */ concat_info->verbose = verbose; concat_info->max_memory_use_in_kb = max_chunk_size_in_kb; concat_info->check_dim_info = check_dim_info; concat_info->output_datatype = datatype; concat_info->output_is_signed = is_signed; concat_info->output_valid_range[0] = valid_range[0]; concat_info->output_valid_range[1] = valid_range[1]; if (dimension_name != NULL) concat_info->dimension_name = strdup(dimension_name); else concat_info->dimension_name = NULL; concat_info->output_mincid = MI_ERROR; concat_info->output_icvid = MI_ERROR; /* Fill in coordinate info. We allocate space even if coordinates aren't specified just in case we can't get the info from the files. */ concat_info->num_file_coords = malloc(sizeof(int) * (*num_input_files)); concat_info->file_coords = malloc(sizeof(void *) * (*num_input_files)); concat_info->file_widths = malloc(sizeof(void *) * (*num_input_files)); concat_info->file_offsets = malloc(sizeof(double) * (*num_input_files)); for (ifile=0; ifile < *num_input_files; ifile++) { concat_info->num_file_coords[ifile] = 1; concat_info->file_coords[ifile] = malloc(sizeof(double)); concat_info->file_coords[ifile][0] = ((dimension_coords.numvalues > 0) ? dimension_coords.values[ifile] : dimension_start + dimension_step * ifile); if ((dimension_widths.numvalues > 0) || (dimension_width != DBL_MAX)) { concat_info->file_widths[ifile] = malloc(sizeof(double)); concat_info->file_widths[ifile][0] = ((dimension_widths.numvalues > 0) ? dimension_widths.values[ifile] : dimension_width); } else concat_info->file_widths[ifile] = NULL; concat_info->file_offsets[ifile] = (ifile < file_offsets.numvalues) ? file_offsets.values[ifile] : 0.0; } } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static 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; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_concat_dim_name @INPUT : concat_info - concatenation information first_filename - name of first input file @OUTPUT : first_mincid - mincid for first input file @RETURNS : (nothing) @DESCRIPTION: Routine to get the name of the concatenation dimension, if necessary. @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_concat_dim_name(Concat_Info *concat_info, char *first_filename, int *first_mincid) { char *filename; int created_tempfile; int input_mincid, imgid, dimid; int ndims, dim[MAX_VAR_DIMS], min_ndims; char dimname[MAX_NC_NAME]; /* Expand the file header and open the file */ filename = miexpand_file(first_filename, NULL, TRUE, &created_tempfile); if (!filename) { fprintf(stderr, "Could not expand file \"%s\"!\n", first_filename); exit(EXIT_FAILURE); } input_mincid = miopen(filename, NC_NOWRITE); if (created_tempfile) { (void) remove(filename); } free(filename); *first_mincid = input_mincid; /* Do we have to get the dimension name from the file? */ if (concat_info->dimension_name == NULL) { concat_info->dimension_in_input_file = TRUE; imgid = ncvarid(input_mincid, MIimage); (void) ncvarinq(input_mincid, imgid, NULL, NULL, &ndims, dim, NULL); min_ndims = 3; if (ndims > 0) { (void) ncdiminq(input_mincid, dim[ndims-1], dimname, NULL); if (strcmp(dimname, MIvector_dimension) == 0) min_ndims++; } if (ndims < min_ndims) { (void) fprintf(stderr, "Cannot concatentate along image dimensions.\n"); exit(EXIT_FAILURE); } (void) ncdiminq(input_mincid, dim[0], dimname, NULL); concat_info->dimension_name = strdup(dimname); } /* If the dimension name is given, then check the file */ else { dimid = get_image_dimension_id(input_mincid, concat_info->dimension_name); concat_info->dimension_in_input_file = (dimid != MI_ERROR); } /* If the dimension is in the file, then check that no coordinate info was given on the command line */ if (concat_info->dimension_in_input_file && concat_info->coords_specified) { (void) fprintf(stderr, "Do not specify coordinates for a dimension in the input files.\n"); exit(EXIT_SUCCESS); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_input_file_info @INPUT : caller_data - pointer to concat_info structure input_mincid - id of input minc file input_curfile - number of current input file loop_info - pointer to structure containing loop information @OUTPUT : caller_data - updated concat_info structure @RETURNS : (nothing) @DESCRIPTION: Routine to get information from each input file. @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_input_file_info(void *caller_data, int input_mincid, int input_curfile, Loop_Info *loop_info) { Concat_Info *concat_info; int varid, dimid; int ndims, dim[MAX_VAR_DIMS]; char dimname[MAX_NC_NAME], string[MI_MAX_ATTSTR_LEN]; long dimlength; int regular; double dimstart, dimstep, dimwidth; long index; /* Get the concat_info structure pointer */ concat_info = (Concat_Info *) caller_data; /* Check that the concatenation dimension is present */ dimid = get_image_dimension_id(input_mincid, concat_info->dimension_name); if ((concat_info->dimension_in_input_file && (dimid == MI_ERROR)) || (!concat_info->dimension_in_input_file && (dimid != MI_ERROR))) { (void) fprintf(stderr, "Concatenation dimension is not present in all files.\n"); exit(EXIT_FAILURE); } /* Get information on the concatenation dimension coordinates */ if (concat_info->dimension_in_input_file) { /* Allocate the arrays */ (void) ncdiminq(input_mincid, dimid, dimname, &dimlength); concat_info->num_file_coords[input_curfile] = dimlength; free(concat_info->file_coords[input_curfile]); concat_info->file_coords[input_curfile] = malloc(sizeof(double) * dimlength); /* Set defaults */ if (!Sort_sequential || (input_curfile < 1)) { dimstart = 0; } else { index = concat_info->num_file_coords[input_curfile-1] - 1; dimstart = concat_info->file_coords[input_curfile-1][index] + 1; } dimstep = 1; regular = TRUE; /* Look for dimension variable */ ncopts = 0; varid = ncvarid(input_mincid, dimname); if (!Sort_sequential && (varid != MI_ERROR)) { /* Find out if its regular or irregular */ (void) ncvarinq(input_mincid, varid, NULL, NULL, &ndims, dim, NULL); regular = !((ndims == 1) && (dim[0] = dimid)); if (miattgetstr(input_mincid, varid, MIspacing, sizeof(string), string) != NULL) { if (strcmp(string, MI_IRREGULAR) == 0) regular = FALSE; else if (strcmp(string, MI_REGULAR) == 0) regular = TRUE; } /* Expand whole file if irregular dimension */ if (!regular) { ncopts = NC_OPTS_VAL; input_mincid = get_info_whole_file(loop_info); ncopts = 0; } /* Otherwise just get start and step */ else { (void) miattget1(input_mincid, varid, MIstart, NC_DOUBLE, &dimstart); (void) miattget1(input_mincid, varid, MIstep, NC_DOUBLE, &dimstep); } } /* If dimension variable exists */ /* Loop through indices, getting values */ for (index=0; index < dimlength; index++) { concat_info->file_coords[input_curfile][index] = dimstart + dimstep * index; if (!regular) { (void) mivarget1(input_mincid, varid, &index, NC_DOUBLE, NULL, &concat_info->file_coords[input_curfile][index]); } concat_info->file_coords[input_curfile][index] += concat_info->file_offsets[input_curfile]; } /* Look for dimension width variable */ (void) strcat(dimname, DIM_WIDTH_SUFFIX); varid = ncvarid(input_mincid, dimname); if (varid != MI_ERROR) { /* Set default width */ dimwidth = dimstep; /* Find out if its regular or irregular */ (void) ncvarinq(input_mincid, varid, NULL, NULL, &ndims, dim, NULL); regular = !((ndims == 1) && (dim[0] = dimid)); if (miattgetstr(input_mincid, varid, MIspacing, sizeof(string), string) != NULL) { if (strcmp(string, MI_IRREGULAR) == 0) regular = FALSE; else if (strcmp(string, MI_REGULAR) == 0) regular = TRUE; } /* Expand whole file if irregular dimension */ if (!regular) { ncopts = NC_OPTS_VAL; input_mincid = get_info_whole_file(loop_info); ncopts = 0; } /* Otherwise just get width */ else { (void) miattget1(input_mincid, varid, MIwidth, NC_DOUBLE, &dimwidth); } /* Allocate space for widths */ concat_info->file_widths[input_curfile] = malloc(sizeof(double) * dimlength); /* Loop through indices, getting values */ for (index=0; index < dimlength; index++) { concat_info->file_widths[input_curfile][index] = dimwidth; if (!regular) { (void) mivarget1(input_mincid, varid, &index, NC_DOUBLE, NULL, &concat_info->file_widths[input_curfile][index]); } } } /* If dimension width variable exists */ ncopts = NC_OPTS_VAL; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_image_dimension_id @INPUT : input_mincid - id of input minc file dimension_name - name of dimension @OUTPUT : (none) @RETURNS : id of dimension in image variable or MI_ERROR if dimension does not subscript the image. @DESCRIPTION: Routine to get the id of a dimension in a minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_image_dimension_id(int input_mincid, char *dimension_name) { int dimid, ndims, dim[MAX_VAR_DIMS], idim; int found; ncopts = 0; dimid = ncdimid(input_mincid, dimension_name); ncopts = NC_OPTS_VAL; if (dimid == MI_ERROR) return MI_ERROR; /* Get image variable info */ (void) ncvarinq(input_mincid, ncvarid(input_mincid, 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 dimid; else return MI_ERROR; } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_concat @INPUT : caller_data - pointer to concat_info structure num_voxels - number of voxels to handle input_num_buffers - number of input buffers input_vector_length - number of input vector elements to handle input_data - array of pointers to input buffers output_num_buffers - number of output buffers output_vector_length - number of output vector elements to handle loop_info - pointer to loop info structure @OUTPUT : output_data - array of pointers to output buffers @RETURNS : (nothing) @DESCRIPTION: Routine to do the concatenation work @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void do_concat(void *caller_data, long num_voxels, int input_num_buffers, int input_vector_length, double *input_data[], int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info) /* ARGSUSED */ { Concat_Info *concat_info; int input_mincid, output_mincid, outimgid, inimgid, varid, invarid; int ifile; int icoord; long mindex; char dimname[MAX_NC_NAME]; long instart[MAX_VAR_DIMS], incount[MAX_VAR_DIMS]; long outstart[MAX_VAR_DIMS], outcount[MAX_VAR_DIMS]; long mmstart[MAX_VAR_DIMS]; int inndims, indim[MAX_VAR_DIMS], dimid; int idim, odim, imm; char *varname; double value; /* Check that the arguments are as expected */ if ((input_num_buffers != 1) || (output_num_buffers != 0)) { (void) fprintf(stderr, "Unexpected arguments to do_concat!!!\n"); exit(EXIT_FAILURE); } /* Cast caller_data into concat_info pointer */ concat_info = (Concat_Info *) caller_data; /* Get the mincid for the current input file */ input_mincid = get_info_current_mincid(loop_info); ifile = get_info_current_file(loop_info); icoord = get_info_current_index(loop_info); inimgid = ncvarid(input_mincid, MIimage); /* Check if output file has been created */ if (concat_info->output_mincid == MI_ERROR) { /* Sort the coords */ sort_coords(concat_info); /* Create the output file */ create_concat_file(input_mincid, concat_info); } /* Get output file info */ output_mincid = concat_info->output_mincid; mindex = concat_info->file_to_dim_order[ifile][icoord]; outimgid = ncvarid(output_mincid, MIimage); /* Write out the coordinates info */ varid = ncvarid(output_mincid, concat_info->dimension_name); (void) mivarput1(output_mincid, varid, &mindex, NC_DOUBLE, NULL, &concat_info->file_coords[ifile][icoord]); if (concat_info->have_widths) { (void) strcat(strcpy(dimname, concat_info->dimension_name), DIM_WIDTH_SUFFIX); varid = ncvarid(output_mincid, dimname); (void) mivarput1(output_mincid, varid, &mindex, NC_DOUBLE, NULL, &concat_info->file_widths[ifile][icoord]); } /* Convert the input shape info into output shape info */ get_info_shape(loop_info, MAX_VAR_DIMS, instart, incount); outstart[0] = mindex; outcount[0] = 1; odim = 1; (void) ncvarinq(input_mincid, inimgid, NULL, NULL, &inndims, indim, NULL); if (concat_info->dimension_in_input_file) dimid = ncdimid(input_mincid, concat_info->dimension_name); else dimid = MI_ERROR; for (idim=0; idim < inndims; idim++) { if (indim[idim] != dimid) { outstart[odim] = instart[idim]; outcount[odim] = incount[idim]; odim++; } } /* Copy the image max and min info from the input file */ for (imm=0; imm < 2; imm++) { if (imm == 0) { varname = MIimagemin; value = 0.0; } else { varname = MIimagemax; value = 1.0; } ncopts = 0; invarid = ncvarid(input_mincid, varname); ncopts = NC_OPTS_VAL; if (invarid != MI_ERROR) { (void) mitranslate_coords(input_mincid, inimgid, instart, invarid, mmstart); (void) mivarget1(input_mincid, invarid, mmstart, NC_DOUBLE, NULL, &value); } varid = ncvarid(output_mincid, varname); (void) mitranslate_coords(output_mincid, outimgid, outstart, varid, mmstart); (void) mivarput1(output_mincid, varid, mmstart, NC_DOUBLE, NULL, &value); /* Save global min and max */ if (value < concat_info->global_minimum) concat_info->global_minimum = value; if (value > concat_info->global_maximum) concat_info->global_maximum = value; } /* Copy the data */ (void) miicv_put(concat_info->output_icvid, outstart, outcount, input_data[0]); } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_coords @INPUT : concat_info - pointer to structure containing concat info @OUTPUT : concat_info - ordering information initialized (file_to_dim_order) @RETURNS : (nothing) @DESCRIPTION: Routine to do sort the coordinates and store the appropriate information. @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void sort_coords(Concat_Info *concat_info) { int ifile; Sort_Element *sort_list; int concat_dimension_length; int index, icoord; double mean_step, mean_width, diff; int regular_spacing, constant_width, have_widths; /* Allocate space for the ordering information */ concat_info->file_to_dim_order = malloc(sizeof(void *) * (concat_info->num_input_files)); concat_dimension_length = 0; for (ifile=0; ifile < concat_info->num_input_files; ifile++) { concat_dimension_length += concat_info->num_file_coords[ifile]; concat_info->file_to_dim_order[ifile] = malloc(sizeof(int) * concat_info->num_file_coords[ifile]); } concat_info->concat_dimension_length = concat_dimension_length; /* Set up sorting stuff */ sort_list = malloc(sizeof(Sort_Element) * concat_dimension_length); index = 0; for (ifile=0; ifile < concat_info->num_input_files; ifile++) { for (icoord=0; icoord < concat_info->num_file_coords[ifile]; icoord++) { sort_list[index].coord = concat_info->file_coords[ifile][icoord]; sort_list[index].curfile = ifile; sort_list[index].curcoord = icoord; index++; } } /* Do sort */ qsort(sort_list, (size_t) concat_dimension_length, sizeof(Sort_Element), sort_function); /* Store results of sort and figure out whether we have a regular spacing and a constant width */ if (concat_dimension_length > 1) { mean_step = (sort_list[concat_dimension_length-1].coord - sort_list[0].coord) / (double) (concat_dimension_length - 1); } else { mean_step = 1; } if (concat_info->file_widths[0] != NULL) mean_width = concat_info->file_widths[0][0]; regular_spacing = TRUE; constant_width = TRUE; have_widths = TRUE; for (index=0; index < concat_dimension_length; index++) { ifile = sort_list[index].curfile; icoord = sort_list[index].curcoord; if ((ifile < 0) || (ifile >= concat_info->num_input_files) || (icoord < 0) || (icoord >= concat_info->num_file_coords[ifile])) { (void) fprintf(stderr, "Serious sorting bug!!!!\n"); exit(EXIT_FAILURE); } concat_info->file_to_dim_order[ifile][icoord] = index; if (concat_info->file_widths[ifile] == NULL) have_widths = FALSE; if (index > 0) { diff = (sort_list[index].coord - sort_list[index-1].coord) - mean_step; if (mean_step != 0) diff = diff / mean_step; diff = ABS(diff); if (diff > COORD_EPSILON) regular_spacing = FALSE; if (have_widths) { diff = (concat_info->file_widths[ifile][icoord] - mean_width); if (mean_width != 0) diff = diff / mean_width; diff = ABS(diff); if (diff > COORD_EPSILON) constant_width = FALSE; } } } concat_info->regular_spacing = regular_spacing; concat_info->constant_width = constant_width; concat_info->have_widths = have_widths; concat_info->dim_step = mean_step; concat_info->dim_start = sort_list[0].coord; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_function @INPUT : value1, value2 - two values to compare @OUTPUT : (none) @RETURNS : -1, 0, 1 for value1 <, ==, > value2 @DESCRIPTION: Routine to do do comparisons of Sort_Elements for qsort. @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int sort_function(const void *value1, const void *value2) { const Sort_Element *element1, *element2; int return_value; element1 = (const Sort_Element *) value1; element2 = (const Sort_Element *) value2; if (element1->coord < element2->coord) return_value = -1; else if (element1->coord > element2->coord) return_value = 1; else if (element1->curfile < element2->curfile) return_value = -1; else if (element1->curfile > element2->curfile) return_value = 1; else if (element1->curcoord < element2->curcoord) return_value = -1; else if (element1->curcoord > element2->curcoord) return_value = 1; else return_value = 0; if (!Sort_ascending) return_value *= -1; return return_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_concat_file @INPUT : inmincid - id of sample input file concat_info - pointer to structure containing concat info @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to create the output concatentated minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : March 9, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void create_concat_file(int inmincid, Concat_Info *concat_info) { int outmincid, outimgid, inimgid, coordid, widthid, invarid; int maxid, minid, icvid; int in_ndims, indim[MAX_VAR_DIMS], idim; int out_ndims, outdim[MAX_VAR_DIMS], out_nimgdims; int nexcluded, excluded_vars[20]; nc_type datatype; char dimname[MAX_NC_NAME]; long dimlength; double valid_range[2]; /* Create the file */ outmincid = micreate(concat_info->output_file, concat_info->cflags); /* Get image variable id for input file */ inimgid = ncvarid(inmincid, MIimage); /* Get the list of dimensions subscripting the image variable */ (void) ncvarinq(inmincid, inimgid, NULL, &datatype, &in_ndims, indim, NULL); /* Figure out how many image dimensions we have */ out_nimgdims = 2; if (in_ndims < out_nimgdims) { (void) fprintf(stderr, "Not enough dimensions in input file.\n"); exit(EXIT_FAILURE); } (void) ncdiminq(inmincid, indim[in_ndims-1], dimname, NULL); if (strcmp(dimname, MIvector_dimension) == 0) out_nimgdims++; /* Set up the output minc file */ /* Create the concatenation dimension */ out_ndims = 0; outdim[out_ndims] = ncdimdef(outmincid, concat_info->dimension_name, (long) concat_info->concat_dimension_length); ncopts = 0; coordid = micreate_std_variable(outmincid, concat_info->dimension_name, NC_DOUBLE, 1, &outdim[out_ndims]); ncopts = NC_OPTS_VAL; if (coordid == MI_ERROR) coordid = ncvardef(outmincid, concat_info->dimension_name, NC_DOUBLE, 1, &outdim[out_ndims]); ncopts = 0; invarid = ncvarid(inmincid, concat_info->dimension_name); if (invarid != MI_ERROR) (void) micopy_all_atts(inmincid, invarid, outmincid, coordid); ncopts = NC_OPTS_VAL; (void) miattputstr(outmincid, coordid, MIspacing, (concat_info->regular_spacing ? MI_REGULAR : MI_IRREGULAR)); (void) miattputdbl(outmincid, coordid, MIstart, concat_info->dim_start); (void) miattputdbl(outmincid, coordid, MIstep, concat_info->dim_step); if (concat_info->have_widths) { (void) strcat(strcpy(dimname, concat_info->dimension_name), DIM_WIDTH_SUFFIX); ncopts = 0; widthid = micreate_std_variable(outmincid, dimname, NC_DOUBLE, 1, &outdim[out_ndims]); ncopts = NC_OPTS_VAL; if (widthid == MI_ERROR) widthid = ncvardef(outmincid, dimname, NC_DOUBLE, 1, &outdim[out_ndims]); ncopts = 0; invarid = ncvarid(inmincid, dimname); if (invarid != MI_ERROR) (void) micopy_all_atts(inmincid, invarid, outmincid, widthid); (void) ncattdel(outmincid, widthid, MIwidth); ncopts = NC_OPTS_VAL; (void) miattputstr(outmincid, widthid, MIspacing, (concat_info->constant_width ? MI_REGULAR : MI_IRREGULAR)); if (concat_info->constant_width) (void) miattputdbl(outmincid, widthid, MIwidth, concat_info->file_widths[0][0]); } out_ndims++; /* Loop, creating output dimensions */ for (idim=0; idim < in_ndims; idim++) { /* Get the dimension info from the input file */ (void) ncdiminq(inmincid, indim[idim], dimname, &dimlength); /* Check that this is not the concat dimension */ if (strcmp(dimname, concat_info->dimension_name) != 0) { /* Copy the dimension */ outdim[out_ndims] = ncdimdef(outmincid, dimname, dimlength); out_ndims++; } } /* Copy other variables in file */ nexcluded = 0; ncopts = 0; excluded_vars[nexcluded] = inimgid; if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemax); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemin); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; (void) strcpy(dimname, concat_info->dimension_name); excluded_vars[nexcluded] = ncvarid(inmincid, dimname); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; (void) strcat(dimname, DIM_WIDTH_SUFFIX); excluded_vars[nexcluded] = ncvarid(inmincid, dimname); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; (void) micopy_all_var_defs(inmincid, outmincid, nexcluded, excluded_vars); ncopts = NC_OPTS_VAL; /* Add the time stamp to the history */ update_history(outmincid, concat_info->history); /* 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 (concat_info->output_datatype != MI_ORIGINAL_TYPE) { datatype = concat_info->output_datatype; } concat_info->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 (concat_info->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); } if (concat_info->output_datatype != MI_ORIGINAL_TYPE) { if (concat_info->output_is_signed) (void) miattputstr(outmincid, outimgid, MIsigntype, MI_SIGNED); else (void) miattputstr(outmincid, outimgid, MIsigntype, MI_UNSIGNED); if (concat_info->output_valid_range[1] > concat_info->output_valid_range[0]) { (void) miset_valid_range(outmincid, outimgid, concat_info->output_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; (void) micopy_all_var_values(inmincid, outmincid, nexcluded, excluded_vars); ncopts = NC_OPTS_VAL; /* Create the icv and attach it */ icvid = miicv_create(); (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_attach(icvid, outmincid, outimgid); /* Return the file and icv id's */ concat_info->output_mincid = outmincid; concat_info->output_icvid = icvid; } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static void update_history(int mincid, char *arg_string) { nc_type datatype; int att_length; char *string; /* 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); 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); } minc-tools-2.3.00+dfsg/progs/mincdiff/0002755000175000000620000000000012574624760016535 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincdiff/mincdiff.man10000644000175000000620000000216712574624760021076 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH MINCDIFF 1 "$Date: 2004-05-20 21:52:08 $" "" "MINC User's Guide" .SH NAME mincdiff \- report differences between minc files .SH SYNOPSIS .B mincdiff .BI [-header|-body] .BI [-l] .BI [diff\ options] .BI file1 .BI file2 .SH DESCRIPTION The \fImincdiff\fR shell script compares two minc files by running \fBdiff(1)\fR on the headers of the two minc files, and \fBcmp(1)\fR on the image variable. You can view only the header differences using \fB\-header\fR or only the body (image variable) differences using \fB\-body\fR. The option \fB\-l\fR is passed on to \fBcmp\fR of the image variable. Any unrecognized options (e.g. -u) are passed verbatim to the \fBdiff\fR of the headers. .SH OPTIONS .TP \fB\-header\fR Compare only the headers of the two files. .TP \fB\-body\fR Compare only the image data of the two files. .TP \fB\-l\fR Print the byte offset in decimal and the byte value in octal for each difference encountered in the image variable. .SH AUTHOR Peter Neelin .SH COPYRIGHTS Copyright \(co 1993 by Peter Neelin .SH "SEE ALSO" .IR diff (1), .IR cmp (1). minc-tools-2.3.00+dfsg/progs/mincdiff/mincdiff0000755000175000000620000000605012574624760020241 0ustar stevestaff#! /bin/sh # # Script to find differences between minc files # # Usage: mincdiff [-l] [diff options] # # Modifications: # $Log: mincdiff,v $ # Revision 6.3 2000-09-12 15:22:06 neelin # Removed copyright notice. # # Revision 6.2 2000/09/12 15:11:04 neelin # Rewrite in Bourne shell by Steve Robbins. Addition of -header and -body # options, plus allowing of diff options. # # Revision 6.1 1999/10/19 14:45:20 neelin # Fixed Log subsitutions for CVS # # Revision 6.0 1997/09/12 13:23:46 neelin # Release of minc version 0.6 # # Revision 5.0 1997/08/21 13:24:47 neelin # Release of minc version 0.5 # # Revision 4.0 1997/05/07 20:01:01 neelin # Release of minc version 0.4 # # Revision 3.0 1995/05/15 19:31:40 neelin # Release of minc version 0.3 # # Revision 2.1 1995/01/24 09:12:29 neelin # Added support for compressed minc files. # # Revision 2.0 94/09/28 10:34:46 neelin # Release of minc version 0.2 # # Revision 1.4 94/09/28 10:34:44 neelin # Pre-release # # Revision 1.3 94/07/18 10:41:26 neelin # Added -nonormalize option to mincextract for comparing image data. # # Revision 1.2 93/11/01 16:50:03 neelin # Some fixes. # die () { echo >&2 $@ exit 1 } header_diff=yes header_diff_opt= body_cmp=yes body_cmp_opt= while test $# -gt 2; do case $1 in -header) header_diff=yes; body_cmp=no ;; -body) header_diff=no; body_cmp=yes ;; -l) body_cmp_opt=-l ;; -*) header_diff_opt="$header_diff_opt $1" ;; *) break; esac shift done test $# -eq 2 || die "Usage: $0 [-header|-body] [-l] [diff options] " if test x$TMPDIR = x; then for TMPDIR in /usr/tmp /var/tmp /tmp; do test -d $TMPDIR && break; done fi test -d $TMPDIR || die "TMPDIR $TMPDIR does not exist." # Files created tmp1="$TMPDIR/mincdiff-$$-tmp1" tmp2="$TMPDIR/mincdiff-$$-tmp2" header1="$TMPDIR/mincdiff-$$-header1" header2="$TMPDIR/mincdiff-$$-header2" body1="$TMPDIR/mincdiff-$$-body1" body2="$TMPDIR/mincdiff-$$-body2" # Clean up upon exit trap "/bin/rm -f $tmp1 $tmp2 $header1 $header2 $body1 $body2" 0 1 2 15 # Expand the files dumpfile1=`mincexpand $1 $tmp1 -name_only` dumpfile2=`mincexpand $2 $tmp2 -name_only` # Dump the headers and compare them if test $header_diff = yes; then mincheader $dumpfile1 > $header1 || exit 1 mincheader $dumpfile2 > $header2 || exit 1 diff $header_diff_opt $header1 $header2 diff_status=$? else diff_status=0 fi # Dump the raw data and compare them if test $body_cmp = yes; then echo "Binary image comparison:" mincextract -nonormalize -file $dumpfile1 > $body1 \ || die "Error extracting image data from $dumpfile1" mincextract -nonormalize -file $dumpfile2 > $body2 \ || die "Error extracting image data from $dumpfile2" cmp $body_cmp_opt $body1 $body2 \ && echo "Images are identical." cmp_status=$? else cmp_status=0 fi # Exit with SUCCESS if and only if the parts (header and/or body) # that we compared are identical. test $diff_status -eq 0 -a $cmp_status -eq 0 minc-tools-2.3.00+dfsg/progs/mincexample/0002755000175000000620000000000012574624760017260 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincexample/Makefile_sample0000644000175000000620000000205312574624760022257 0ustar stevestaff# -------------------------------------------------------------------- # # Sample Makefile for building mincexample # # Executable names PROGS = mincexample1 mincexample2 EXTRA_OBJ = time_stamp.o CDEFINES = -DDEBUG# cpp defines OPT = -O INCLUDES = -I. -I/usr/local/include LDOPT = -lminc -lnetcdf -lsun -lc_s # -------------------------------------------------------------------- CFLAGS = $(CDEFINES) $(INCLUDES) $(OPT)# CFLAGS and LINTFLAGS should LINTFLAGS = $(CDEFINES) $(INCLUDES)# be same, except for -g/-O PROG_OBJ = $(PROGS:=.o)# list of objects # -------------------------------------------------------------------- default: $(PROGS) #Dependency on Makefile $(PROG_OBJ) $(EXTRA_OBJ) : Makefile .c.o:# defines the rule for creating .o $(CC) $(CFLAGS) -c $< -o $@ # How to make executables $(PROGS) : $$@.o $(EXTRA_OBJ) $(CC) $@.o $(EXTRA_OBJ) -o $@ $(LDOPT) # Remove all derived files in this directory clean: rm -f $(PROGS) $(PROG_OBJ) $(EXTRA_OBJ) minc-tools-2.3.00+dfsg/progs/mincexample/mincexample1.c0000644000175000000620000005432612574624760022017 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincexample1 @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Example program to load a minc file and then save it. @METHOD : @GLOBALS : @CALLS : @CREATED : August 24, 1993 (Peter Neelin) @MODIFIED : * $Log: mincexample1.c,v $ * Revision 6.7 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.6 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2001/09/18 15:32:46 neelin * Create image variable last to allow big images and to fix compatibility * problems with 2.3 and 3.x. * * Revision 6.3 2001/08/16 16:41:34 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.2 2001/04/17 18:40:18 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:21 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:45 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:45 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:58 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:39 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:34:41 neelin * Release of minc version 0.2 * * Revision 1.6 94/09/28 10:34:35 neelin * Pre-release * * Revision 1.5 94/03/16 12:10:50 neelin * Changed ncopen,create,close to miopen,... * Changed setting of step and start attributes in output file. * * Revision 1.4 93/08/30 11:00:55 neelin * Added reading of dimension step and start. * Added printing of volume information in main program. * Put icv setup stuff in separate routine. * Broke up save_volume into smaller routines. * * Revision 1.3 93/08/26 12:31:17 neelin * Added define so that minc_def.h is optionally included. * * Revision 1.2 93/08/26 11:57:00 neelin * Removed include of ParseArgv stuff and added MALLOC and FREE definitions * from minc_def.h so that it is not always needed. * * Revision 1.1 93/08/26 11:45:49 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Definitions for accessing information on each dimension */ #define NUMBER_OF_DIMENSIONS 3 /* Number of dimensions of volume */ #define SLICE 0 /* Index for slice information */ #define ROW 1 /* Index for row information */ #define COLUMN 2 /* Index for column information */ /* Default ncopts values for error handling */ #define NC_OPTS_VAL NC_VERBOSE | NC_FATAL /* Types */ typedef struct { long nslices; /* Number of slices */ long nrows; /* Number of rows in image */ long ncolumns; /* Number of columns in image */ unsigned char *data; /* Pointer to volume data */ double maximum; /* Volume maximum */ double minimum; /* Volume minimum */ double step[NUMBER_OF_DIMENSIONS]; /* Step sizes for dimensions */ double start[NUMBER_OF_DIMENSIONS]; /* Start positions for dimensions */ char dimension_names[NUMBER_OF_DIMENSIONS][MAX_NC_NAME]; /* Dimension names */ } VIO_Volume; /* Function prototypes */ static void load_volume(char *infile, VIO_Volume *volume); static void get_dimension_info(char *infile, int icvid, VIO_Volume *volume); static void setup_icv(int icvid); static void read_volume_data(int icvid, VIO_Volume *volume); static void save_volume(char *infile, char *outfile, char *arg_string, VIO_Volume *volume); static void setup_variables(int inmincid, int mincid, VIO_Volume *volume, char *arg_string); static void setup_image_variables(int inmincid, int mincid, int ndims, int dim[]); static void update_history(int mincid, char *arg_string); static void write_volume_data(int icvid, VIO_Volume *volume); /* Main program */ int main(int argc, char *argv[]) { char *infile, *outfile; VIO_Volume volume; char *arg_string; /* Get arguments */ if (argc != 3) { (void) fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* Save list of arguments as string */ arg_string = time_stamp(argc, argv); /* Read in input volume */ load_volume(infile, &volume); /* Print out volume information */ (void) printf("File %s:\n", infile); (void) printf(" maximum = %10g, minimum = %10g\n", volume.maximum, volume.minimum); (void) printf(" slices = %10s: n=%3d, step=%10g, start=%10g\n", volume.dimension_names[SLICE], (int) volume.nslices, volume.step[SLICE], volume.start[SLICE]); (void) printf(" rows = %10s: n=%3d, step=%10g, start=%10g\n", volume.dimension_names[ROW], (int) volume.nrows, volume.step[ROW], volume.start[ROW]); (void) printf(" columns = %10s: n=%3d, step=%10g, start=%10g\n", volume.dimension_names[COLUMN], (int) volume.ncolumns, volume.step[COLUMN], volume.start[COLUMN]); /* Save the volume, copying information from input file */ save_volume(infile, outfile, arg_string, &volume); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : load_volume @INPUT : infile - input file name @OUTPUT : volume - input volume data @RETURNS : (nothing) @DESCRIPTION: Routine to read in a transverse volume and store the data in a volume structure. @METHOD : @GLOBALS : @CALLS : @CREATED : August 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void load_volume(char *infile, VIO_Volume *volume) { int icvid, mincid; /* Create and set up icv for input */ icvid = miicv_create(); setup_icv(icvid); /* Open the image file */ mincid = miopen(infile, NC_NOWRITE); /* Attach the icv to the file */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); /* Get dimension information */ get_dimension_info(infile, icvid, volume); /* Read in volume data */ read_volume_data(icvid, volume); /* Close the file and free the icv */ (void) miclose(mincid); (void) miicv_free(icvid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_icv @INPUT : icvid - id of icv to set up @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up an icv @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_icv(int icvid) { /* Set desired type */ (void) miicv_setint(icvid, MI_ICV_TYPE, NC_BYTE); (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ (void) miicv_setint(icvid, MI_ICV_VALID_MIN, 0); (void) miicv_setint(icvid, MI_ICV_VALID_MAX, 255); /* Do normalization so that all pixels are on same scale */ (void) 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) */ (void) miicv_setint(icvid, MI_ICV_DO_FILLVALUE, TRUE); /* 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. */ (void) miicv_setint(icvid, MI_ICV_DO_DIM_CONV, TRUE); (void) miicv_setint(icvid, MI_ICV_XDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icvid, MI_ICV_YDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icvid, MI_ICV_ZDIM_DIR, MI_ICV_POSITIVE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dimension_info @INPUT : infile - name of input file icvid - id of the image conversion variable @OUTPUT : volume - input volume data @RETURNS : (nothing) @DESCRIPTION: Routine to get dimension information from an input file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_dimension_info(char *infile, int icvid, VIO_Volume *volume) { int mincid, imgid, varid; int idim, ndims; int dim[MAX_VAR_DIMS]; long *dimlength; char *dimname; /* Get the minc file id and the image variable id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); (void) miicv_inqint(icvid, MI_ICV_VARID, &imgid); if ((mincid == MI_ERROR) || (imgid == MI_ERROR)) { (void) fprintf(stderr, "File %s is not attached to an icv!\n", infile); exit(EXIT_FAILURE); } /* Get the list of dimensions subscripting the image variable */ (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); /* Check that we only have three dimensions */ if (ndims != NUMBER_OF_DIMENSIONS) { (void) fprintf(stderr, "File %s does not have exactly %d dimensions\n", infile, NUMBER_OF_DIMENSIONS); exit(EXIT_FAILURE); } /* Loop through dimensions, checking them and getting their sizes */ for (idim=0; idimnslices) ; break; case 1: dimlength = &(volume->nrows) ; break; case 2: dimlength = &(volume->ncolumns) ; break; } /* Get dimension name and size */ dimname = volume->dimension_names[idim]; (void) ncdiminq(mincid, dim[idim], dimname, dimlength); } /* Get dimension step and start (defaults = 1 and 0). For slices, we read straight from the variable, but for image dimensions, we get the step and start from the icv. If we didn't have MI_ICV_DO_DIM_CONV set to TRUE, then we would have to get the image dimension step and start straight from the variables. */ for (idim=0; idimstep[idim] = 1.0; volume->start[idim] = 0.0; } ncopts = 0; (void) miicv_inqdbl(icvid, MI_ICV_ADIM_STEP, &volume->step[COLUMN]); (void) miicv_inqdbl(icvid, MI_ICV_ADIM_START, &volume->start[COLUMN]); (void) miicv_inqdbl(icvid, MI_ICV_BDIM_STEP, &volume->step[ROW]); (void) miicv_inqdbl(icvid, MI_ICV_BDIM_START, &volume->start[ROW]); if ((varid=ncvarid(mincid, volume->dimension_names[SLICE])) != MI_ERROR) { (void) miattget1(mincid, varid, MIstep, NC_DOUBLE, &volume->step[SLICE]); (void) miattget1(mincid, varid, MIstart, NC_DOUBLE, &volume->start[SLICE]); } ncopts = NC_OPTS_VAL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_volume_data @INPUT : icvid - id of icv to set up @OUTPUT : volume - volume data @RETURNS : (nothing) @DESCRIPTION: Routine to read in the volume data. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void read_volume_data(int icvid, VIO_Volume *volume) { long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; /* Set up the start and count variables for reading the volume */ (void) miset_coords(3, 0, start); count[0] = volume->nslices; count[1] = volume->nrows; count[2] = volume->ncolumns; /* Allocate space for the data */ volume->data = malloc(sizeof(*volume->data) * volume->ncolumns * volume->nrows * volume->nslices); /* Read in the volume */ (void) miicv_get(icvid, start, count, volume->data); /* Get the volume min and max */ (void) miicv_inqdbl(icvid, MI_ICV_NORM_MIN, &volume->minimum); (void) miicv_inqdbl(icvid, MI_ICV_NORM_MAX, &volume->maximum); } /* ----------------------------- MNI Header ----------------------------------- @NAME : save_volume @INPUT : infile - input file name (NULL means don't copy info) outfile - output file name arg_string - string giving argument list volume - volume data @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to save a transverse volume, copying information from an optional input file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void save_volume(char *infile, char *outfile, char *arg_string, VIO_Volume *volume) { int mincid, icvid, inmincid; /* Create output file */ mincid = micreate(outfile, NC_CLOBBER); /* Open the input file if it is provided */ if (infile != NULL) { inmincid = miopen(infile, NC_NOWRITE); } else { inmincid = MI_ERROR; } /* Set up variables and put output file in data mode */ setup_variables(inmincid, mincid, volume, arg_string); /* Close the input file */ if (inmincid != MI_ERROR) { (void) miclose(inmincid); } /* Create an icv and set it up */ icvid = miicv_create(); setup_icv(icvid); /* Attach the icv to the file */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); /* Write out the volume data */ write_volume_data(icvid, volume); /* Close the file and free the icv */ (void) miclose(mincid); (void) miicv_free(icvid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_variables @INPUT : inmincid - id of input minc file (MI_ERROR if no file) mincid - id of output minc file volume - volume information arg_string - string giving argument list @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up variables in the output minc file @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_variables(int inmincid, int mincid, VIO_Volume *volume, char *arg_string) { int dim[MAX_VAR_DIMS], ndims, idim, varid; int excluded_vars[10], nexcluded; /* Create the dimensions */ ndims = NUMBER_OF_DIMENSIONS; dim[0] = ncdimdef(mincid, volume->dimension_names[0], volume->nslices); dim[1] = ncdimdef(mincid, volume->dimension_names[1], volume->nrows); dim[2] = ncdimdef(mincid, volume->dimension_names[2], volume->ncolumns); /* If an input file is provided, copy all header info from that file except image, image-max, image-min */ if (inmincid != MI_ERROR) { /* Look for the image variable and the image-max/min variables so that we can exclude them from the copy */ nexcluded = 0; excluded_vars[nexcluded] = ncvarid(inmincid, MIimage); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemax); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemin); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; /* Copy the variable definitions */ (void) micopy_all_var_defs(inmincid, mincid, nexcluded, excluded_vars); } /* Set up the dimension variables. If the variable doesn't exist, create it (either no input file or variable did not exist in it). If the dimensions are not standard, then no variable is created. */ for (idim=0; idim < ndims; idim++) { ncopts = 0; varid = ncvarid(mincid, volume->dimension_names[idim]); if (varid == MI_ERROR) { varid = micreate_std_variable(mincid, volume->dimension_names[idim], NC_INT, 0, NULL); } ncopts = NC_OPTS_VAL; if (varid != MI_ERROR) { (void) miattputdbl(mincid, varid, MIstep, volume->step[idim]); (void) miattputdbl(mincid, varid, MIstart, volume->start[idim]); } } /* Create the image, image-max and image-min variables */ setup_image_variables(inmincid, mincid, ndims, dim); /* Add the time stamp to the history */ update_history(mincid, arg_string); /* Put the file in data mode */ (void) ncendef(mincid); /* Copy over variable values */ if (inmincid != MI_ERROR) { (void) micopy_all_var_values(inmincid, mincid, nexcluded, excluded_vars); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_image_variables @INPUT : inmincid - id of input minc file (MI_ERROR if no file) mincid - id of output minc file ndims - number of dimensions dim - list of dimension ids @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up image, image-max and image-min variables in the output minc file @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_image_variables(int inmincid, int mincid, int ndims, int dim[]) { int imgid, maxid, minid; static double valid_range[2] = {0.0, 255.0}; /* Create the image max and min variables */ maxid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, 0, NULL); minid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, 0, NULL); if (inmincid != MI_ERROR) { (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemax), mincid, maxid); (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemin), mincid, minid); } /* Create the image variable, copy attributes, set the signtype attribute, set the valid range attribute and delete valid max/min attributes */ imgid = micreate_std_variable(mincid, MIimage, NC_BYTE, ndims, dim); if (inmincid != MI_ERROR) { (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimage), mincid, imgid); ncopts = 0; (void) ncattdel(mincid, imgid, MIvalid_max); (void) ncattdel(mincid, imgid, MIvalid_min); ncopts = NC_OPTS_VAL; } (void) miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); (void) miset_valid_range(mincid, imgid, valid_range); } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static void update_history(int mincid, char *arg_string) { nc_type datatype; int att_length; char *string; /* 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); 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 : write_volume_data @INPUT : icvid - id of icv to set up volume - volume data @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to write out the volume data. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void write_volume_data(int icvid, VIO_Volume *volume) { int mincid; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Set up the start and count variables for writinging the volume */ (void) miset_coords(3, 0, start); count[0] = volume->nslices; count[1] = volume->nrows; count[2] = volume->ncolumns; /* Write out the volume min and max */ (void) mivarput1(mincid, ncvarid(mincid, MIimagemin), NULL, NC_DOUBLE, NULL, &volume->minimum); (void) mivarput1(mincid, ncvarid(mincid, MIimagemax), NULL, NC_DOUBLE, NULL, &volume->maximum); /* Write out the volume */ (void) miicv_put(icvid, start, count, volume->data); /* Free space for the data */ free(volume->data); } minc-tools-2.3.00+dfsg/progs/mincexample/mincexample2.c0000644000175000000620000006333012574624760022013 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mincexample2 @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Example program copy a minc file one slice at a time (where mincexample loads in the whole volume and then writes it out). @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : * $Log: mincexample2.c,v $ * Revision 6.7 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.6 2008/01/12 19:08:15 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.5 2004/11/01 22:38:38 bert * Eliminate all references to minc_def.h * * Revision 6.4 2001/09/18 15:32:46 neelin * Create image variable last to allow big images and to fix compatibility * problems with 2.3 and 3.x. * * Revision 6.3 2001/08/16 16:41:34 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.2 2001/04/17 18:40:19 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:22 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:45 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:45 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:58 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:39 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:34:42 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:34:36 neelin * Pre-release * * Revision 1.2 94/03/17 08:36:29 neelin * Added support for 2-d files. * * Revision 1.1 94/03/16 12:00:59 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include #include #include #include /* Constants */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Definitions for accessing information on each dimension */ #define NUMBER_OF_DIMENSIONS 3 /* Number of dimensions of volume */ #define SLICE 0 /* Index for slice information */ #define ROW 1 /* Index for row information */ #define COLUMN 2 /* Index for column information */ /* Default ncopts values for error handling */ #define NC_OPTS_VAL NC_VERBOSE | NC_FATAL /* Types */ typedef struct { long nslices; /* Number of slices */ long nrows; /* Number of rows in image */ long ncolumns; /* Number of columns in image */ double maximum; /* Volume maximum */ double minimum; /* Volume minimum */ double step[NUMBER_OF_DIMENSIONS]; /* Step sizes for dimensions */ double start[NUMBER_OF_DIMENSIONS]; /* Start positions for dimensions */ char dimension_names[NUMBER_OF_DIMENSIONS][MAX_NC_NAME]; /* Dimension names */ } Volume_Info; /* Function prototypes */ static int get_volume_info(char *infile, Volume_Info *volume_info); static void setup_input_icv(int icvid); static void get_dimension_info(char *infile, int icvid, Volume_Info *volume_info); static void close_volume(int icvid); static void get_volume_slice(int icvid, Volume_Info *volume_info, int slice_num, unsigned char *image); static int save_volume_info(int input_icvid, char *outfile, char *arg_string, Volume_Info *volume_info); static void setup_output_icv(int icvid); static void setup_variables(int inmincid, int mincid, Volume_Info *volume_info, char *arg_string); static void setup_image_variables(int inmincid, int mincid, int ndims, int dim[]); static void update_history(int mincid, char *arg_string); static void save_volume_slice(int icvid, Volume_Info *volume_info, int slice_num, unsigned char *image, double slice_min, double slice_max); /* Main program */ int main(int argc, char *argv[]) { char *infile, *outfile; Volume_Info volume_info; char *arg_string; int input_icvid, output_icvid; unsigned char *image; int islice; /* Get arguments */ if (argc != 3) { (void) fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; /* Save list of arguments as string */ arg_string = time_stamp(argc, argv); /* Read in input volume information */ input_icvid = get_volume_info(infile, &volume_info); /* Print out volume information */ (void) printf("File %s:\n", infile); (void) printf(" maximum = %10g, minimum = %10g\n", volume_info.maximum, volume_info.minimum); (void) printf(" slices = %10s: n=%3d, step=%10g, start=%10g\n", volume_info.dimension_names[SLICE], (int) volume_info.nslices, volume_info.step[SLICE], volume_info.start[SLICE]); (void) printf(" rows = %10s: n=%3d, step=%10g, start=%10g\n", volume_info.dimension_names[ROW], (int) volume_info.nrows, volume_info.step[ROW], volume_info.start[ROW]); (void) printf(" columns = %10s: n=%3d, step=%10g, start=%10g\n", volume_info.dimension_names[COLUMN], (int) volume_info.ncolumns, volume_info.step[COLUMN], volume_info.start[COLUMN]); /* Save the volume, copying information from input file */ output_icvid = save_volume_info(input_icvid, outfile, arg_string, &volume_info); /* Loop through slices, copying them */ image = malloc(volume_info.nrows * volume_info.ncolumns * sizeof(*image)); for (islice=0; islice < volume_info.nslices; islice++) { get_volume_slice(input_icvid, &volume_info, islice, image); save_volume_slice(output_icvid, &volume_info, islice, image, volume_info.minimum, volume_info.maximum); } /* Free up image and close files */ close_volume(input_icvid); close_volume(output_icvid); free(image); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_info @INPUT : infile - input file name @OUTPUT : volume_info - input volume information @RETURNS : Id of icv created @DESCRIPTION: Routine to read volume information for a file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int get_volume_info(char *infile, Volume_Info *volume_info) { int icvid, mincid; /* Create and set up icv for input */ icvid = miicv_create(); setup_input_icv(icvid); /* Open the image file */ mincid = miopen(infile, NC_NOWRITE); /* Attach the icv to the file */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); /* Get dimension information */ get_dimension_info(infile, icvid, volume_info); /* Get the volume min and max */ (void) miicv_inqdbl(icvid, MI_ICV_NORM_MIN, &volume_info->minimum); (void) miicv_inqdbl(icvid, MI_ICV_NORM_MAX, &volume_info->maximum); return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_input_icv @INPUT : icvid - id of icv to set up @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up an icv @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_input_icv(int icvid) { /* Set desired type */ (void) miicv_setint(icvid, MI_ICV_TYPE, NC_BYTE); (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ (void) miicv_setint(icvid, MI_ICV_VALID_MIN, 0); (void) miicv_setint(icvid, MI_ICV_VALID_MAX, 255); /* Do normalization so that all pixels are on same scale */ (void) 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) */ (void) miicv_setint(icvid, MI_ICV_DO_FILLVALUE, TRUE); /* 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. */ (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_POSITIVE); (void) miicv_setint(icvid, MI_ICV_YDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icvid, MI_ICV_ZDIM_DIR, MI_ICV_POSITIVE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dimension_info @INPUT : infile - name of input file icvid - id of the image conversion variable @OUTPUT : volume - input volume data @RETURNS : (nothing) @DESCRIPTION: Routine to get dimension information from an input file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_dimension_info(char *infile, int icvid, Volume_Info *volume_info) { int mincid, imgid, varid; int idim, ndims; int dim[MAX_VAR_DIMS]; long *dimlength; char *dimname; int offset; int missing_one_dimension; /* Get the minc file id and the image variable id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); (void) miicv_inqint(icvid, MI_ICV_VARID, &imgid); if ((mincid == MI_ERROR) || (imgid == MI_ERROR)) { (void) fprintf(stderr, "File %s is not attached to an icv!\n", infile); exit(EXIT_FAILURE); } /* Get the list of dimensions subscripting the image variable */ (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); (void) miicv_inqint(icvid, MI_ICV_NUM_DIMS, &ndims); /* Check that we have two or three dimensions */ if ((ndims != NUMBER_OF_DIMENSIONS) && (ndims != NUMBER_OF_DIMENSIONS-1)) { (void) fprintf(stderr, "File %s does not have %d or %d dimensions\n", infile, NUMBER_OF_DIMENSIONS, NUMBER_OF_DIMENSIONS-1); exit(EXIT_FAILURE); } /* Pretend that we have three dimensions */ offset = ndims - NUMBER_OF_DIMENSIONS; missing_one_dimension = (offset < 0); ndims = NUMBER_OF_DIMENSIONS; /* Loop through dimensions, checking them and getting their sizes */ for (idim=0; idimnslices) ; break; case 1: dimlength = &(volume_info->nrows) ; break; case 2: dimlength = &(volume_info->ncolumns) ; break; } dimname = volume_info->dimension_names[idim]; /* Get dimension name and size */ if (missing_one_dimension && (idim==0)) { (void) strcpy(dimname, "unknown"); *dimlength = 1; } else { (void) ncdiminq(mincid, dim[idim+offset], dimname, dimlength); } } /* Get dimension step and start (defaults = 1 and 0). For slices, we read straight from the variable, but for image dimensions, we get the step and start from the icv. If we didn't have MI_ICV_DO_DIM_CONV set to TRUE, then we would have to get the image dimension step and start straight from the variables. */ for (idim=0; idimstep[idim] = 1.0; volume_info->start[idim] = 0.0; } ncopts = 0; (void) miicv_inqdbl(icvid, MI_ICV_ADIM_STEP, &volume_info->step[COLUMN]); (void) miicv_inqdbl(icvid, MI_ICV_ADIM_START, &volume_info->start[COLUMN]); (void) miicv_inqdbl(icvid, MI_ICV_BDIM_STEP, &volume_info->step[ROW]); (void) miicv_inqdbl(icvid, MI_ICV_BDIM_START, &volume_info->start[ROW]); if ((varid=ncvarid(mincid, volume_info->dimension_names[SLICE])) != MI_ERROR) { (void) miattget1(mincid, varid, MIstep, NC_DOUBLE, &volume_info->step[SLICE]); (void) miattget1(mincid, varid, MIstart, NC_DOUBLE, &volume_info->start[SLICE]); } ncopts = NC_OPTS_VAL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_volume @INPUT : icvid - id of open icv @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close a minc file and free the associated icv @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void close_volume(int icvid) { int mincid; /* Get the minc file id and close the file */ ncopts = 0; if (miicv_inqint(icvid, MI_ICV_CDFID, &mincid) != MI_ERROR) { (void) miclose(mincid); } ncopts = NC_OPTS_VAL; /* Free the icv */ (void) miicv_free(icvid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_slice @INPUT : icvid - id of icv to read volume_info - info for volume slice_num - number of slice to read in (counting from zero) @OUTPUT : image - image that is read in @RETURNS : (nothing) @DESCRIPTION: Routine to read in an image. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_volume_slice(int icvid, Volume_Info *volume_info, int slice_num, unsigned char *image) { long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int offset, ndims; /* Get number of dimensions */ (void) miicv_inqint(icvid, MI_ICV_NUM_DIMS, &ndims); offset = ndims - NUMBER_OF_DIMENSIONS; /* Check slice_num */ if (slice_num >= volume_info->nslices) { (void) fprintf(stderr, "Slice %d is not in the file.\n", slice_num); exit(EXIT_FAILURE); } /* Set up the start and count variables for reading the volume */ (void) miset_coords(3, 0, start); if (offset >= 0) { start[offset] = slice_num; count[offset] = 1; } count[1+offset] = volume_info->nrows; count[2+offset] = volume_info->ncolumns; /* Read in the volume */ (void) miicv_get(icvid, start, count, image); } /* ----------------------------- MNI Header ----------------------------------- @NAME : save_volume_info @INPUT : input_icvid - input file icvid (MI_ERROR means no input volume) outfile - output file name arg_string - string giving argument list volume_info - volume information @OUTPUT : (nothing) @RETURNS : icv of output file @DESCRIPTION: Routine to save a 3-D volume, copying information from an optional input file. @METHOD : @GLOBALS : @CALLS : @CREATED : August 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int save_volume_info(int input_icvid, char *outfile, char *arg_string, Volume_Info *volume_info) { int mincid, icvid, inmincid; /* Create output file */ mincid = micreate(outfile, NC_NOCLOBBER); /* Open the input file if it is provided */ inmincid = MI_ERROR; if (input_icvid != MI_ERROR) { (void) miicv_inqint(input_icvid, MI_ICV_CDFID, &inmincid); } /* Set up variables and put output file in data mode */ setup_variables(inmincid, mincid, volume_info, arg_string); /* Create an icv and set it up */ icvid = miicv_create(); setup_output_icv(icvid); /* Attach the icv to the file */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_output_icv @INPUT : icvid - id of icv to set up @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up an icv @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_output_icv(int icvid) { /* Set desired type */ (void) miicv_setint(icvid, MI_ICV_TYPE, NC_BYTE); (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ (void) miicv_setint(icvid, MI_ICV_VALID_MIN, 0); (void) miicv_setint(icvid, MI_ICV_VALID_MAX, 255); /* No normalization so that pixels are scaled to the slice */ (void) miicv_setint(icvid, MI_ICV_DO_NORM, FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_variables @INPUT : inmincid - id of input minc file (MI_ERROR if no file) mincid - id of output minc file volume_info - volume information arg_string - string giving argument list @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up variables in the output minc file @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_variables(int inmincid, int mincid, Volume_Info *volume_info, char *arg_string) { int dim[MAX_VAR_DIMS], ndims, idim, varid; int excluded_vars[10], nexcluded; /* Create the dimensions */ ndims = NUMBER_OF_DIMENSIONS; dim[0] = ncdimdef(mincid, volume_info->dimension_names[0], volume_info->nslices); dim[1] = ncdimdef(mincid, volume_info->dimension_names[1], volume_info->nrows); dim[2] = ncdimdef(mincid, volume_info->dimension_names[2], volume_info->ncolumns); /* If an input file is provided, copy all header info from that file except image, image-max, image-min */ if (inmincid != MI_ERROR) { /* Look for the image variable and the image-max/min variables so that we can exclude them from the copy */ nexcluded = 0; excluded_vars[nexcluded] = ncvarid(inmincid, MIimage); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemax); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; excluded_vars[nexcluded] = ncvarid(inmincid, MIimagemin); if (excluded_vars[nexcluded] != MI_ERROR) nexcluded++; /* Copy the variable definitions */ (void) micopy_all_var_defs(inmincid, mincid, nexcluded, excluded_vars); } /* Set up the dimension variables. If the variable doesn't exist, create it (either no input file or variable did not exist in it). If the dimensions are not standard, then no variable is created. */ for (idim=0; idim < ndims; idim++) { ncopts = 0; varid = ncvarid(mincid, volume_info->dimension_names[idim]); if (varid == MI_ERROR) { varid = micreate_std_variable(mincid, volume_info->dimension_names[idim], NC_INT, 0, NULL); } ncopts = NC_OPTS_VAL; if (varid != MI_ERROR) { (void) miattputdbl(mincid, varid, MIstep, volume_info->step[idim]); (void) miattputdbl(mincid, varid, MIstart, volume_info->start[idim]); } } /* Create the image, image-max and image-min variables */ setup_image_variables(inmincid, mincid, ndims, dim); /* Add the time stamp to the history */ update_history(mincid, arg_string); /* Put the file in data mode */ (void) ncendef(mincid); /* Copy over variable values */ if (inmincid != MI_ERROR) { (void) micopy_all_var_values(inmincid, mincid, nexcluded, excluded_vars); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_image_variables @INPUT : inmincid - id of input minc file (MI_ERROR if no file) mincid - id of output minc file ndims - number of dimensions dim - list of dimension ids @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up image, image-max and image-min variables in the output minc file @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void setup_image_variables(int inmincid, int mincid, int ndims, int dim[]) { int imgid, maxid, minid; static double valid_range[2] = {0.0, 255.0}; /* Create the image max and min variables (varying over slices) */ maxid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, 1, dim); minid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, 1, dim); if (inmincid != MI_ERROR) { (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemax), mincid, maxid); (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemin), mincid, minid); } /* Create the image variable, copy attributes, set the signtype attribute, set the valid range attribute and delete valid max/min attributes */ imgid = micreate_std_variable(mincid, MIimage, NC_BYTE, ndims, dim); if (inmincid != MI_ERROR) { (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimage), mincid, imgid); ncopts = 0; (void) ncattdel(mincid, imgid, MIvalid_max); (void) ncattdel(mincid, imgid, MIvalid_min); ncopts = NC_OPTS_VAL; } (void) miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); (void) miset_valid_range(mincid, imgid, valid_range); } /* ----------------------------- 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 : ---------------------------------------------------------------------------- */ static void update_history(int mincid, char *arg_string) { nc_type datatype; int att_length; char *string; /* 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); 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 : save_volume_slice @INPUT : icvid - id of icv to write volume_info - volume information (minimum and maximum are ignored) slice_num - number of slice to write image - image to write slice_min - minimum real value for slice slice_max - maximum real value for slice @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to write out a slice. @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void save_volume_slice(int icvid, Volume_Info *volume_info, int slice_num, unsigned char *image, double slice_min, double slice_max) { int mincid; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Set up the start and count variables for writinging the volume */ (void) miset_coords(3, 0, start); start[0] = slice_num; count[0] = 1; count[1] = volume_info->nrows; count[2] = volume_info->ncolumns; /* Write out the slice min and max */ (void) mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, NULL, &slice_min); (void) mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, NULL, &slice_max); /* Write out the volume */ (void) miicv_put(icvid, start, count, image); } minc-tools-2.3.00+dfsg/progs/mincview/0002755000175000000620000000000012574624760016577 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincview/invert_raw_image.c0000644000175000000620000000647412574624760022276 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : invert_raw_image.c @INPUT : argc - number of arguments argv - arguments 1 - image size in x (-ve means invert) 2 - image size in y (-ve means invert) 3 - number of bytes per pixel (optional - default = 1) @OUTPUT : (none) @DESCRIPTION: Reads an image from standard input and copies it to standard output, inverting along either or both dimensions according to the arguments @METHOD : @GLOBALS : @CALLS : @CREATED : December 3,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #include #define ABS( x ) ( ((x) > (0)) ? (x) : (-(x)) ) #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define ERROR_STATUS -1 #define NORMAL_STATUS 0 /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { int i,j,k,oi,image_size,offset,bytes_per_pixel,row_size,nread,nwritten; int xsize,ysize,xstart,ystart,xstop,ystop,xstep,ystep; char *pname; char *buffer,*outbuf; /* Check arguments */ pname=argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 3) || (argc > 4)) { (void) fprintf(stderr,"Usage : %s xsize ysize \n",pname); exit(ERROR_STATUS); } xsize = atol(argv[1]); ysize = atol(argv[2]); if (argc == 4) { bytes_per_pixel = atol(argv[3]); if (bytes_per_pixel <=0) { (void) fprintf(stderr,"%s : Negative bytes per pixel\n",pname); } } else { bytes_per_pixel = 1; } if ((xsize == 0) || (ysize == 0)) { (void) fprintf(stderr,"%s : Illegal image size\n",pname); exit(ERROR_STATUS); } image_size = ABS(xsize*ysize); row_size = ABS(xsize); if (((buffer=malloc(image_size*bytes_per_pixel)) == NULL) || ((outbuf=malloc(row_size*bytes_per_pixel)) == NULL)){ (void) fprintf(stderr,"%s : Image too large\n",pname); exit(ERROR_STATUS); } /* Get range of loop */ xstart = MAX(0,-xsize-1); xstop = MAX(0,xsize-1); xstep = SIGN(xsize); ystart = MAX(0,-ysize-1); ystop = MAX(0,ysize-1); ystep = SIGN(ysize); /* Loop through images */ while ((nread=fread(buffer, bytes_per_pixel, image_size, stdin)) == image_size) { /* Write out inverted image */ for (j=ystart; ystep*j <= ystop; j += ystep) { offset=j*ABS(xsize); for (i=xstart, oi=0; xstep*i <= xstop; i += xstep, oi++) { for (k=0; k0) { (void) fprintf(stderr,"%s : Insufficient data\n", pname); exit(ERROR_STATUS); } return NORMAL_STATUS; } minc-tools-2.3.00+dfsg/progs/mincview/invert_raw_image.man10000644000175000000620000000116612574624760022701 0ustar stevestaff.\" Hey, EMACS: -*- nroff -*- .TH INVERT_RAW_IMAGE 1 .SH NAME invert_raw_image \- invert 2D image along either or both axes .SH SYNOPSIS .B invert_raw_image .BI xsize .BI xsize .BI [bytesperpixel] .SH DESCRIPTION .B invert_raw_image reads an image from standard input and copies it to standard output, inverting along either or both dimensions according to the arguments. The parameter .BI xsize specifies the number of pixels in the x-direction. Use a negative number to flip the image along this axis. Ditto for .BI ysize. The number of bytes per pixel defaults to 1 if not specified. minc-tools-2.3.00+dfsg/progs/mincview/mincview.man10000644000175000000620000000133412574624760021175 0ustar stevestaff.TH MINCVIEW 1 "$Date: 2011-02-15 02:48:11 $" "" "MINC User's Guide" .SH NAME mincview \- view a MINC file .SH SYNOPSIS \fBmincview\ \fImincfile [slicenumber]\fR .SH DESCRIPTION \fImincview\fR converts the image into PNM format and loads one or all of the slices into \fIdisplay\fR from Imagemagick. This is a very primitive tool, mostly useful for debugging but at least being a very low level tool should always work. Controls for display are simple, use the key for the next image or the right mouse button on the image for a more detailed menu interface The programs Display or register are more suited for typical use. .SH "SEE ALSO" .LP .BR display (1), .BR ImageMagick (1), .BR Display (1), .BR register (1). minc-tools-2.3.00+dfsg/progs/mincview/mincview0000755000175000000620000000633112574624760020347 0ustar stevestaff#! /bin/bash # # Script for viewing a minc file. # uses display from ImageMagick to display the images. PGM_CODE="P5" PPM_CODE="P6" # Check arguments if [ $# -lt "1" ] || \ [ $# -gt "2" ] || \ [ "$1" = "-help" ] || \ [ "$1" = "-h" ] then cat < [] Images are displayed with the patients left on the left side of the screen. END exit -1 fi infile="$1" # Create temporary directory tmpdir="/tmp/mincview-$$" # cleanup nicely trap "rm -rf $tmpdir" 0 1 2 3 13 15 mkdir -p $tmpdir # Get dimension names dims=( $(mincinfo -vardims image $infile ) ) echo " + Dimensions: ${dims[*]}" # Check for vector dimension pnm_code=$PGM_CODE if [ "${dims[${#dims[*]} - 1]}" = "vector_dimension" ] then ndims=$(( ${#dims[*]} - 1 )) nvec=$(mincinfo $infile -dimlength $dims[$#dims]) start_suffix=",0" if [ $nvec != 3 ] then count_suffix=",1" else count_suffix=",3" pnm_code=$PPM_CODE fi else ndims="${#dims[*]}" start_suffix="" count_suffix="" fi echo " + ndims: $ndims" if [ $ndims -gt 3 ] then nprefix=$(( $ndims - 3 )) start_prefix=$(awk 'BEGIN{for (i=0;i<$nprefix;i++) print "0,"}' < /dev/null) count_prefix=$(awk 'BEGIN{for (i=0;i<$nprefix;i++) print "1,"}' < /dev/null) elif [ $ndims -lt "2" ] then echo "No image found in file $infile" exit -1 else start_prefix="" count_prefix="" fi # Get number of slices and image dimensions ind1=$(( $ndims - 3 )) ind2=$(( $ndims - 2 )) ind3=$(( $ndims - 1 )) if [ $ind1 -gt "-1" ] then nslices=$(mincinfo $infile -dimlength ${dims[$ind1]}) else nslices=1 fi # figure out which slices to get allslices=$(seq 0 $(($nslices - 1))) slices_to_get=${2:-"$allslices"} if [ "$slices_to_get" = "$2" ] then if [ "$slices_to_get" -ge "$nslices" ] || [ "$slices_to_get" -lt "0" ] then echo "" echo "$0: Slice number $slices_to_get out of range (0-$nslices)" echo "" exit -1 fi fi # Check for inverting images to get standard orientation imgsize=( $(mincinfo -dimlength ${dims[$ind2]} -dimlength ${dims[$ind3]} $infile) ) echo " + imgsize: ${imgsize[0]}x${imgsize[1]}" echo " + nslices $nslices" # Loop through slices, if needed echo -n "Loading slices" for slice in $slices_to_get do echo -n "." if [ $ndims -gt 2 ] then start="${start_prefix}$slice,0,0${start_suffix}" count="${count_prefix}1,${imgsize[0]},${imgsize[1]}${count_suffix}" else start="0,0$start_suffix" count="${imgsize[0]},${imgsize[1]}$count_suffix" fi output_file=$(printf "$tmpdir/slice-%04d.pgm" $slice) echo "$pnm_code" > $output_file echo "${imgsize[1]} ${imgsize[0]}" >> $output_file echo "255" >> $output_file mincextract -byte \ -start $start \ -count $count \ -positive_direction \ $infile >> $output_file slice=$(($slice + 1)) done echo "Done" cat < to advance to the next image The image number is displayed in the title of the window -------------------------------------------------------------- END # Display the images display -monitor -geometry 512x512 -flip $tmpdir/slice-*.pgm minc-tools-2.3.00+dfsg/progs/mincpik/0002755000175000000620000000000012574624760016410 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/mincpik/mincpik.in0000755000175000000620000004372512574624760020406 0ustar stevestaff#! /usr/bin/env perl # # Copyright 2009 # Andrew Janke - a.janke@gmail.com # The University of Queensland # # 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 the University make no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. use strict; use warnings "all"; use Getopt::Long; use Pod::Usage; use File::Basename; use File::Temp qw/ tempdir /; my($Help, $Usage, $me, @opt_table, $tmpdir, %opt); my(@args, $args, $infile, $outfile, %ordering, $CODE); # permutation 'matrix' for differing views %ordering = ( 'zspace' => ['yspace', 'xspace'], 'yspace' => ['zspace', 'xspace'], 'xspace' => ['zspace', 'yspace'], ); $me = &basename($0); %opt = ( 'help' => 0, 'man' => 0, 'verbose' => 0, 'clobber' => 0, 'fake' => 0, 'scale' => 2, 'width' => undef, 'bitdepth' => 8, 'range' => undef, 'image_range' => undef, 'auto_range' => 0, 'lookup' => undef, 'slice' => undef, 'dirs' => ['zspace'], 'triplanar' => 0, 'tilesize' => 250, 'title' => 0, 'title_text' => undef, 'title_size' => 16, 'sagittal_offset' => undef, 'sagittal_offset_perc' => undef, 'orientation' => 'vertical', 'anot_bar' => undef, ); # Check arguments &GetOptions( 'help|?' => \$opt{'help'}, 'man' => \$opt{'man'}, 'version' => sub { &print_version_info }, 'v|verbose' => \$opt{'verbose'}, 'c|clobber' => \$opt{'clobber'}, 'f|fake' => \$opt{'fake'}, 'scale=i' => \$opt{'scale'}, 'width=i' => \$opt{'width'}, 'depth=i' => \$opt{'bitdepth'}, 'title' => \$opt{'title'}, 'title_text=s' => \$opt{'title_text'}, 'title_size=i' => \$opt{'title_size'}, 'anot_bar=s' => \$opt{'anot_bar'}, # Image range and lookup table options 'range=f{2}' => \@{$opt{'range'}}, 'image_range=f{2}' => \@{$opt{'image_range'}}, 'auto_range' => \$opt{'auto_range'}, 'lookup=s' => \$opt{'lookup'}, # Slicing options 's|slice=i' => \$opt{'slice'}, 'z|axial|transverse' => sub { $opt{'dirs'} = ['zspace']; }, 'y|coronal' => sub { $opt{'dirs'} = ['yspace']; }, 'x|sagittal' => sub { $opt{'dirs'} = ['xspace']; }, # triplanar options 't|triplanar' => \$opt{'triplanar'}, 'tilesize=i' => \$opt{'tilesize'}, 'sagittal_offset=i' => \$opt{'sagittal_offset'}, 'sagittal_offset_perc=i' => \$opt{'sagittal_offset_perc'}, 'vertical' => \$opt{'orientation'}, 'horizontal' => \$opt{'orientation'}, ) or pod2usage(-verbose => 1) && exit; # handle -man, -help or missing args pod2usage(-verbose => 1) if $opt{'help'}; pod2usage(-exitstatus => 0, -verbose => 2) if $opt{'man'}; pod2usage(-verbose => 0) && exit if ($#ARGV != 1); # Check arguments #&Getopt::Tabular::SetHelp ($Help, $Usage); #&GetOptions (\@opt_table, \@ARGV) || exit 1; #die $Usage if ($#ARGV < 0); # create temporary directory $tmpdir = &tempdir( "$me-XXXXXXXX", TMPDIR => 1, CLEANUP => 1 ); # set up file names and do a few checks $infile = $ARGV[0]; $outfile = (defined($ARGV[1])) ? $ARGV[1] : 'PNG:-'; die "$me: Couldn't find $infile\n\n" if (!-e $infile); if($outfile ne 'PNG:-' && -e $outfile && !$opt{'clobber'}){ die "\n$me: $outfile exists, use -clobber to overwrite\n\n"; } if($opt{'bitdepth'} != 16 && $opt{bitdepth} != 8) { die "\n$me: Invalid bitdepth specified - $opt{bitdepth} instead of 8 or 16\n\n"; } # sanity check if($opt{'auto_range'} && @{$opt{'image_range'}}){ die "\n$me: only specify one of -auto_range and -image_range (not both)\n\n"; } # warn about -slice and -triplanar if(defined($opt{'slice'}) && $opt{'triplanar'}){ warn "\n$me: you probably don't want to use both -triplanar and -slice\n\n"; } # warn about -sagittal_offset and -sagittal_offset_perc if(defined($opt{'sagittal_offset'}) && $opt{'sagittal_offset_perc'}){ warn "\n$me: only use one of -sagittal_offset -sagittal_offset_perc\n\n"; } # set up directions for triplanar if($opt{'triplanar'}){ $opt{'dirs'} = ['zspace', 'xspace', 'yspace']; } my ($space, $n_slices, $convert_infile, $imgfile, @extract_args, @convert_args, $img_x, $img_y, $img_step_x, $img_step_y, $img_length_x, $img_length_y, $dim_names, $pipe_args, $dimorder, @mont_files); # find the 5% to 95% PcT image range if -auto_range if($opt{'auto_range'}){ my $buf; print STDERR "Getting range of $infile\n" if $opt{'verbose'}; chomp($buf = `mincstats -quiet -pctT 5 $infile`); $buf *= 1.0; @{$opt{'image_range'}}[0] = $buf; chomp($buf = `mincstats -quiet -pctT 95 $infile`); $buf *= 1.0; @{$opt{'image_range'}}[1] = $buf; # a bit of output if($opt{'verbose'}){ print STDERR "Using image range of [@{$opt{image_range}}[0]:@{$opt{image_range}}[1]]\n"; } } # foreach slicing direction foreach $space (@{$opt{'dirs'}}){ print STDERR "Doing direction $space\n" if $opt{'verbose'}; my($slice); $CODE = "GRAY"; # set up the imgfile depending on triplanar and -title if($opt{'triplanar'}){ $imgfile = "$tmpdir/trip-$space.png"; push(@mont_files, $imgfile); } elsif($opt{'title'}){ $imgfile = "$tmpdir/image.png"; } else{ $imgfile = $outfile; } # Get the info we need $args = "mincinfo ". "-dimlength $space ". "-dimlength $ordering{$space}[0] ". "-dimlength $ordering{$space}[1] ". "-attvalue $ordering{$space}[0]:step ". "-attvalue $ordering{$space}[1]:step ". "-dimnames ". $infile; ($n_slices, $img_x, $img_y, $img_step_x, $img_step_y, $dim_names) = split("\n", `$args`); if(defined($opt{'width'})){ $opt{'scale'} = $opt{'width'}/abs($img_step_y * $img_y); print STDERR "Auto-scaling width factor: $opt{'scale'}\n" if $opt{'verbose'}; } $img_length_x = abs(int($img_step_x * $img_x * $opt{'scale'})); $img_length_y = abs(int($img_step_y * $img_y * $opt{'scale'})); # figure out the slice to get $slice = (!defined($opt{'slice'})) ? int($n_slices/2) : $opt{'slice'}; # do the sagittal offset (only one of these should be done) if($space eq 'xspace'){ # slice offset if(defined($opt{'sagittal_offset'})){ $slice += $opt{'sagittal_offset'}; } # perc offset if(defined($opt{'sagittal_offset_perc'})){ $slice += int($n_slices * $opt{'sagittal_offset_perc'} / 100); } } # check we didn't step of the edge if($slice >= $n_slices || $slice < 0){ die "Slice $slice out of range (0-" . ($n_slices-1) . ")\n\n"; } # check if we have a vector_dimension already if($dim_names =~ m/vector_dimension/){ $CODE = 'RGB'; } # take only the first timepoint if we have a time dimension my @time_res_args = (); if($dim_names =~ m/time/){ @time_res_args = ('-dimrange', "time=0,0"); } # do the reshaping $dimorder = join(',', $space, @{$ordering{$space}}); if($CODE eq 'RGB'){ $dimorder .= ',vector_dimension'; } @args = ('mincreshape', '-clobber', '-quiet', '-normalize', '+direction', '-dimsize', "$space=-1", '-dimsize', "$ordering{$space}[0]=-1", '-dimsize', "$ordering{$space}[1]=-1", '-dimorder', $dimorder, '-dimrange', "$space=$slice,1", @time_res_args, $infile, "$tmpdir/reshaped.mnc"); if(scalar(@{$opt{'range'}}) != 0){ push(@args, '-valid_range', @{$opt{'range'}}[0], @{$opt{'range'}}[1]); } if(scalar(@{$opt{'image_range'}}) != 0){ push(@args, '-image_range', @{$opt{'image_range'}}[0], @{$opt{'image_range'}}[1]); } &do_cmd(@args); # do the lookup if required $convert_infile = "$tmpdir/reshaped.mnc"; if($opt{'lookup'}){ if($CODE eq 'RGB'){ warn "$me: Input is vector-valued already. No colour lookup done.\n"; } else{ $convert_infile = "$tmpdir/lookup.mnc"; $CODE = 'RGB'; &do_cmd('minclookup', '-clobber', '-quiet', split(' ', $opt{'lookup'}), "$tmpdir/reshaped.mnc", $convert_infile); } } # set up mincextract command @extract_args = ('mincextract', $convert_infile, '-normalize', ($opt{'bitdepth'} == 16) ? ('-short', '-unsigned') : '-byte'); # set up convert arguments # a flip is 'normal' due to the difference between mnc and most image co-ordinates @convert_args = ('convert', '-depth', $opt{'bitdepth'}, '-flip', '-size', $img_y . 'x' . $img_x, '-geometry', $img_length_y . 'x' . $img_length_x . '!', "$CODE:-", $imgfile); # check if we are big or little endian for convert's MSB wierdity $pipe_args = '|'; if($opt{'bitdepth'} == 16){ if(unpack("c",substr(pack("s",1),0,1))){ warn "$me: LSB machine, swapping bytes with dd and crossing fingers\n"; $pipe_args .= ' dd conv=swab | '; } } &do_cmd(join(' ', @extract_args, $pipe_args, @convert_args)); } # do the triplanar if requested if($opt{'triplanar'}){ my @orient_args; if($opt{'title'}){ $imgfile = "$tmpdir/mont.png"; } else{ $imgfile = $outfile; } if($opt{'orientation'} eq 'vertical'){ @orient_args = ('-tile', ('1x' . ($#mont_files + 1))); } else{ # $opt{orientation} eq 'horizontal' @orient_args = ('-tile', (($#mont_files + 1) . 'x1')); } # do the montage &do_cmd('montage', @orient_args, '-background', 'grey10', '-geometry', "$opt{tilesize}x$opt{tilesize}+1+1", @mont_files, $imgfile); } # Add the title if($opt{'title'}){ # set up the title text if(!defined($opt{'title_text'})){ $opt{'title_text'} = &basename($infile); $opt{'title_text'} =~ s/\.mnc$//; } # This really does not work all that well (but should), go figure # &do_cmd('convert', '-box', 'black', # '-font', '7x13', # '-fill', 'white', # '-draw', "text 4,12 \"$opt{'title_text'}\"", # $imgfile, $outfile); # # use montage instead &do_cmd('montage', '-geometry', '100x100%', '-background', 'black', '-fill', 'white', '-label', $opt{'title_text'}, '-pointsize', $opt{'title_size'}, $imgfile, $outfile); } # create the annotated bar if required if(defined($opt{'anot_bar'})){ my($min, $max, $pcode, $q0, $q1, $q2, $q3, $q4, $bh, $bw, $bb, $ob, $textbump, $i, @buf); # set up a few constants $textbump = 3; $pcode = '%6g'; # text border, other border, height, width $bb = 50; $ob = 25; $bh = $img_length_y - ($ob*2); $bw = int($bh/10); # get range if not defined if(!defined($opt{'image_range'}[0])){ print STDERR "Getting image range\n" if $opt{'verbose'}; @buf = split(/\n/, `mincstats -min -max -quiet $infile`); @{$opt{'image_range'}}[0] = $buf[0] * 1.0; @{$opt{'image_range'}}[1] = $buf[1] * 1.0; } $min = @{$opt{'image_range'}}[0]; $max = @{$opt{'image_range'}}[1]; # set up the datafile my(@data) = undef; my($packstring) = ''; for($i=0; $i<$bh; $i++){ $data[$i] = $i; $packstring .= 'f'; } open(FH, ">$tmpdir/tmp-float.raw"); for($i=0; $i<$bw; $i++){ syswrite(FH, pack($packstring, @data)); } close(FH); # make minc file &do_cmd('rawtominc', '-clobber', '-float', '-input', "$tmpdir/tmp-float.raw", '-xstep', 1, '-ystep', 1, '-zstep', 1, '-xstart', 0, '-ystart', 0, '-zstart', 0, '-dimorder', 'xspace,yspace,zspace', "$tmpdir/bar.mnc", $bw, $bh, 1); # make .miff bar image (whoa... recursion!) &do_cmd('mincpik', '-clobber', '-scale', 1, (defined($opt{'lookup'})) ? ('-lookup', $opt{'lookup'}) : (), "$tmpdir/bar.mnc", "$tmpdir/bar.png"); # set up the text $q0 = sprintf($pcode, $min); $q1 = sprintf($pcode, (($max-$min) * 0.25) + $min); $q2 = sprintf($pcode, (($max-$min) * 0.5) + $min); $q3 = sprintf($pcode, (($max-$min) * 0.75) + $min); $q4 = sprintf($pcode, $max); # create the bar itself via convert &do_cmd('convert', '-bordercolor', 'white', '-border', $bb, # color for all the decorations '-fill', 'black', # bar border '-draw', "line " . join(',', $bb, $bb, ($bb+$bw), $bb ), '-draw', "line " . join(',', $bb, ($bb+$bh), ($bb+$bw), ($bb+$bh)), '-draw', "line " . join(',', $bb, $bb, $bb, ($bb+$bh)), '-draw', "line " . join(',', ($bb+$bw), $bb, ($bb+$bw), ($bb+$bh)), # 3 ticks at 1/4, 1/2 and 3/4 '-draw', "line " . join(',', ($bb+($bw*3/4)), ($bb+($bh*1/4)), ($bb+$bw), ($bb+($bh*1/4))), '-draw', "line " . join(',', ($bb+($bw*3/4)), ($bb+($bh*2/4)), ($bb+$bw), ($bb+($bh*2/4))), '-draw', "line " . join(',', ($bb+($bw*3/4)), ($bb+($bh*3/4)), ($bb+$bw), ($bb+($bh*3/4))), # text '-draw', 'text ' . ($bb+$bw) . ',' . ($bb+($bh*0/4)+$textbump) . " '$q4'", '-draw', 'text ' . ($bb+$bw) . ',' . ($bb+($bh*1/4)+$textbump) . " '$q3'", '-draw', 'text ' . ($bb+$bw) . ',' . ($bb+($bh*2/4)+$textbump) . " '$q2'", '-draw', 'text ' . ($bb+$bw) . ',' . ($bb+($bh*3/4)+$textbump) . " '$q1'", '-draw', 'text ' . ($bb+$bw) . ',' . ($bb+($bh*4/4)+$textbump) . " '$q0'", # finally crop of the extra border '-crop', (($bb*2)+$bw-($bb-$ob)) . "x" . (($bb*2)+$bh-$bb) . "+" . ($bb-$ob) . "+" . ($bb-$ob), "$tmpdir/bar.png", $opt{'anot_bar'}); } sub do_cmd { print STDERR "@_\n" if $opt{'verbose'}; if(!$opt{'fake'}){ system(@_) == 0 or die "\n$me: Failed executing @_\n\n"; } } # print version information sub print_version_info { my $PACKAGE = '@PACKAGE_NAME@'; my $VERSION = '@PACKAGE_VERSION@'; my $PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@'; print STDOUT "\n$PACKAGE version $VERSION\n". "Comments to $PACKAGE_BUGREPORT\n\n"; exit 0; } __END__ =head1 NAME B - generate images from minc files =head1 SYNOPSIS B [options] .mnc [] mincpik generates image files from MINC volumes using the Imagemagick convert utility. Use -help or -man for more information and examples =head1 DESCRIPTION B generates image files from MINC volumes using the Imagemagick B utility. For a complete list of output file types see the B man pages. EXAMPLES: To display a default view, axial (z) slicing, middle slice using display. (display is part of the Imagemagick package) mincpik infile.mnc PNG:- | display - To generate a PNG file of the 15th coronal slice mincpik -slice 15 -coronal infile.mnc outfile.png To generate a JPG file using the hotmetal lookup table with the image range 0 to 100 mincpik -lookup '-hotmetal' -image_range 0 100 infile.mnc outfile.jpg ImageMagick: http://www.wizards.dupont.com/cristy/ImageMagick.html NB: ImageMagick should be compiled without 16-bit quanta. Currently if there is a time dimension in the file the image will only produced from the first time point Problems or comments should be sent to: a.janke\@gmail.com =head1 OPTIONS =over 4 =item B<-v>, B<--verbose> Be noisy when doing things =item B<--version> Print version number and exit =item B<-?>, B<--help> Dump some quick help output =item B<--man> Dump a man page =item B<-c> B<--clobber> overwrite the output file if it exists already =item B<-f> B<--fake> do a dry run, (echo cmds only). This is usually used in combination with -verbose to echo commands only =item B<--scale> scaling factor for resulting image, by default images are output at twice their original resolution =item B<--width> autoscale the resulting image to have a fixed image width (in pixels) =item B<--depth> bitdepth for resulting image 8 or 16 (MSB machines only!) =item B<--title> add a title to the resulting image, if just this option is specified the text used for the title is the name of the input image file. =item B<--title_text> use the input string for the title [default: input-filename]. This option must be used in conjunction with -title =item B<--title_size> font point size for the title =item B<--anot_bar> create an annotated bar to match the image (use height of the output image) =back =head2 Image range and lookup table options =over 4 =item B<--range> valid range of values for MINC file =item B<--image_range> range of image values to use for pixel intensity =item B<--auto_range> automatically determine image range using a 5 and 95% PcT. (histogram) =item B<--lookup> arguments to pass to minclookup =back =head2 Slicing options =over 4 =item B<-s> B<--slice> slice number to get. (note this is in voxel co-ordinates) =item B<-z> B<--axial> B<--transverse> get an axial/transverse (z) slice =item B<-y> B<--coronal> get a coronal (y) slice =item B<-x> B<--sagittal> get a sagital (x) slice =back =head2 Triplanar options =over 4 =item B<-t> B<--triplanar> create a triplanar view of the input file =item B<--tilesize> pixel size for each image in a triplanar =item B<--sagittal_offset> offset the sagittal slice from the centre =item B<--sagittal_offset_perc> offset the sagittal slice by a percentage from the centre =item B<--vertical> create a vertical triplanar view (Default) =item B<--horizontal> create a horizontal triplanar view =back =head1 SEE ALSO convert(1) mincextract(1) display(1) =head1 AUTHOR Andrew Janke - a.janke@gmail.com =head1 COPYRIGHTS Copyright 2012 by Andrew L Janke =cut minc-tools-2.3.00+dfsg/progs/minccomplete/0002755000175000000620000000000012574624760017435 5ustar stevestaffminc-tools-2.3.00+dfsg/progs/minccomplete/minccomplete.in0000755000175000000620000001071712574624760022453 0ustar stevestaff#! /usr/bin/env perl # # Checks if a MINC file is complete # # Copyright Andrew Janke a.janke@gmail.com # # 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 makes no representations about the suitability of this software # for any purpose. It is provided "as is" without express or implied warranty. use strict; use warnings "all"; use Getopt::Long; use Pod::Usage; use File::Basename; my($me, %opt, $dump, $infile, $error); $me = basename($0); %opt = ( 'help' => 0, 'man' => 0, 'verbose' => 0, 'error_string' => undef, ); # Check arguments &GetOptions( 'help|?' => \$opt{'help'}, 'man' => \$opt{'man'}, 'v|verbose' => \$opt{'verbose'}, 'version' => sub { &print_version_info }, 'e|error_string=s' => \$opt{'error_string'}, ) or pod2usage(-verbose => 1) && exit; # handle -man, -help or missing args pod2usage(-verbose => 1) if $opt{'help'}; pod2usage(-exitstatus => 0, -verbose => 2) if $opt{'man'}; pod2usage(-verbose => 0) && exit if ($#ARGV != 0); $infile = $ARGV[0]; # check if the file in question exists if(!-e $infile){ if(defined($opt{error_string})){ print STDOUT "$opt{error_string}\n"; exit(-1); } else{ die "$me: Couldn't find $infile\n\n"; } } # figure out what we have here if($infile =~ m/\.mnc(\.gz|)$/){ my($version); print STDOUT "Checking a MINC file ($infile)\n" if $opt{'verbose'}; # first check that the header is "valid" if(system("mincdump -h $infile 2> /dev/null 1>&2") == 0){ $error = 0; } else{ $error = -1; } # if good, check if the image is complete for MINC2 if($error == 0){ chomp($version = `mincinfo -minc_version $infile 2> /dev/null`); if($version =~ m/HDF5/){ print STDOUT "File is $version\n" if $opt{'verbose'}; chomp($dump = `mincinfo -attvalue image:complete $infile 2> /dev/null`); $error = -1 if ($dump ne 'true_'); } } } elsif($infile =~ m/\.xfm$/){ print STDOUT "Checking a XFM file ($infile)\n" if $opt{'verbose'}; if(system("xfm2param $infile 2> /dev/null 1>&2") == 0){ $error = 0; } else{ $error = -1; } } # now output the status if($error){ if(defined($opt{'error_string'})){ print STDOUT "$opt{'error_string'}\n"; } else{ print STDOUT "$error\n"; } } else{ print STDOUT "$error\n"; } exit($error); # print version information sub print_version_info { my $PACKAGE = '@PACKAGE_NAME@'; my $VERSION = '@PACKAGE_VERSION@'; my $PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@'; print STDOUT "\n$PACKAGE version $VERSION\n". "Comments to $PACKAGE_BUGREPORT\n\n"; exit 0; } __END__ =head1 NAME B - checks if a MINC file is complete =head1 SYNOPSIS B [options] minccomplete is designed as a QC tool that you can use to check if MINC or xfm files have been completely written. =head1 DESCRIPTION B will check if a file exists and is completely written. The reasons for non-completion are varied but typically caused when a process writing a MINC or xfm file is interrupted. A zero will be returned if the file is complete and a -1 if it isn't. Examples: $ minccomplete in.mnc 0 $ minccomplete in.xfm 0 A MINC file is returned as complete if the image attribute image:complete is set. ie: mincinfo -attvalue image:complete infile.mnc A xfm file is deemed complete if xfm2param can parse the file. The associated exit codes will also be set as part of this so that minccomplete can be used in scripts. An entirely simplistic example: #! /bin/sh infile=$1 if [ `minccomplete ${infile}` ] then echo "Yes!" else echo "Nope, try again" fi Problems or comments should be sent to: a.janke@gmail.com =head1 OPTIONS =over 4 =item B<-v>, B<--verbose> Be noisy when doing things =item B<--version> Print version number and exit =item B<-h>, B<--help> Dump some quick help output =item B<--man> Dump a man page =item B<-e>, B<--error_string> Don't die on errors (eg: file not found) and print the supplied value instead =back =head1 SEE ALSO mincheader(1) mincinfo(1) xfm2param(1) =head1 AUTHOR Andrew Janke - a.janke@gmail.com =head1 COPYRIGHTS Copyright 2000 by Andrew L Janke =cut minc-tools-2.3.00+dfsg/COPYING0000644000175000000620000000075212574624760014661 0ustar stevestaffCopyright 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. minc-tools-2.3.00+dfsg/NEWS0000644000175000000620000007533412574624760014335 0ustar stevestaffNew in Release 2.3.00 --------------------- * Many fixes for dcm2mnc * Add -skewness and -kurtosis to mincstats * Some general refactoring and bug fixing * Don't treat leading zero in numeric arguments as indicating an octal radix. * Add confusion matrix statistics to minccmp, similar to voldiff. * Fix bugs in mincresample, mnc2nii, mincaverage, and minccalc. 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. minc-tools-2.3.00+dfsg/conversion/0002755000175000000620000000000012601654630015777 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/minctoecat/0002755000175000000620000000000012574624760020137 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/minctoecat/minctoecat.c0000644000175000000620000011014212574624760022426 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minctoecat @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts a minc format file to a CTI ECAT file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 2005 (Anthonin Reilhac) @MODIFIED : @COPYRIGHT : Copyright 2005 Anthonin Reilhac, 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 #include #include #include #include "ecat_write.h" #define ShortMax 32767 #define BECQUEREL_PER_NCURIE 37 typedef enum MODALITY { UNKNOWN_MODALITY, PET, MRI, NUM_MODALITY } modality; typedef enum DATA_UNIT { UNKNOWN_DATA_UNIT, LABEL, NCIPERCC } data_unit; typedef struct MINC_INFO { int dim; long x_size; long y_size; long z_size; long time_length; double x_step; double y_step; double z_step; int x_start_flag; int y_start_flag; int z_start_flag; double x_start; double y_start; double z_start; double *time_points; double *time_widths; data_unit dunit; modality mod; float isotope_halflife; } minc_info; /*Fuction declarations*/ double decay_correction(double scan_time, double measure_time, double start_time, double half_life); minc_info *get_minc_info(char *file); void free_minc_info(minc_info *mi); void get_patient_info(char *file, Main_header *mh); void get_study_info(char *file, Main_header *mh); void get_acquisition_info(char *file, Main_header *mh); void get_ecat_acquisition_info(char *file, Main_header *mh); void get_ecat_main_header_info(char *file, Main_header *mh); void get_ecat_subheader_info(char *file, Image_subheader *sh); Main_header * init_main_header(char *file, minc_info *mi); Image_subheader * init_image_subheader(char *file, minc_info *mi, Main_header *mh); void write_ecat_frame(MatrixFile *mf, Image_subheader *sh, short *ptr,int fr, float min, float max); void usage_error(char *progname); int main ( int argc, char **argv) { /* Variables for arguments */ static int include_patient_info = TRUE; static int include_study_info = TRUE; static int include_acquisition_info = TRUE; static int include_ecat_acquisition_info = TRUE; static int include_ecat_main_info = TRUE; static int include_ecat_subheader_info = TRUE; static int do_decay_corr_factor = TRUE; static int label_data = FALSE; /* Argument option table */ static ArgvInfo argTable[] = { {"-ignore_patient_variable", ARGV_CONSTANT, (char *) FALSE, (char *) &include_patient_info,"Ignore informations from the minc patient variable."}, {"-ignore_study_variable", ARGV_CONSTANT, (char *) FALSE, (char *) &include_study_info,"Ignore informations from the minc study variable."}, {"-ignore_acquisition_variable", ARGV_CONSTANT, (char *) FALSE, (char *) &include_acquisition_info,"Ignore informations from the minc acquisition variable."}, {"-ignore_ecat_acquisition_variable", ARGV_CONSTANT, (char *) FALSE, (char *) &include_ecat_acquisition_info,"Ignore informations from the minc ecat_acquisition variable."}, {"-ignore_ecat_main", ARGV_CONSTANT, (char *) FALSE, (char *) &include_ecat_main_info,"Ignore informations from the minc ecat-main variable."}, {"-ignore_ecat_subheader_variable", ARGV_CONSTANT, (char *) FALSE, (char *) &include_ecat_subheader_info,"Ignore informations from the minc ecat-subhdr variable."}, {"-no_decay_corr_fctr", ARGV_CONSTANT, (char *) FALSE, (char *) &do_decay_corr_factor,"Do not compute the decay correction factors"}, {"-label", ARGV_CONSTANT, (char *) TRUE, (char *) &label_data,"Voxel values are treated as integers, scale and calibration factors are set to unity"}, {NULL, ARGV_END, NULL, NULL, NULL} }; /*other variables*/ char *pname; /*name of the present command ->argv[0]*/ char *minc_file_name = NULL; /*name of the input file (minc format)*/ char *ecat_file_name = NULL; /*name of the output file (to be created into the ecat format*/ minc_info *mi = NULL; VIO_Volume volume; short int *ecat_short_ptr = NULL; float ecat_scale_factor = 1; VIO_progress_struct progress; VIO_STR DimOrder[VIO_MAX_DIMENSIONS]; MatrixFile *out_matrix_file = NULL; Main_header *out_main_header = NULL; Image_subheader *out_image_subheader = NULL; int fr; int incre = 0; int x_ind, y_ind, z_ind; double cal_factor; /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { usage_error(pname); } /* Get file names */ minc_file_name = argv[1]; ecat_file_name = argv[2]; /*getting informations about the dimensions from the minc file*/ if((mi = get_minc_info(minc_file_name)) == NULL) { fprintf(stderr, "Can not get informations from %s.\n", minc_file_name); return (1); } /*checking the number of dimension*/ if((mi->dim != 3) && (mi->dim != 4)) { (void)fprintf(stderr, "input file has not a valid number of dimensions (%d), should be either 3 (static file) or 4 (dynamic file)", mi->dim); return (-1); } /*************************************************************************************** * filling the ecat header fields with the available informations * ***************************************************************************************/ /*MAIN HEADER: filling compulsory fields*/ if((out_main_header = init_main_header(minc_file_name, mi)) == NULL) { fprintf(stderr, "Can not initilize the ecat main header.\n"); return (1); } /*additional informations if available and requested*/ /*getting patient informations from the MIpatient variable*/ if(include_patient_info) get_patient_info(minc_file_name, out_main_header); /*getting study information from the MIstudy variable*/ if(include_study_info) get_study_info(minc_file_name, out_main_header); /*getting acquisition information from the MIacquisition variable*/ if(include_acquisition_info) get_acquisition_info(minc_file_name, out_main_header); /*getting acquisition information from the ecat_acquisition variable*/ if(include_ecat_acquisition_info) get_ecat_acquisition_info(minc_file_name, out_main_header); /*getting ecat main header informations from the ecat-main variable*/ if(include_ecat_main_info) get_ecat_main_header_info(minc_file_name, out_main_header); /*SUBHEADER TEMPLATE: filling compulsory fields*/ if((out_image_subheader = init_image_subheader(minc_file_name, mi, out_main_header)) == NULL) { fprintf(stderr, "Can not initialize the ecat image subheader.\n"); return (1); } /*getting additionnal informations from the ecat-subhdr variable*/ if(include_ecat_subheader_info) get_ecat_subheader_info(minc_file_name, out_image_subheader); /*writting the main header*/ if((out_matrix_file = matrix_create(ecat_file_name, out_main_header)) == NULL) { fprintf(stderr, "cannot create %s \n", ecat_file_name); return(-1); } /*extracting the mincvolume in the order time, X, Y, Z*/ if(mi->dim == 4) { DimOrder[0] = MItime; DimOrder[1] = MIzspace; DimOrder[2] = MIyspace; DimOrder[3] = MIxspace; } else { DimOrder[0] = MIzspace; DimOrder[1] = MIyspace; DimOrder[2] = MIxspace; } /*input the volume*/ if(input_volume(minc_file_name, mi->dim, DimOrder, NC_UNSPECIFIED, FALSE, 1, 1, TRUE, &volume, (minc_input_options *) NULL) != VIO_OK) return (1); /*initializing the progress report*/ initialize_progress_report(&progress, FALSE, mi->time_length, "Converting data"); cal_factor = (mi->dunit == NCIPERCC)?BECQUEREL_PER_NCURIE:1; cal_factor /= out_main_header->calibration_factor; /*getting the values*/ for(fr = 0; fr < ((mi->time_length)?mi->time_length:1); fr++) { float max_val = 0, min_val = 0; int vx, vy, vz; VIO_Real value; if((ecat_short_ptr = (short int *)calloc((mi->x_size) * (mi->y_size) * (mi->z_size), sizeof(short int))) == NULL) { fprintf(stderr, "allocation error\n"); return 1; } for(vz = 0; vz < mi->z_size; vz++) for(vy = 0; vy < mi->y_size; vy++) for(vx = 0; vx < mi->x_size; vx++) { value = ((mi->time_length)?get_volume_real_value(volume, fr, vz, vy, vx, 0):get_volume_real_value(volume, vz, vy, vx, 0,0)) * cal_factor; if(value > max_val) max_val = value; if(value < min_val) min_val = value; } if((mi->dunit != LABEL) && (!label_data)) ecat_scale_factor = ((max_val > fabs(min_val))?max_val:fabs(min_val))/ShortMax; for(vz = 0; vz < mi->z_size; vz++) { for(vy = 0; vy < mi->y_size; vy++) { for(vx = 0; vx < mi->x_size; vx++) { value = ((mi->time_length)?get_volume_real_value(volume, fr, vz, vy, vx, 0):get_volume_real_value(volume, vz, vy, vx, 0,0)); x_ind = ((mi->x_step > 0)?mi->x_size - 1 - vx:vx); y_ind = ((mi->y_step > 0)?mi->y_size - 1 - vy:vy); z_ind = ((mi->z_step > 0)?mi->z_size - 1 - vz:vz); ecat_short_ptr[VIO_IJK(z_ind, y_ind, x_ind, mi->y_size, mi->x_size)] = (short int)VIO_ROUND(value * cal_factor/(ecat_scale_factor)); } } } /*filling image subheader*/ if(mi->dim == 4) { out_image_subheader->frame_start_time = (unsigned int)(mi->time_points[fr] * 1000); out_image_subheader->frame_duration = (unsigned int)(mi->time_widths[fr] * 1000); } if((mi->dunit == NCIPERCC) && (do_decay_corr_factor)) out_image_subheader->decay_corr_fctr = decay_correction(mi->time_points[fr],mi->time_widths[fr] , 0.0, mi->isotope_halflife); out_image_subheader->scale_factor = ecat_scale_factor; write_ecat_frame(out_matrix_file, out_image_subheader, ecat_short_ptr, fr, min_val, max_val); free(ecat_short_ptr); ecat_short_ptr = NULL; update_progress_report(&progress, incre); incre++; } free_minc_info(mi); matrix_close(out_matrix_file); free(out_main_header); free(out_image_subheader); terminate_progress_report(&progress); return (0); } double decay_correction(double scan_time, double measure_time, double start_time, double half_life) { double mean_life; double measure_correction; double decay; /* Check for negative half_life and calculate mean life */ if (half_life <= 0.0) return 1.0; mean_life = half_life/ log(2.0); /* Normalize scan time and measure_time */ scan_time = (scan_time - start_time) / mean_life; measure_time /= mean_life; /* Calculate correction for decay over measuring time (assuming a constant activity). Check for possible rounding errors. */ if ((measure_time*measure_time/2.0) < DBL_EPSILON) { measure_correction = 1.0 - measure_time/2.0; } else { measure_correction = (1.0 - exp(-measure_time)) / fabs(measure_time); } /* Calculate decay */ decay = exp(-scan_time) * measure_correction; if (decay<=0.0) decay = DBL_MAX; else decay = 1.0/decay; return decay; } minc_info *get_minc_info(char *file) { int minc_fd; /*minc file descriptor*/ minc_info *mi = NULL; /*minc info structures*/ int var_id; int num_att; char attname[NC_MAX_NAME+1]; char buff[NC_MAX_NAME+1]; int i; /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if((mi = (minc_info *)calloc(1, sizeof(minc_info))) == NULL) return NULL; /*initlaization*/ mi->time_length = 0; mi->mod = UNKNOWN_MODALITY; mi->dunit = UNKNOWN_DATA_UNIT; mi->x_start_flag = 0; mi->y_start_flag = 0; mi->z_start_flag = 0; /* first we check out the number of dimension. we suppose that the time dimension is the 4th*/ ncinquire(minc_fd, &(mi->dim), NULL, NULL, NULL); /*getting infos about the x, y and and z sizes and width*/ ncdiminq(minc_fd, ncdimid(minc_fd, MIxspace), NULL, &(mi->x_size)); ncdiminq(minc_fd, ncdimid(minc_fd, MIyspace), NULL, &(mi->y_size)); ncdiminq(minc_fd, ncdimid(minc_fd, MIzspace), NULL, &(mi->z_size)); /*getting information about each dimensions*/ /*MIxspace*/ var_id = ncvarid(minc_fd, MIxspace); ncvarinq(minc_fd,var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MIstart, attname) == 0) { mi->x_start_flag = 1; miattget1(minc_fd,var_id, MIstart, NC_DOUBLE, &(mi->x_start)); } if(strcmp(MIstep, attname) == 0) miattget1(minc_fd,var_id, MIstep, NC_DOUBLE, &(mi->x_step)); } /*MIyspace*/ var_id = ncvarid(minc_fd, MIyspace); ncvarinq(minc_fd,var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MIstart, attname) == 0) { mi->y_start_flag = 1; miattget1(minc_fd,var_id, MIstart, NC_DOUBLE, &(mi->y_start)); } if(strcmp(MIstep, attname) == 0) miattget1(minc_fd,var_id, MIstep, NC_DOUBLE, &(mi->y_step)); } /*MIzspace*/ var_id = ncvarid(minc_fd, MIzspace); ncvarinq(minc_fd,var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MIstart, attname) == 0) { mi->z_start_flag = 1; miattget1(minc_fd,var_id, MIstart, NC_DOUBLE, &(mi->z_start)); } if(strcmp(MIstep, attname) == 0) miattget1(minc_fd,var_id, MIstep, NC_DOUBLE, &(mi->z_step)); } /* if it is a dynamic file, then we ask for the number of frames, the frame durations and start time*/ if(mi->dim == 4){ long start_time_vector[1]; long count_time_vector[1]; var_id = ncdimid(minc_fd, MItime); ncdiminq(minc_fd, var_id, NULL, &(mi->time_length)); if((mi->time_points = (double *) calloc(mi->time_length, sizeof(double))) == NULL) return NULL; if((mi->time_widths = (double *) calloc(mi->time_length, sizeof(double))) == NULL) return NULL; start_time_vector[0] = 0; count_time_vector[0] = mi->time_length; mivarget(minc_fd, var_id, start_time_vector, count_time_vector, NC_DOUBLE, MI_SIGNED, mi->time_points); mivarget(minc_fd, ncvarid(minc_fd, MItime_width), start_time_vector, count_time_vector, NC_DOUBLE, MI_SIGNED, mi->time_widths); } /*defining the modality*/ if(mivar_exists(minc_fd, MIstudy)) { var_id = ncvarid(minc_fd, MIstudy); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MImodality, attname) == 0) { miattgetstr(minc_fd, var_id, MImodality, NC_MAX_NAME, buff); if(strcmp(buff, MI_PET) == 0) mi->mod = PET; if(strcmp(buff, MI_MRI) == 0) mi->mod = MRI; if(strcmp(buff, MI_LABEL) == 0) mi->dunit = LABEL; } } } if(mivar_exists(minc_fd, MIimagemin)) { var_id = ncvarid(minc_fd, MIimagemin); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MIunits, attname) == 0) { miattgetstr(minc_fd, var_id, MIunits, NC_MAX_NAME, buff); if(strcmp(buff, "nCi/cc") == 0) mi->dunit = NCIPERCC; } } } /*getting isotope halflife if available*/ if(mivar_exists(minc_fd, MIacquisition)) { var_id = ncvarid(minc_fd, MIacquisition); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp(MIradionuclide_halflife, attname) == 0) miattget1(minc_fd, var_id, MIradionuclide_halflife, NC_FLOAT, &(mi->isotope_halflife)); } } miclose(minc_fd); return mi; } void free_minc_info(minc_info *mi) { if(mi->time_points != NULL) free(mi->time_points); if(mi->time_widths != NULL) free(mi->time_widths); } void get_patient_info(char *file, Main_header *mh) { int minc_fd; /*minc file descriptor*/ /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, MIpatient)) { int var_id; int num_att; int i; var_id = ncvarid(minc_fd, MIpatient); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { char attname[NC_MAX_NAME+1]; char buffer_val[NC_MAX_NAME+1]; ncattname(minc_fd,var_id,i,attname); if(strcmp(MIfull_name, attname) == 0) miattgetstr(minc_fd, var_id, MIfull_name, 32, mh->patient_name); if(strcmp(MIage, attname) == 0) miattget1(minc_fd, var_id, MIage, NC_FLOAT, &(mh->patient_age)); if(strcmp(MIweight, attname) == 0) miattget1(minc_fd, var_id, MIweight, NC_FLOAT, &(mh->patient_weight)); if(strcmp(MIsize, attname) == 0) miattget1(minc_fd, var_id, MIsize, NC_FLOAT, &(mh->patient_height)); if(strcmp(MIsex, attname) == 0) { miattgetstr(minc_fd, var_id, MIsex, NC_MAX_NAME+1, buffer_val); if(strcmp(buffer_val, MI_MALE) == 0) mh->patient_sex[0] = 'M'; if(strcmp(buffer_val, MI_FEMALE) == 0) mh->patient_sex[0] = 'F'; } if(strcmp(MIidentification, attname) == 0) miattgetstr(minc_fd, var_id, MIidentification, 16, mh->patient_id); /*I don't kown how to deal with the patient birth date yet in minc, it is a string, in ecat a int ... I guess this is not important as long as we have the age of the patient Might be filled below if the ecat-header variable exists */ } } miclose(minc_fd); } void get_study_info(char *file, Main_header *mh) { int minc_fd; /*minc file descriptor*/ /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, MIstudy)) { int var_id; int i; struct tm scan_time; int num_att; var_id = ncvarid(minc_fd, MIstudy); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { char attname[NC_MAX_NAME+1]; ncattname(minc_fd,var_id,i,attname); if(strcmp(MIstart_year, attname) == 0) { miattget1(minc_fd, var_id, MIstart_year, NC_INT, &scan_time.tm_year); scan_time.tm_year -= 1900; } if(strcmp(MIstart_month, attname) == 0) { miattget1(minc_fd, var_id, MIstart_month, NC_INT, &scan_time.tm_mon); scan_time.tm_mon--; } if(strcmp(MIstart_day, attname) == 0) miattget1(minc_fd, var_id, MIstart_day, NC_INT, &scan_time.tm_mday); if(strcmp(MIstart_hour, attname) == 0) miattget1(minc_fd, var_id, MIstart_hour, NC_INT, &scan_time.tm_hour); if(strcmp(MIstart_minute, attname) == 0) miattget1(minc_fd, var_id, MIstart_minute, NC_INT, &scan_time.tm_min); if(strcmp(MIstart_seconds, attname) == 0) miattget1(minc_fd, var_id, MIstart_seconds, NC_INT, &scan_time.tm_sec); if(strcmp(MIidentification, attname) == 0) miattgetstr(minc_fd, var_id, MIidentification, 16, mh->patient_id); if(strcmp(MIstudy_id, attname) == 0) miattgetstr(minc_fd, var_id, MIstudy_id, 12, mh->study_name); if(strcmp(MIreferring_physician, attname) == 0) miattgetstr(minc_fd, var_id, MIreferring_physician, 32, mh->physician_name); if(strcmp(MIoperator, attname) == 0) miattgetstr(minc_fd, var_id, MIoperator, 32, mh->operator_name); } mh->scan_start_time = mktime(&scan_time); } miclose(minc_fd); } void get_acquisition_info(char *file, Main_header *mh) { int minc_fd; /*minc file descriptor*/ /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, MIacquisition)) { int var_id; int num_att; int i; struct tm injection_time; var_id = ncvarid(minc_fd, MIacquisition); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { char attname[NC_MAX_NAME+1]; ncattname(minc_fd,var_id,i,attname); if(strcmp(MIradionuclide, attname) == 0) miattgetstr(minc_fd, var_id, MIradionuclide, 8, mh->isotope_code); if(strcmp(MIradionuclide_halflife, attname) == 0) miattget1(minc_fd, var_id, MIradionuclide_halflife, NC_FLOAT, &(mh->isotope_halflife)); if(strcmp(MItracer, attname) == 0) miattgetstr(minc_fd, var_id, MItracer, 32, mh->radiopharmaceutical); if(strcmp(MIinjection_year, attname) == 0) { miattget1(minc_fd, var_id, MIinjection_year, NC_INT, &injection_time.tm_year); injection_time.tm_year -= 1900; } if(strcmp(MIinjection_month, attname) == 0) { miattget1(minc_fd, var_id, MIinjection_month, NC_INT, &injection_time.tm_mon); injection_time.tm_mon--; } if(strcmp(MIinjection_day, attname) == 0) miattget1(minc_fd, var_id, MIinjection_day, NC_INT, &injection_time.tm_mday); if(strcmp(MIinjection_hour, attname) == 0) miattget1(minc_fd, var_id, MIinjection_hour, NC_INT, &injection_time.tm_hour); if(strcmp(MIinjection_minute, attname) == 0) miattget1(minc_fd, var_id, MIinjection_minute, NC_INT, &injection_time.tm_min); if(strcmp(MIinjection_seconds, attname) == 0) miattget1(minc_fd, var_id, MIinjection_seconds, NC_INT, &injection_time.tm_sec); if(strcmp(MIinjection_dose, attname) == 0) miattget1(minc_fd, var_id, MIinjection_dose, NC_FLOAT, &(mh->dosage)); if(strcmp(MIdose_units, attname) == 0) miattgetstr(minc_fd, var_id, MIdose_units, 32, mh->data_units); } mh->dose_start_time = mktime(&injection_time); } miclose(minc_fd); } void get_ecat_acquisition_info(char *file, Main_header *mh) { int minc_fd; /*minc file descriptor*/ /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, "ecat_acquisition")) { int var_id; int num_att; int i; char attname[NC_MAX_NAME+1]; var_id = ncvarid(minc_fd, "ecat_acquisition"); ncvarinq(minc_fd, var_id, NULL, NULL, NULL, NULL, &num_att); for(i = 0; i < num_att; i++) { ncattname(minc_fd,var_id,i,attname); if(strcmp("septa_retracted", attname) == 0) { char buff[NC_MAX_NAME+1]; miattgetstr(minc_fd, var_id, "septa_retracted", NC_MAX_NAME+1, buff); if(strcmp(buff, MI_TRUE) == 0) mh->septa_state = 1; if(strcmp(buff, MI_FALSE) == 0) mh->septa_state = 0; } } } miclose(minc_fd); } void get_ecat_main_header_info(char *file, Main_header *mh) { int minc_fd; /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, "ecat-main")) { int var_id; char buffer[NC_MAX_NAME+1]; var_id = ncvarid(minc_fd, "ecat-main"); miattgetstr(minc_fd, var_id, "Magic_Number", 14, mh->magic_number); miattgetstr(minc_fd, var_id, "Original_Filename", 32, mh->original_file_name); miattgetstr(minc_fd, var_id, "System_Type", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->system_type)); miattgetstr(minc_fd, var_id, "Serial_Number", 10, mh->serial_number); miattgetstr(minc_fd, var_id, "Scan_Start_Time", sizeof(buffer), buffer); sscanf(buffer, "%u", &(mh->scan_start_time)); miattgetstr(minc_fd, var_id, "Isotope_Name", 8, mh->isotope_code); miattgetstr(minc_fd, var_id, "Isotope_Halflife", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->isotope_halflife)); miattgetstr(minc_fd, var_id, "Radiopharmaceutical", 32, mh->radiopharmaceutical); miattgetstr(minc_fd, var_id, "Gantry_Tilt", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->gantry_tilt)); miattgetstr(minc_fd, var_id, "Gantry_Rotation", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->gantry_rotation)); miattgetstr(minc_fd, var_id, "Bed_Elevation", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->bed_elevation)); miattgetstr(minc_fd, var_id, "Intrinsic_Tilt", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->intrinsic_tilt)); miattgetstr(minc_fd, var_id, "Wobble_Speed", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->wobble_speed)); miattgetstr(minc_fd, var_id, "Transm_Source_Type", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->transm_source_type)); miattgetstr(minc_fd, var_id, "Distance_Scanned", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->distance_scanned)); miattgetstr(minc_fd, var_id, "Transaxial_Fov", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->transaxial_fov)); miattgetstr(minc_fd, var_id, "Angular_Compression", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->angular_compression)); miattgetstr(minc_fd, var_id, "Coin_Samp_Mode", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->coin_samp_mode)); miattgetstr(minc_fd, var_id, "Axial_Samp_Mode", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->axial_samp_mode)); miattgetstr(minc_fd, var_id, "Calibration_Factor", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->calibration_factor)); miattgetstr(minc_fd, var_id, "Study_Type", 12, mh->study_name); miattgetstr(minc_fd, var_id, "Patient_Id", 16, mh->patient_id); miattgetstr(minc_fd, var_id, "Patient_Name", 32, mh->patient_name); miattgetstr(minc_fd, var_id, "Patient_Sex", sizeof(buffer), buffer); sscanf(buffer, "%c", mh->patient_sex); miattgetstr(minc_fd, var_id, "Patient_Dexterity", sizeof(buffer), buffer); sscanf(buffer, "%c", mh->patient_dexterity); miattgetstr(minc_fd, var_id, "Patient_Age", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->patient_age)); miattgetstr(minc_fd, var_id, "Patient_Height", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->patient_height)); miattgetstr(minc_fd, var_id, "Patient_Weight", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->patient_weight)); miattgetstr(minc_fd, var_id, "Patient_Birth_Date", sizeof(buffer), buffer); sscanf(buffer, "%d", &(mh->patient_birth_date)); miattgetstr(minc_fd, var_id, "Physician_Name", 32, mh->physician_name); miattgetstr(minc_fd, var_id, "Operator_Name", 32, mh->operator_name); miattgetstr(minc_fd, var_id, "Study_Description", 32, mh->study_description); /*bugg -> in minc lib, acquisition should be replaced by acquisition*/ miattgetstr(minc_fd, var_id, "Acquision_Type", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->acquisition_type)); miattgetstr(minc_fd, var_id, "Patient_Orientation", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->patient_orientation)); miattgetstr(minc_fd, var_id, "Facility_Name", 20, mh->facility_name); miattgetstr(minc_fd, var_id, "Lwr_Sctr_Thres", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->lwr_sctr_thres)); miattgetstr(minc_fd, var_id, "Lwr_True_Thres", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->lwr_true_thres)); miattgetstr(minc_fd, var_id, "Upr_True_Thres", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->upr_true_thres)); miattgetstr(minc_fd, var_id, "User_Process_Code", 10, mh->user_process_code); miattgetstr(minc_fd, var_id, "Acquisition_Mode", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->acquisition_mode)); miattgetstr(minc_fd, var_id, "Bin_Size", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->bin_size)); miattgetstr(minc_fd, var_id, "Branching_Fraction", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->branching_fraction)); miattgetstr(minc_fd, var_id, "Dose_Start_Time", sizeof(buffer), buffer); sscanf(buffer, "%u", &(mh->dose_start_time)); miattgetstr(minc_fd, var_id, "Dosage", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->dosage)); miattgetstr(minc_fd, var_id, "Well_Counter_Corr_Factor", sizeof(buffer), buffer); sscanf(buffer, "%f", &(mh->well_counter_factor)); miattgetstr(minc_fd, var_id, "Data_Units", 32, mh->data_units); miattgetstr(minc_fd, var_id, "Septa_State", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(mh->septa_state)); } miclose(minc_fd); } void get_ecat_subheader_info(char *file, Image_subheader *sh) { int minc_fd; /*minc file descriptor*/ /* we first open the netcdf file*/ minc_fd = miopen(file, NC_NOWRITE); if(mivar_exists(minc_fd, "ecat-subhdr")) { int var_id; char buffer[NC_MAX_NAME]; var_id = ncvarid(minc_fd, "ecat-subhdr"); miattgetstr(minc_fd, var_id, "Recon_Zoom", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->recon_zoom)); miattgetstr(minc_fd, var_id, "Filter_Code", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->filter_code)); miattgetstr(minc_fd, var_id, "X_Resolution", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->x_resolution)); miattgetstr(minc_fd, var_id, "Y_Resolution", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->y_resolution)); miattgetstr(minc_fd, var_id, "Z_Resolution", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->z_resolution)); miattgetstr(minc_fd, var_id, "X_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->num_r_elements)); miattgetstr(minc_fd, var_id, "Y_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->num_angles)); miattgetstr(minc_fd, var_id, "Z_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->z_rotation_angle)); miattgetstr(minc_fd, var_id, "Decay_Corr_Fctr", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->decay_corr_fctr)); miattgetstr(minc_fd, var_id, "Corrections_Applied", sizeof(buffer), buffer); sscanf(buffer, "%d", &(sh->processing_code)); miattgetstr(minc_fd, var_id, "Gate_Duration", sizeof(buffer), buffer); sscanf(buffer, "%u", &(sh->gate_duration)); miattgetstr(minc_fd, var_id, "R_Wave_Offset", sizeof(buffer), buffer); sscanf(buffer, "%d", &(sh->r_wave_offset)); miattgetstr(minc_fd, var_id, "Num_Accepted_Beats", sizeof(buffer), buffer); sscanf(buffer, "%d", &(sh->num_accepted_beats)); miattgetstr(minc_fd, var_id, "Filter_Cutoff_Frequency", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->filter_cutoff_frequency)); miattgetstr(minc_fd, var_id, "Filter_Dc_Component", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->filter_resolution)); miattgetstr(minc_fd, var_id, "Filter_Ramp_Slope", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->filter_ramp_slope)); miattgetstr(minc_fd, var_id, "Filter_Order", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->filter_order)); miattgetstr(minc_fd, var_id, "Annotation", 40, sh->annotation); miattgetstr(minc_fd, var_id, "Da_X_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_1_1)); miattgetstr(minc_fd, var_id, "Da_Y_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_1_2)); miattgetstr(minc_fd, var_id, "Da_Z_Rotation_Angle", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_1_3)); miattgetstr(minc_fd, var_id, "Da_X_Translation", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_2_1)); miattgetstr(minc_fd, var_id, "Da_Y_Translation", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_2_2)); miattgetstr(minc_fd, var_id, "Da_Z_Translation", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_2_3)); miattgetstr(minc_fd, var_id, "Da_X_Scale_Factor", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_3_1)); miattgetstr(minc_fd, var_id, "Da_Y_Scale_Factor", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_3_2)); miattgetstr(minc_fd, var_id, "Da_Z_Scale_Factor", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->mt_3_3)); miattgetstr(minc_fd, var_id, "Rfilter_Cutoff", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->rfilter_cutoff)); miattgetstr(minc_fd, var_id, "Rfilter_Resolution", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->rfilter_resolution)); miattgetstr(minc_fd, var_id, "Rfilter_Code", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->rfilter_code)); miattgetstr(minc_fd, var_id, "Rfilter_Order", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->rfilter_order)); miattgetstr(minc_fd, var_id, "Zfilter_Cutoff", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->zfilter_cutoff)); miattgetstr(minc_fd, var_id, "Zfilter_Resolution", sizeof(buffer), buffer); sscanf(buffer, "%f", &(sh->zfilter_resolution)); miattgetstr(minc_fd, var_id, "Zfilter_Code", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->zfilter_code)); miattgetstr(minc_fd, var_id, "Zfilter_Order", sizeof(buffer), buffer); sscanf(buffer, "%hd", &(sh->zfilter_order)); } miclose(minc_fd); } Main_header * init_main_header(char *file, minc_info *mi) { Main_header *mh = NULL; if((mh = (Main_header *)calloc(1, sizeof(Main_header))) == NULL) return NULL; /**************************************************************************** * first, we filled the compulsory fields * ****************************************************************************/ /*software version 7*/ mh->sw_version = 72; /*well ecat is a format for PET data, so MRI data in the minc format will have the PetVolume flag in ecat ....*/ mh->file_type = PetVolume; mh->num_planes = mi->z_size; mh->num_frames = (mi->dim == 4)?mi->time_length:1; mh->num_gates = 1; /*not handled for now*/ mh->num_bed_pos = 0; /*not handles for now*/ /*inital bed position, I do the backward calculation of ecattominc - !! MIstart may not exist*/ mh->init_bed_position = -mi->z_start/10.; mh->init_bed_position = (mi->z_step < 0)?-mi->z_start/10.:-(mi->z_step * (mi->z_size - 1) + mi->z_start)/10.; mh->bed_offset[0] = 0; /*for now, the distance_scanned is equal to the number of axial planes times the plane width*/ mh->distance_scanned = fabs(mi->z_step) * (mi->z_size + 1)/10.0; /*cm*/ mh->plane_separation = fabs(mi->z_step)/10.; mh->calibration_factor = 1; /*forced*/ mh->calibration_units = 2; /*2 = processed, by default*/ mh->calibration_units_label = 0; /*by default*/ if(mi->dunit == NCIPERCC) { mh->calibration_units = 1; mh->calibration_units_label = 1; } mh->patient_sex[0] = 'U'; /*for now unknown*/ mh->patient_dexterity[0] = 'U'; /*unknown for now*/; return mh; } Image_subheader * init_image_subheader(char *file, minc_info *mi, Main_header *mh) { Image_subheader *sh = NULL; if((sh = (Image_subheader *)calloc(1, sizeof(Image_subheader))) == NULL) return NULL; sh->data_type = SunShort; /*forced*/ sh->num_dimensions = 3; /*forced*/ sh->x_dimension = mi->x_size; sh->y_dimension = mi->y_size; sh->z_dimension = mi->z_size; sh->x_pixel_size = fabs(mi->x_step)/10.0; /*in cm*/ sh->y_pixel_size = fabs(mi->y_step)/10.0; sh->z_pixel_size = fabs(mi->z_step)/10.0; sh->x_offset = 0; if(mi->x_start_flag) sh->x_offset = -(mi->x_start + (mi->x_size - 1.0)/2.0 * mi->x_step)/10.; sh->y_offset = 0; if(mi->y_start_flag) sh->y_offset = (mi->y_start + (mi->y_size - 1.0)/2.0 * mi->y_step)/10.; sh->z_offset = 0; return sh; } void write_ecat_frame(MatrixFile *mf, Image_subheader *sh, short *ptr,int fr, float min, float max) { MatrixData md; md.matnum = 0; md.matfile = mf; md.mat_type = mf->mhptr->file_type; md.data_type = sh->data_type; md.shptr = sh; md.data_ptr = ptr; md.xdim = sh->x_dimension; md.ydim = sh->y_dimension; md.zdim = sh->z_dimension; md.scale_factor = sh->scale_factor; md.pixel_size = sh->x_pixel_size; md.y_size = sh->y_pixel_size; md.z_size = sh->z_pixel_size; md.data_min = min; md.data_max = max; md.data_size = (sh->x_dimension) * (sh->y_dimension) * (sh->z_dimension) * sizeof(short int); md.x_origin = md.y_origin = md.z_origin = 0.0; matrix_write(mf,mat_numcod(fr + 1, 1, 1, 0, 0) , &md); } void usage_error(char *progname) { (void) fprintf(stderr, "\nUsage: %s [] \n", progname); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(EXIT_FAILURE); } minc-tools-2.3.00+dfsg/conversion/minctoecat/machine_indep.c0000644000175000000620000002141712574624760023071 0ustar stevestaff#include #include #define __USE_XOPEN #include #include "ecat_write.h" #define OK 0 #define ERROR -1 #ifdef _WIN32 unsigned short ntohs(unsigned short us) { unsigned char *p = (unsigned char*)&us; return ((unsigned short)p[1] + (p[0] >> 8)); } unsigned long ntohl(unsigned long ul) { unsigned char *p = (unsigned char*)&ul; return ((unsigned long)p[3] + (p[2] >> 8) + (p[1] >> 16) + (p[0] >> 24)); } #else #include #endif void SWAB(const void *from, void *to, int length) { if (ntohs(1) == 1) swab(from, to, length); else memcpy(to,from,length); } void SWAW ( const void *from, void *to, int length) { if (ntohs(1) == 1) swaw(from,to,length); else memcpy(to,from,length*2); } float vaxftohf( bufr, off) unsigned short *bufr; int off; { unsigned int sign_exp, high, low, mantissa, ret; unsigned u = (bufr[off+1] << 16) + bufr[off]; if (u == 0) return 0.0; sign_exp = u & 0x0000ff80; sign_exp = (sign_exp - 0x0100) & 0xff80; high = u & 0x0000007f; low = u & 0xffff0000; mantissa = (high << 16) + (low >> 16); sign_exp = sign_exp << 16; ret = sign_exp + mantissa; return *(float*)(&ret); } #if defined(__alpha) || defined(_WIN32) /* LITTLE_ENDIAN : alpha, intel */ void ftovaxf(f, bufr) float f; unsigned short *bufr; { unsigned *u, sign_exp, low, high, mantissa, ret; float f_tmp = f; u = (unsigned*)(&f_tmp); if (u == 0) { bufr[0] = bufr[1] = 0; return; } sign_exp = *u & 0xff800000; sign_exp = sign_exp >> 16; sign_exp = (sign_exp + 0x0110) & 0xff80; low = *u & 0x0000ffff; high = *u & 0x007f0000; mantissa = (high >> 16) + (low << 16); ret = sign_exp + mantissa; bufr[0] = ret; bufr[1] = ret >>16; } #else /* BIG ENDIAN : sun hp sgi*/ void ftovaxf(orig,number) unsigned short number[2]; float orig; { /* convert from sun float to vax float */ union { unsigned short t[2]; float t4; } test; unsigned short int exp; number[0] = 0; number[1] = 0; test.t4 = orig; if (test.t4 == 0.0) return; number[1] = test.t[1]; exp = ((test.t[0] & 0x7f00) + 0x0100) & 0x7f00; test.t[0] = (test.t[0] & 0x80ff) + exp; number[0] = test.t[0]; } #endif /* LITTLE VS BIG ENDIAN*/ int file_data_to_host(dptr, nblks, dtype) char *dptr; int nblks, dtype; { int i, j; char *tmp = NULL; matrix_errno = 0; matrix_errtxt[0] = '\0'; if ((tmp = malloc(512)) == NULL) return ERROR; switch(dtype) { case ByteData: break; case VAX_Ix2: if (ntohs(1) == 1) for (i=0, j=0; i] \fR \fBminctoecat [\-help]\fR .SH "DESCRIPTION" \fIminctoecat\fR will convert a 2D image, a 3D volumes or a 4D dynamic volumes written in minc file format to a 2D, 3D or 4D Ecat7 file. Whereas the Ecat7 format has been designed for PET volume issued by Ecat scanners (CTI/SIEMENS \- Knoxville, TN, USA) it reads any minc volume type (static PET, dynamic PET, aMRI, fMRI, labels) natively delivered by any tomograph brand and transform it to a viable Ecat7 file in term of number of dimensions, dimension size, dimension step, orientation, spatial position, voxel values and voxel unit. Furthermore, it tries the best as it can to fill optional Ecat7 header fields from the available minc variables and attributes. By default, it extracts these additional information from common minc variables, namely: patient_variable, study_variable, acquisition_variable. In the case of volumes originally delivered by an Ecat PET scanners, then, other variable are generally available within the minc header: ecat_acquisition_variable, ecat_main and ecat_sub\-header_variable. There, for a such minc volume, the conversion yields a Ecat7 volume almost identical to the original native volume in terms of header fields. The user can alter this behaviour using the ignore\-list options (see the option section). .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH "Command specific options" .TP \fI\-ignore_patient_variable:\fR Ignore information from the minc patient variable (MIpatient). .TP \fI\-ignore_study_variable:\fR Ignore information from the minc study variable (MIstudy). .TP \fI\-ignore_acquisition_variable:\fR Ignore information from the minc acquisition variable (MIacquisition). .TP \fI\-ignore_ecat_accquisition_variable:\fR Ignore information from the minc ecat acquisition variable (MIecat_acquisition). .TP \fI\-ignore_ecat_main_variable:\fR Ignore information from the minc ecat\-mainheader variable (MIecat\-main). .TP \fI\-ignore_ecat_subheader_variable:\fR Ignore information from the minc ecat\-subheader variable (MIecat\-subheader). .TP \fI\-no_decay_corr_fctr:\fR do not regenerate the decay correction factors employed as part as the reconstruction of the original PET volume. The correction factors is a field in the ecat sub\-headers which can be used by processing programs. By default, if the volume is a PET volume ,those factors are regenerated. This option has no interest in the case of non PET data. .TP \fI\-label:\fR Treats voxel as integer values instead of floating point values. In this case, the scale factors and the calibration factors are both set to unity. This option is particularly useful when the input minc files is a volume of labels. .SH "General options" .TP \fI\-help:\fR Print summary of command\-line options and abort .TP \fI\-version:\fR Print the program's version and abort .SH "KNOWN BUGS" No bug listed so far :=) .SH "SEE ALSO" ecattominc(1), rawtominc(1), minctoraw(1), dicomtominc(1) .SH "AUTHOR" Anthonin Reilhac (anthonin.reilhac@cermep.fr) .SH "COPYRIGHTS" Copyrights 2005 by Anthonin Reilhac minc-tools-2.3.00+dfsg/conversion/minctoecat/ecat_write.c0000644000175000000620000004573112574624760022441 0ustar stevestaff#include "ecat_write.h" #include "machine_indep.h" #include "string.h" #define MatBLKSIZE 512 #define MatFirstDirBlk 2 #define NameLen 32 #define IDLen 16 #define OK 0 #define ERROR -1 #define V7 70 char* dstypecode[NumDataSetTypes] = { "u","s","i","a","n","pm","v8","v","p8","p","i8","S","S8","N", "FS"}; static char* magicNumber = "MATRIX"; struct MatDir { int matnum; int strtblk; int endblk; int matstat; }; struct matdir { int nmats, nmax; struct MatDir *entry; }; typedef struct matdirblk { int nfree, nextblk, prvblk, nused ; struct MatDir matdir[31] ; } MatDirBlk ; FILE *mat_create(const char *fname,Main_header *mhead); FILE *mat_open( const char *fname, char *fmode); int mat_write_main_header(FILE *fptr, Main_header *header); int map_main_header(char *bufr,Main_header *header); MatDirList *mat_read_directory(MatrixFile *mptr); MatDirBlk *mat_rdirblk(MatrixFile *file, int blknum); int write_host_data(MatrixFile *mptr, int matnum, MatrixData *data); int matrix_find(MatrixFile *matfile, int matnum,struct MatDir *matdir); int mat_enter(FILE *fptr, Main_header *mhptr, int matnum, int nblks); int insert_mdir(struct MatDir matdir, MatDirList *dirlist); int mat_write_image_subheader(FILE *fptr, Main_header *mhptr,int blknum, Image_subheader *header); int map_image_header(char *buf, Image_subheader *header); int matrix_freelist(MatDirList *matdirlist); int mat_close(FILE *fptr); MatrixFile *matrix_create(const char *fname, Main_header * proto_mhptr) { MatrixFile *mptr = NULL; FILE *fptr, *mat_create(); matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; fptr = mat_create(fname, proto_mhptr); if (!fptr) return( NULL ); mptr = (MatrixFile *) calloc(1, sizeof(MatrixFile)); if (!mptr) { fclose( fptr ); return( NULL ); } mptr->fptr = fptr; mptr->fname = (char *) malloc(strlen(fname) + 1); if (!mptr->fname) { free( mptr ); fclose( fptr ); return( NULL ); } strcpy(mptr->fname, fname); mptr->mhptr = (Main_header *) malloc(sizeof(Main_header)); if (!mptr->mhptr) { free( mptr->fname ); free( mptr ); fclose( fptr ); return( NULL ); } memcpy(mptr->mhptr, proto_mhptr, sizeof(Main_header)); mptr->dirlist = mat_read_directory(mptr); if (!mptr->dirlist) { free( mptr->fname ); free( mptr->mhptr ); free( mptr ); fclose( fptr ); return( NULL ); } return mptr; } FILE *mat_create(const char *fname,Main_header *mhead) { FILE *fptr; int bufr[MatBLKSIZE/sizeof(int)]; int ret; fptr = mat_open( fname, "w+"); if (!fptr) return( NULL ); ret = mat_write_main_header( fptr, mhead ); if( ret != 0 ) { mat_close( fptr); return( NULL ); } memset(bufr,0,MatBLKSIZE); bufr[0] = 31; bufr[1] = 2; ret = write_matrix_data(fptr,MatFirstDirBlk,1,(char*)bufr,SunLong); if( ret != 0 ) { mat_close( fptr); return( NULL ); } return (fptr); } FILE *mat_open( const char *fname, char *fmode) { FILE *fopen(), *fptr; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; fptr = fopen(fname, fmode); return (fptr); } int mat_write_main_header(FILE *fptr, Main_header *header) { char bufr[MatBLKSIZE]; map_main_header(bufr, header); return mat_wblk(fptr, 1, bufr, 1); /* write main header at block 1 */ } int map_main_header(char *bufr,Main_header *header) { int i = 0, j = 0; char mn[20]; /* set magic number */ sprintf(mn,"%s%d%s", magicNumber,header->sw_version, dstypecode[header->file_type]); bufWrite(mn, bufr, &i, 14); /* copy buffer into struct */ bufWrite(header->original_file_name, bufr, &i, NameLen); bufWrite_s(header->sw_version, bufr, &i); bufWrite_s(header->system_type, bufr, &i); bufWrite_s(header->file_type, bufr, &i); bufWrite(header->serial_number, bufr, &i, 10); bufWrite_u(header->scan_start_time, bufr, &i); bufWrite(header->isotope_code, bufr, &i, 8); bufWrite_f(header->isotope_halflife, bufr, &i); bufWrite(header->radiopharmaceutical, bufr, &i, NameLen); bufWrite_f(header->gantry_tilt, bufr, &i); bufWrite_f(header->gantry_rotation, bufr, &i); bufWrite_f(header->bed_elevation, bufr, &i); bufWrite_f(header->intrinsic_tilt, bufr, &i); bufWrite_s(header->wobble_speed, bufr, &i); bufWrite_s(header->transm_source_type, bufr, &i); bufWrite_f(header->distance_scanned, bufr, &i); bufWrite_f(header->transaxial_fov, bufr, &i); bufWrite_s(header->angular_compression, bufr, &i); bufWrite_s(header->coin_samp_mode, bufr, &i); bufWrite_s(header->axial_samp_mode, bufr, &i); bufWrite_f(header->calibration_factor, bufr, &i); bufWrite_s(header->calibration_units, bufr, &i); bufWrite_s(header->calibration_units_label, bufr, &i); bufWrite_s(header->compression_code, bufr, &i); bufWrite(header->study_name, bufr, &i, 12); bufWrite(header->patient_id, bufr, &i, IDLen); bufWrite(header->patient_name, bufr, &i, NameLen); bufWrite(header->patient_sex, bufr, &i, 1); bufWrite(header->patient_dexterity, bufr, &i, 1); bufWrite_f(header->patient_age, bufr, &i); bufWrite_f(header->patient_height, bufr, &i); bufWrite_f(header->patient_weight, bufr, &i); bufWrite_i(header->patient_birth_date, bufr, &i); bufWrite(header->physician_name, bufr, &i, NameLen); bufWrite(header->operator_name, bufr, &i, NameLen); bufWrite(header->study_description, bufr, &i, NameLen); bufWrite_s(header->acquisition_type, bufr, &i); bufWrite_s(header->patient_orientation, bufr, &i); bufWrite(header->facility_name, bufr, &i, 20); bufWrite_s(header->num_planes, bufr, &i); bufWrite_s(header->num_frames, bufr, &i); bufWrite_s(header->num_gates, bufr, &i); bufWrite_s(header->num_bed_pos, bufr, &i); bufWrite_f(header->init_bed_position, bufr, &i); for(j = 0; j < 15; j++) bufWrite_f(header->bed_offset[j], bufr, &i); bufWrite_f(header->plane_separation, bufr, &i); bufWrite_s(header->lwr_sctr_thres, bufr, &i); bufWrite_s(header->lwr_true_thres, bufr, &i); bufWrite_s(header->upr_true_thres, bufr, &i); bufWrite(header->user_process_code, bufr, &i, 10); bufWrite_s(header->acquisition_mode, bufr, &i); bufWrite_f(header->bin_size, bufr, &i); bufWrite_f(header->branching_fraction, bufr, &i); bufWrite_u(header->dose_start_time, bufr, &i); bufWrite_f(header->dosage, bufr, &i); bufWrite_f(header->well_counter_factor, bufr, &i); bufWrite(header->data_units, bufr, &i, 32); bufWrite_s(header->septa_state, bufr, &i); return 1; } int mat_wblk(FILE *fptr,int blkno, char *bufr,int nblks) { int err; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; /* seek to position in file */ err = fseek(fptr, (blkno - 1) * MatBLKSIZE, 0); if (err) return (ERROR); /* write matrix data */ err = fwrite(bufr, 1, nblks * MatBLKSIZE, fptr); if (err == -1) return (ERROR); if (err != nblks * MatBLKSIZE) { matrix_errno = MAT_WRITE_ERROR; return (ERROR); } return (0); } MatDirList *mat_read_directory(MatrixFile *mptr) { struct MatDir matdir; MatDirList *dirlist; MatDirBlk *matdirblk; int i, blknum; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; dirlist = (MatDirList *) calloc(1, sizeof(MatDirList)); if (dirlist == NULL) return (NULL); blknum = MatFirstDirBlk; do { matdirblk = mat_rdirblk(mptr, blknum); if (matdirblk == NULL) { free(dirlist); return (NULL); } for (i = 0; i < matdirblk->nused; i++) { matdir.matnum = matdirblk->matdir[i].matnum; matdir.strtblk = matdirblk->matdir[i].strtblk; matdir.endblk = matdirblk->matdir[i].endblk; matdir.matstat = matdirblk->matdir[i].matstat; insert_mdir(matdir, dirlist); } blknum = matdirblk->nextblk; free(matdirblk); } while (blknum != MatFirstDirBlk); return (dirlist); } MatDirBlk *mat_rdirblk(MatrixFile *file, int blknum) { MatDirBlk *matdirblk; int i, j, err, ndirs; int dirbufr[MatBLKSIZE / 4]; FILE *fptr = file->fptr; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; matdirblk = (MatDirBlk *) malloc(MatBLKSIZE); if (matdirblk == NULL) return (NULL); err = read_matrix_data(fptr, blknum, 1, (char *) dirbufr, SunLong); if (err == ERROR) { free(matdirblk); return (NULL); } matdirblk->nfree = dirbufr[0]; matdirblk->nextblk = dirbufr[1]; matdirblk->prvblk = dirbufr[2]; matdirblk->nused = dirbufr[3]; if (matdirblk->nused > 31) { matrix_errno = MAT_INVALID_DIRBLK; free(matdirblk); return (NULL); } ndirs = (MatBLKSIZE / 4 - 4) / 4; for (i = 0; i < ndirs; i++) { matdirblk->matdir[i].matnum = 0; matdirblk->matdir[i].strtblk = 0; matdirblk->matdir[i].endblk = 0; matdirblk->matdir[i].matstat = 0; } for (i = 0; i < matdirblk->nused; i++) { j = i + 1; matdirblk->matdir[i].matnum = dirbufr[j * 4 + 0]; matdirblk->matdir[i].strtblk = dirbufr[j * 4 + 1]; matdirblk->matdir[i].endblk = dirbufr[j * 4 + 2]; matdirblk->matdir[i].matstat = dirbufr[j * 4 + 3]; } return (matdirblk); } int matrix_write(MatrixFile *mptr, int matnum,MatrixData *data) { matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; if (mptr == NULL) matrix_errno = MAT_READ_FROM_NILFPTR ; else if (mptr->mhptr == NULL) matrix_errno = MAT_NOMHD_FILE_OBJECT ; else if (data->shptr == NULL) matrix_errno = MAT_NIL_SHPTR ; else if (data->data_ptr == NULL) matrix_errno = MAT_NIL_DATA_PTR ; if (matrix_errno != OK) return (ERROR) ; return write_host_data(mptr, matnum, data); } int write_host_data(MatrixFile *mptr, int matnum, MatrixData *data) { struct MatDir matdir, dir_entry ; Image_subheader *imagesub ; int status, blkno, nblks ; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; status = OK ; nblks = (data->data_size+511)/512; if (matrix_find(mptr, matnum, &matdir) == ERROR) { blkno = mat_enter(mptr->fptr, mptr->mhptr, matnum, nblks) ; if( blkno == ERROR ) return( ERROR ); dir_entry.matnum = matnum ; dir_entry.strtblk = blkno ; /*le 24 Avril 2001, j'ai enleve le -1*/ dir_entry.endblk = dir_entry.strtblk + nblks /*- 1*/ ; dir_entry.matstat = 1 ; insert_mdir(dir_entry, mptr->dirlist) ; matdir = dir_entry ; } imagesub = (Image_subheader *) data->shptr ; if (imagesub == NULL) { imagesub = (Image_subheader *) calloc(1, MatBLKSIZE); data->shptr = imagesub; } /* use MatrixData info */ imagesub->x_pixel_size = data->pixel_size; imagesub->y_pixel_size = data->y_size; imagesub->z_pixel_size = data->z_size; imagesub->num_dimensions = 3; imagesub->x_dimension = data->xdim; imagesub->y_dimension = data->ydim; imagesub->z_dimension = data->zdim; imagesub->image_max = (int)(data->data_max/data->scale_factor); imagesub->image_min = (int)(data->data_min/data->scale_factor); imagesub->scale_factor = data->scale_factor; imagesub->data_type = data->data_type; if( mat_write_image_subheader(mptr->fptr,mptr->mhptr,matdir.strtblk, imagesub) == ERROR ) return( ERROR ); status = write_matrix_data(mptr->fptr, matdir.strtblk+1, nblks, data->data_ptr, imagesub->data_type) ; if( status == ERROR ) return( ERROR ); return(status) ; } int matrix_find(MatrixFile *matfile, int matnum,struct MatDir *matdir) { MatDirNode *node ; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; if (matfile == NULL) return(ERROR) ; if (matfile->dirlist == NULL) return(ERROR) ; node = matfile->dirlist->first ; while (node != NULL) { if (node->matnum == matnum) { matdir->matnum = node->matnum ; matdir->strtblk = node->strtblk ; matdir->endblk = node->endblk ; matdir->matstat = node->matstat ; break ; } node = node->next ; } if (node != NULL) return(OK) ; else return(ERROR) ; } int mat_enter(FILE *fptr, Main_header *mhptr, int matnum, int nblks) { int dirblk, dirbufr[128], i, nxtblk, busy, oldsize; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; dirblk = MatFirstDirBlk; if( fseek(fptr, 0, 0) ) return( ERROR ); /* * nfs locks are very time consuming lockf( fileno(fptr), F_LOCK, 0); */ if (read_matrix_data(fptr, dirblk, 1, (char *) dirbufr, SunLong) == ERROR) return (ERROR); busy = 1; while (busy) { nxtblk = dirblk + 1; for (i = 4; i < 128; i += 4) { if (dirbufr[i] == 0) { busy = 0; break; } else if (dirbufr[i] == matnum) { oldsize = dirbufr[i + 2] - dirbufr[i + 1] + 1; if (oldsize < nblks) { dirbufr[i] = 0xFFFFFFFF; write_matrix_data(fptr, dirblk, 1, (char*)dirbufr, SunLong); nxtblk = dirbufr[i + 2] + 1; } else { nxtblk = dirbufr[i + 1]; dirbufr[0]++; dirbufr[3]--; busy = 0; break; } } else { nxtblk = dirbufr[i + 2] + 1; } } if (!busy) break; if (dirbufr[1] != MatFirstDirBlk) { dirblk = dirbufr[1]; if (read_matrix_data(fptr, dirblk, 1, (char *) dirbufr, SunLong) == ERROR) return (ERROR); } else { dirbufr[1] = nxtblk; if (write_matrix_data(fptr, dirblk, 1, (char *) dirbufr, SunLong) == ERROR) return (ERROR); dirbufr[0] = 31; dirbufr[1] = MatFirstDirBlk; dirbufr[2] = dirblk; dirbufr[3] = 0; dirblk = nxtblk; for (i = 4; i < 128; i++) dirbufr[i] = 0; } } dirbufr[i] = matnum; dirbufr[i + 1] = nxtblk; dirbufr[i + 2] = nxtblk + nblks; dirbufr[i + 3] = 1; dirbufr[0]--; dirbufr[3]++; if (write_matrix_data(fptr, dirblk, 1, (char*)dirbufr, SunLong) == ERROR) return (ERROR); if( fseek(fptr, 0, 0) ) return( ERROR ); /* * nfs locks are very time consuming lockf( fileno(fptr), F_UNLOCK, 0); */ return (nxtblk); } int insert_mdir(struct MatDir matdir, MatDirList *dirlist) { MatDirNode *node ; matrix_errno = MAT_OK; matrix_errtxt[0] = '\0'; if (dirlist == NULL) { dirlist = (MatDirList *) malloc(sizeof(MatDirList)) ; if (dirlist == NULL) return(ERROR) ; dirlist->nmats = 0 ; dirlist->first = NULL ; dirlist->last = NULL ; } node = (MatDirNode *) malloc(sizeof(MatDirNode)) ; if (node == NULL) return(ERROR) ; node->matnum = matdir.matnum ; node->strtblk = matdir.strtblk ; node->endblk = matdir.endblk ; node->matstat = matdir.matstat; node->next = NULL ; if (dirlist->first == NULL) /* if list was empty, add first node */ { dirlist->first = node ; dirlist->last = node ; dirlist->nmats = 1 ; } else { (dirlist->last)->next = node ; dirlist->last = node ; ++(dirlist->nmats) ; } return OK; } int mat_write_image_subheader(FILE *fptr, Main_header *mhptr,int blknum, Image_subheader *header) { char buf[MatBLKSIZE]; map_image_header(buf, header); return mat_wblk(fptr, blknum, buf, 1); } int map_image_header(char *buf, Image_subheader *header) { int i = 0; bufWrite_s(header->data_type, buf, &i); bufWrite_s(header->num_dimensions, buf, &i); bufWrite_s(header->x_dimension, buf, &i); bufWrite_s(header->y_dimension, buf, &i); bufWrite_s(header->z_dimension, buf, &i); bufWrite_f(header->z_offset, buf, &i); bufWrite_f(header->x_offset, buf, &i); bufWrite_f(header->y_offset, buf, &i); bufWrite_f(header->recon_zoom, buf, &i); bufWrite_f(header->scale_factor, buf, &i); bufWrite_s(header->image_min, buf, &i); bufWrite_s(header->image_max, buf, &i); bufWrite_f(header->x_pixel_size, buf, &i); bufWrite_f(header->y_pixel_size, buf, &i); bufWrite_f(header->z_pixel_size, buf, &i); bufWrite_u(header->frame_duration, buf, &i); bufWrite_u(header->frame_start_time, buf, &i); bufWrite_s(header->filter_code, buf, &i); bufWrite_f(header->x_resolution, buf, &i); bufWrite_f(header->y_resolution, buf, &i); bufWrite_f(header->z_resolution, buf, &i); bufWrite_f(header->num_r_elements, buf, &i); bufWrite_f(header->num_angles, buf, &i); bufWrite_f(header->z_rotation_angle, buf, &i); bufWrite_f(header->decay_corr_fctr, buf, &i); bufWrite_i(header->processing_code, buf, &i); bufWrite_u(header->gate_duration, buf, &i); bufWrite_i(header->r_wave_offset, buf, &i); bufWrite_i(header->num_accepted_beats, buf, &i); bufWrite_f(header->filter_cutoff_frequency, buf, &i); bufWrite_f(header->filter_resolution, buf, &i); bufWrite_f(header->filter_ramp_slope, buf, &i); bufWrite_s(header->filter_order, buf, &i); bufWrite_f(header->filter_scatter_fraction, buf, &i); bufWrite_f(header->filter_scatter_slope, buf, &i); bufWrite(header->annotation, buf, &i, 40); bufWrite_f(header->mt_1_1, buf, &i); bufWrite_f(header->mt_1_2, buf, &i); bufWrite_f(header->mt_1_3, buf, &i); bufWrite_f(header->mt_2_1, buf, &i); bufWrite_f(header->mt_2_2, buf, &i); bufWrite_f(header->mt_2_3, buf, &i); bufWrite_f(header->mt_3_1, buf, &i); bufWrite_f(header->mt_3_2, buf, &i); bufWrite_f(header->mt_3_3, buf, &i); bufWrite_f(header->rfilter_cutoff, buf, &i); bufWrite_f(header->rfilter_resolution, buf, &i); bufWrite_s(header->rfilter_code, buf, &i); bufWrite_s(header->rfilter_order, buf, &i); bufWrite_f(header->zfilter_cutoff, buf, &i); bufWrite_f(header->zfilter_resolution, buf, &i); bufWrite_s(header->zfilter_code, buf, &i); bufWrite_s(header->zfilter_order, buf, &i); bufWrite_f(header->mt_1_4, buf, &i); bufWrite_f(header->mt_2_4, buf, &i); bufWrite_f(header->mt_3_4, buf, &i); bufWrite_s(header->scatter_type, buf, &i); bufWrite_s(header->recon_type, buf, &i); bufWrite_s(header->recon_views, buf, &i); return 1; } int matrix_close(MatrixFile *mptr) { int status = OK; matrix_errno = MAT_OK; if (mptr->fname) strcpy(matrix_errtxt,mptr->fname); else matrix_errtxt[0] = '\0'; if (mptr == NULL) return status; if (mptr->mhptr != NULL) free(mptr->mhptr) ; if (mptr->dirlist != NULL) matrix_freelist(mptr->dirlist) ; if (mptr->fptr) status = fclose(mptr->fptr); if (mptr->fname) free(mptr->fname); free(mptr); return status; } int matrix_freelist(MatDirList *matdirlist) { MatDirNode *node, *next ; if (matdirlist == NULL) return OK; if (matdirlist->first != NULL) { node = matdirlist->first ; do { next = node->next ; free(node) ; node = next ; } while(next != NULL) ; } free(matdirlist) ; return OK; } int mat_numcod(int frame, int plane, int gate, int data, int bed) { return ((frame)|((bed&0xF)<<12)|((plane&0xFF)<<16)|(((plane&0x300)>>8)<<9)| ((gate&0x3F)<<24)|((data&0x3)<<30)|((data&0x4)<<9)); } void swaw( const short *from, short *to, int length) { short int temp; int i; for (i=0;i check if byte count less than (nblks-1) (M. Sibomana 23-oct-1997) */ else if( err < (nblks-1)*MatBLKSIZE ) { matrix_errno = MAT_READ_ERROR; return( ERROR ); } return( 0 ); } minc-tools-2.3.00+dfsg/conversion/minctoecat/machine_indep.h0000644000175000000620000000222512574624760023072 0ustar stevestaff#ifndef machine_indep_h #define machine_indep_h #include #if defined(__STDC__) || defined(__cplusplus) #if defined(__cplusplus) extern "C" { #endif void SWAB(const void *from, void *to, int length); int file_data_to_host(char *dptr, int nblks, int dtype); int read_matrix_data( FILE *fptr, int strtblk, int nblks, char *dptr, int dtype); int write_matrix_data( FILE *fptr, int strtblk, int nblks, char *dptr, int dtype); void bufWrite(char* s, char* buf, int* i, int len); void bufWrite_s(short val, char* buf, int* i); void bufWrite_i(int val, char* buf, int* i); void bufWrite_u(unsigned int val, char* buf, int* i); void bufWrite_f(float val, char* buf, int* i); void bufRead(char* s, char* buf, int* i, int len); void bufRead_s(short*, char* buf, int* i); void bufRead_i(int*, char* buf, int* i); void bufRead_u(unsigned int*, char* buf, int* i); void bufRead_f(float*, char* buf, int* i); #if defined(__cplusplus) } #endif /* __cpluplus */ #else /* __STDC__ */ extern void bufWrite(), bufWrite_s(), bufWrite_i(), bufWrite_f(); extern void bufRead(), bufRead_s(), bufRead_i(), bufRead_f(); #endif /* __STDC__ */ #endif /* machine_indep_h */ minc-tools-2.3.00+dfsg/conversion/minctoecat/ecat_write.h0000644000175000000620000001333112574624760022435 0ustar stevestaff#include #include typedef enum { ECAT6, ECAT7, Interfile } FileFormat; typedef enum { MAT_OK, MAT_READ_ERROR, MAT_WRITE_ERROR, MAT_INVALID_DIRBLK, MAT_ACS_FILE_NOT_FOUND, MAT_INTERFILE_OPEN_ERR, MAT_FILE_TYPE_NOT_MATCH, MAT_READ_FROM_NILFPTR, MAT_NOMHD_FILE_OBJECT, MAT_NIL_SHPTR, MAT_NIL_DATA_PTR, MAT_MATRIX_NOT_FOUND, MAT_UNKNOWN_FILE_TYPE, MAT_ACS_CREATE_ERR, MAT_BAD_ATTRIBUTE, MAT_BAD_FILE_ACCESS_MODE, MAT_INVALID_DIMENSION, MAT_NO_SLICES_FOUND, MAT_INVALID_DATA_TYPE, MAT_INVALID_MBED_POSITION } MatrixErrorCode; typedef enum { NoData, Sinogram, PetImage, AttenCor, Normalization, PolarMap, ByteVolume, PetVolume, ByteProjection, PetProjection, ByteImage, Short3dSinogram, Byte3dSinogram, Norm3d, Float3dSinogram,InterfileImage, NumDataSetTypes } DataSetType; typedef enum { UnknownMatDataType, ByteData, VAX_Ix2, VAX_Ix4, VAX_Rx4, IeeeFloat, SunShort, SunLong, NumMatrixDataTypes, ColorData, BitData } MatrixDataType; MatrixErrorCode matrix_errno; char matrix_errtxt[132]; typedef struct XMAIN_HEAD { char magic_number[14]; char original_file_name[32]; short sw_version; short system_type; short file_type; char serial_number[10]; short align_0; /* 4 byte alignment purpose */ unsigned int scan_start_time; char isotope_code[8]; float isotope_halflife; char radiopharmaceutical[32]; float gantry_tilt; float gantry_rotation; float bed_elevation; float intrinsic_tilt; short wobble_speed; short transm_source_type; float distance_scanned; float transaxial_fov; short angular_compression; short coin_samp_mode; short axial_samp_mode; short align_1; float calibration_factor; short calibration_units; short calibration_units_label; short compression_code; char study_name[12]; char patient_id[16]; char patient_name[32]; char patient_sex[1]; char patient_dexterity[1]; float patient_age; float patient_height; float patient_weight; int patient_birth_date; char physician_name[32]; char operator_name[32]; char study_description[32]; short acquisition_type; short patient_orientation; char facility_name[20]; short num_planes; short num_frames; short num_gates; short num_bed_pos; float init_bed_position; float bed_offset[15]; float plane_separation; short lwr_sctr_thres; short lwr_true_thres; short upr_true_thres; char user_process_code[10]; short acquisition_mode; short align_2; float bin_size; float branching_fraction; unsigned int dose_start_time; float dosage; float well_counter_factor; char data_units[32]; short septa_state; short align_3; } Main_header; typedef struct XIMAGE_SUB { short data_type; short num_dimensions; short x_dimension; short y_dimension; short z_dimension; short align_0; float z_offset; float x_offset; float y_offset; float recon_zoom; float scale_factor; short image_min; short image_max; float x_pixel_size; float y_pixel_size; float z_pixel_size; unsigned int frame_duration; unsigned int frame_start_time; short filter_code; short align_1; float x_resolution; float y_resolution; float z_resolution; float num_r_elements; float num_angles; float z_rotation_angle; float decay_corr_fctr; int processing_code; unsigned int gate_duration; int r_wave_offset; int num_accepted_beats; float filter_cutoff_frequency; float filter_resolution; float filter_ramp_slope; short filter_order; short align_2; float filter_scatter_fraction; float filter_scatter_slope; char annotation[40]; float mt_1_1; float mt_1_2; float mt_1_3; float mt_2_1; float mt_2_2; float mt_2_3; float mt_3_1; float mt_3_2; float mt_3_3; float rfilter_cutoff; float rfilter_resolution; short rfilter_code; short rfilter_order; float zfilter_cutoff; float zfilter_resolution; short zfilter_code; short zfilter_order; float mt_1_4; float mt_2_4; float mt_3_4; short scatter_type; short recon_type; short recon_views; short align_3; } Image_subheader; typedef struct matdirnode { int matnum ; int strtblk ; int endblk ; int matstat ; struct matdirnode *next ; } MatDirNode ; typedef struct matdirlist { int nmats ; MatDirNode *first ; MatDirNode *last ; } MatDirList ; typedef struct matrix_file { char *fname ; Main_header *mhptr ; MatDirList *dirlist ; FILE *fptr ; int acs ; FileFormat file_format; char **interfile_header; } MatrixFile; typedef struct matrixdata { int matnum ; /* matrix number */ MatrixFile *matfile ; /* pointer to parent */ DataSetType mat_type ; /* type of matrix? */ MatrixDataType data_type ; /* type of data */ void * shptr ; /* pointer to sub-header */ void * data_ptr ; /* pointer to data */ int data_size ; /* size of data in bytes */ int xdim; /* dimensions of data */ int ydim; /* y dimension */ int zdim; /* for volumes */ float scale_factor ; /* valid if data is int? */ float pixel_size; /* xdim data spacing (cm) */ float y_size; /* ydim data spacing (cm) */ float z_size; /* zdim data spacing (cm) */ float data_min; /* min value of data */ float data_max; /* max value of data */ float x_origin; /* x origin of data */ float y_origin; /* y origin of data */ float z_origin; /* z origin of data */ } MatrixData ; MatrixFile *matrix_create(const char *fname, Main_header * proto_mhptr); int matrix_write(MatrixFile *mptr, int matnum, MatrixData *data); int matrix_close(MatrixFile *mptr); int mat_numcod(int frame, int plane, int gate, int data, int bed); int mat_wblk(FILE *fptr,int blkno, char *bufr,int nblks); void swaw( const short *from, short *to, int length); int mat_rblk(FILE *fptr, int blkno, char *bufr, int nblks); minc-tools-2.3.00+dfsg/conversion/CMakeLists.txt0000644000175000000620000000351512574624760020553 0ustar stevestaff# CMakeFiles.txt for the MINC2 conversion progs # # Andrew Janke - a.janke@gmail.com #LINK_DIRECTORIES() LINK_LIBRARIES( ${LIBMINC_LIBRARIES}) ADD_DEFINITIONS(-DHAVE_CONFIG_H) # conversion progs (and libraries) INCLUDE_DIRECTORIES(Acr_nema) ADD_LIBRARY(acr_nema STATIC Acr_nema/acr_io.c Acr_nema/dicom_client_routines.c Acr_nema/dicom_network.c Acr_nema/element.c Acr_nema/file_io.c Acr_nema/globals.c Acr_nema/group.c Acr_nema/message.c Acr_nema/value_repr.c) ADD_EXECUTABLE(dcm2mnc dcm2mnc/dcm2mnc.c dcm2mnc/dicom_to_minc.c dcm2mnc/siemens_to_dicom.c dcm2mnc/dicom_read.c dcm2mnc/minc_file.c dcm2mnc/progress.c dcm2mnc/string_to_filename.c) TARGET_LINK_LIBRARIES(dcm2mnc acr_nema ) ADD_EXECUTABLE(ecattominc ecattominc/ecattominc.c ecattominc/insertblood.c ecattominc/ecat_file.c ecattominc/machine_indep.c ) ADD_EXECUTABLE(minctoecat minctoecat/minctoecat.c minctoecat/ecat_write.c minctoecat/machine_indep.c ) TARGET_LINK_LIBRARIES(minctoecat ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(mnc2nii nifti1/mnc2nii.c ) TARGET_LINK_LIBRARIES(mnc2nii ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(nii2mnc nifti1/nii2mnc.c ) TARGET_LINK_LIBRARIES(nii2mnc ${VOLUME_IO_LIBRARIES} ${LIBMINC_LIBRARIES} m) ADD_EXECUTABLE(upet2mnc micropet/upet2mnc.c ) IF(BUILD_MINC2) ADD_EXECUTABLE(vff2mnc vff2mnc/vff2mnc.c ) INSTALL(TARGETS vff2mnc DESTINATION bin) ENDIF(BUILD_MINC2) # install progs INSTALL(TARGETS dcm2mnc ecattominc minctoecat mnc2nii nii2mnc upet2mnc DESTINATION bin) INSTALL_MAN_PAGES( ${CMAKE_INSTALL_PREFIX}/man dcm2mnc/dcm2mnc.man1 ecattominc/ecattominc.man1 micropet/upet2mnc.man1 minctoecat/minctoecat.man1 nifti1/mnc2nii.man1 nifti1/nii2mnc.man1 vff2mnc/vff2mnc.man1 ) minc-tools-2.3.00+dfsg/conversion/vff2mnc/0002755000175000000620000000000012574624760017352 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/vff2mnc/vff2mnc.h0000644000175000000620000000470712574624760021072 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : vff2mnc.h @DESCRIPTION: Header file that includes things needed for vff2mnc @METHOD : @GLOBALS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : * $Log: vff2mnc.h,v $ * Revision 1.2 2007-12-11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 1.1 2007/01/16 18:38:18 baghdadi * header file for vff2mnc * * Adopted from vfftominc (perl script) of John G. Sled. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_SYS_TYPES_H #include #endif #include #include #include #include #include #include #include #if HAVE_FLOAT_H #include #endif #include #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define MAX_VFF_DIMS 3 #define MAX_BUF_LINE 80 #define MAX_BUF_TEXT 1000 #define MAX_DESCRIPTION 2 typedef char string_t[511+1]; #define STRING_T_LEN (sizeof(string_t) - 1) #ifndef S_ISDIR #define S_ISDIR(x) (((x) & _S_IFMT) == _S_IFDIR) #endif #define CHKMEM(x) \ if ((x) == NULL) \ (fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__), \ exit(-1)) #define TESTRPT(msg, val) (fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val), r) struct globals { char *minc_history; /* Global for minc history */ char *pname; /* program name */ int little_endian; char * dirname; int List; }; extern struct globals G; struct vff_attrs { int y_bin; int z_bin; int bands; double center_of_rotation; double central_slice; double rfan_y; double rfan_z; double angle_increment; int reverse_order; int bits; int day; int month; int year; string_t cmd_line; }; struct mnc_vars { mitype_t mnc_type; int mnc_ndims; /* MINC image dimension count */ long mnc_start[MAX_VFF_DIMS]; /* MINC data starts */ long mnc_count[MAX_VFF_DIMS]; /* MINC data counts */ char *mnc_hist; /* MINC history */ double mnc_srange[2]; /* MINC image min/max */ double mnc_starts[MAX_VFF_DIMS]; double mnc_steps[MAX_VFF_DIMS]; }; minc-tools-2.3.00+dfsg/conversion/vff2mnc/vff2mnc.c0000644000175000000620000010572412574624760021066 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : vff2mnc.c @DESCRIPTION: Program to convert vff file(s) to minc @GLOBALS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : * $Log: vff2mnc.c,v $ * Revision 1.7 2010-03-27 15:10:59 rotor * * removed default volume_io caching * * fixed a few warnings in vff2mnc (fgets) * * Revision 1.6 2008/09/23 02:18:08 alex * Fixed typo on line 141 which prevented minc to be built * * Revision 1.5 2008/09/04 16:15:01 baghdadi * corrected start value to world coordinate system * * Revision 1.4 2008/01/11 07:17:07 stever * Remove unused variables. * * Revision 1.3 2007/12/11 12:43:01 rotor * * added static to all global variables in main programs to avoid linking * problems with libraries (compress in mincconvert and libz for example) * * Revision 1.2 2007/01/19 17:52:56 baghdadi * Added minor revision for string manipulations. * * Revision 1.1 2007/01/16 18:37:57 baghdadi * To convert vff files produced by the CT scanner to MINC2.0 * * Adopted from vfftominc (perl script) of John G. Sled. ---------------------------------------------------------------------------- */ #include "vff2mnc.h" #include #if HAVE_DIRENT_H #include #endif #if defined (_MSC_VER) #include #endif #include #include /* Function Prototypes */ static void computeScalarRange(int datatype,double range[2], long count,void *buffer); static int usage(void); static void free_list(int num_files, const char **file_list); void read_2Dvff_files_header(const char **file_list, int num_files, struct mnc_vars *m2, struct vff_attrs *vattrs); void read_2Dvff_files_image(mihandle_t hvol, const char **file_list, int num_files, struct mnc_vars m2, struct vff_attrs vattrs,double range[2]); void read_3Dvff_file_header(char *filename, struct mnc_vars *m2, struct vff_attrs *vattrs); void read_3Dvff_file_image(mihandle_t hvol, char *filename, struct mnc_vars m2, struct vff_attrs vattrs, double range[2]); int add_vff_attribute_to_file(mihandle_t hvol,const struct vff_attrs *vattrs); int add_attributes_from_files(mihandle_t hvol); int find_filenames_first(string_t fullpath_pro,string_t fullpath_des,string_t fullpath_par, int *found_protocol,int *found_description, int *found_parameters); struct globals G; #define VERSION_STRING "2.0.12 built " __DATE__ " " __TIME__ static ArgvInfo argTable[] = { {NULL, ARGV_VERINFO, VERSION_STRING, NULL, NULL }, {"-noswap", ARGV_CONSTANT, (char *) FALSE, (char *) &G.little_endian, "Change to noswap default is swap"}, {"-addattrs", ARGV_STRING, (char *) 1, (char *) &G.dirname, "Add attributes from files in the given directory"}, {"-list", ARGV_CONSTANT, (char *) TRUE, (char *) &G.List, "Print list of series (don't create files)"}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { int r; const char **file_list = NULL; /* List of file names */ struct vff_attrs vffattrs; struct mnc_vars mnc2; char out_str[1024]; /* Big string for filename */ midimhandle_t hdim[MAX_VFF_DIMS]; mihandle_t hvol; double mnc_vrange[2]; /* MINC valid min/max */ int num_file_args; /* Number of files on command line */ string_t out_dir; /* Output directory */ int length; struct stat st; int ifile; int num_files; /* Total number of files */ int is_file=0; int is_list=0; int ival; char *extension; mnc2.mnc_srange[0]= -1; mnc2.mnc_srange[1]= -1; G.pname = argv[0]; /* get program name */ G.dirname = NULL; G.little_endian = 1; /*default is little endian unless otherwise*/ G.minc_history = time_stamp(argc, argv); /* Create minc history string */ if (ParseArgv(&argc, argv, argTable, 0) || argc < 2) { usage(); exit(EXIT_FAILURE); } if (G.dirname != NULL) { #if HAVE_DIRENT_H if (stat(G.dirname, &st) != 0 || !S_ISDIR(st.st_mode)) { fprintf(stderr,"Option -addattrs requires directory as argument!!!\n"); exit(EXIT_FAILURE); } #endif } if (G.List) { num_file_args = argc - 1; } else { strcpy(out_str, argv[1]); extension = strrchr(out_str, '.'); if (extension != NULL ) { extension++; if (strcmp(extension, "mnc") !=0) { usage(); exit(EXIT_FAILURE); } } if (argc == 3) { /* check if last argument is dir */ num_file_args = argc - 2; strcpy(out_dir, argv[argc - 1]); /* make sure path ends with slash */ length = strlen(out_dir); if (out_dir[length - 1] != '/') { out_dir[length++] = '/'; out_dir[length++] = '\0'; } if (stat(out_dir, &st) != 0 || !S_ISDIR(st.st_mode)) {/* assume filename */ is_file =1; } } else { //list of 2d files must check! num_file_args = argc - 2; is_list = 1; } } if (!is_file || G.List) { /* Get space for file lists */ /* Allocate the array of pointers used to implement the * list of filenames. */ file_list = malloc(1 * sizeof(char *)); CHKMEM(file_list); /* Go through the list of files, expanding directories where they * are encountered... */ num_files = 0; for (ifile = 1 ; ifile <= num_file_args; ifile++) { #if HAVE_DIRENT_H if (stat(argv[ifile + 1], &st) == 0 && S_ISDIR(st.st_mode)) { DIR *dp; struct dirent *np; char *tmp_str; length = strlen(argv[ifile + 1]); dp = opendir(argv[ifile + 1]); if (dp != NULL) { while ((np = readdir(dp)) != NULL) { /* Generate the full path to the file. */ tmp_str = malloc(length + strlen(np->d_name) + 2); strcpy(tmp_str, argv[ifile + 1]); if (tmp_str[length-1] != '/') { tmp_str[length] = '/'; tmp_str[length+1] = '\0'; } strcat(&tmp_str[length], np->d_name); if (stat(tmp_str, &st) == 0 && S_ISREG(st.st_mode)) { file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = tmp_str; } else { free(tmp_str); } } closedir(dp); } else { fprintf(stderr, "Error opening directory '%s'\n", argv[ifile + 1]); } } else { file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = strdup(argv[ifile + 1]); } #else file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = strdup(argv[ifile + 1]); #endif } } if (G.List) { exit(EXIT_SUCCESS); } if (is_file) { read_3Dvff_file_header(argv[2],&mnc2,&vffattrs); } else { read_2Dvff_files_header(file_list,num_files,&mnc2,&vffattrs); } /* ok starting to create minc2.0 file assuming 3D must take care of 2D*/ r = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, mnc2.mnc_count[2], &hdim[0]); if (r != 0) { TESTRPT("failed create_dimension zspace", r); return (1); } r = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, mnc2.mnc_count[1], &hdim[1]); if (r != 0) { TESTRPT("failed create_dimension yspace", r); return (1); } r = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, mnc2.mnc_count[0], &hdim[2]); if (r != 0) { TESTRPT("failed create_dimension xspace", r); return (1); } r = miset_dimension_start(hdim[0], mnc2.mnc_starts[2]); if (r < 0) { TESTRPT("failed dimension start xspace", r); return (1); } r = miset_dimension_start(hdim[1], mnc2.mnc_starts[1]); if (r < 0) { TESTRPT("failed dimension start yspace", r); return (1); } /* create negative start for xspace to correct orientation */ r = miset_dimension_start(hdim[2], mnc2.mnc_starts[0] * -1); if (r < 0) { TESTRPT("failed dimension start zspace", r); return (1); } /* create negative spacing for zspace to correct orientation */ r = miset_dimension_separation(hdim[0], mnc2.mnc_steps[2] * -1); if (r < 0) { TESTRPT("failed dimension separation xspace", r); return (1); } /* create negative spacing for yspace to correct orientation */ r = miset_dimension_separation(hdim[1], mnc2.mnc_steps[1] * -1); if (r < 0) { TESTRPT("failed dimension separation yspace", r); return (1); } r = miset_dimension_separation(hdim[2], mnc2.mnc_steps[0]); if (r < 0) { TESTRPT("failed dimension separation zspace", r); return (1); } r = micreate_volume(out_str,MAX_VFF_DIMS, hdim, mnc2.mnc_type, MI_CLASS_REAL, NULL, &hvol); if (r != 0) { TESTRPT("error creating volume", r); return (1); } r = micreate_volume_image(hvol); if (r != 0) { TESTRPT("error creating volume", r); return (1); } // read image slice by slice if (is_file) { read_3Dvff_file_image(hvol, argv[2], mnc2, vffattrs, mnc_vrange); } else { read_2Dvff_files_image(hvol,file_list,num_files, mnc2, vffattrs, mnc_vrange); } miset_volume_valid_range(hvol,mnc_vrange[1], mnc_vrange[0]); if (mnc2.mnc_srange[0] == -1 || mnc2.mnc_srange[1] == -1) { /* min and max are not specified in the file voxel range is set same as real range */ mnc2.mnc_srange[0] = mnc_vrange[0]; mnc2.mnc_srange[1] = mnc_vrange[1]; } miset_volume_range(hvol,mnc2.mnc_srange[1], mnc2.mnc_srange[0]); if (is_file) { /* create minc history 3D */ strcat(vffattrs.cmd_line,G.minc_history); G.minc_history = vffattrs.cmd_line; /* attributes from vff file itself 3D case*/ add_vff_attribute_to_file(hvol,&vffattrs); } if (G.dirname != NULL) { /* attributes from external files 3D case*/ add_attributes_from_files(hvol); } else if (!is_file) { /* just afew attributes from 2D case */ ival = vffattrs.year; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_year,1 , &ival); if (r < 0) { TESTRPT("failed to add date:year attribute", r); } ival = vffattrs.month; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_month,1 , &ival); if (r < 0) { TESTRPT("failed to add date:month attribute", r); } ival = vffattrs.day; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_day,1 , &ival); if (r < 0) { TESTRPT("failed to add date:day attribute", r); } } /* add history attribute */ r = miadd_history_attr(hvol,strlen(G.minc_history), G.minc_history); if (r < 0) { TESTRPT("error creating history", r); return (1); } if (file_list != NULL) { free_list(num_files, file_list); free(file_list); } miclose_volume(hvol); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : computeScalarRange @INPUT : datatype range (max, min) count (dim1 * dim2) buffer (actual data) @OUTPUT : voxel range max and min @RETURNS : (nothing) @DESCRIPTION: Function to scan data for voxel min and max @METHOD : @GLOBALS : @CALLS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : Adopted from Bert's code for DICOM files. ---------------------------------------------------------------------------- */ static void computeScalarRange(int datatype,double range[2],long count,void *buffer) { int i; range[0] = DBL_MAX; range[1] = -DBL_MAX; for(i=0; i < count; i++) { double tmp; switch (datatype) { case MI_TYPE_UBYTE: tmp = (double) ((unsigned char *)buffer)[i]; break; case MI_TYPE_BYTE: tmp = (double) ((char *)buffer)[i]; break; case MI_TYPE_SHORT: tmp = (double) ((short *)buffer)[i]; break; case MI_TYPE_USHORT: tmp = (double) ((unsigned short *)buffer)[i]; break; case MI_TYPE_FLOAT: tmp = (double) ((float *)buffer)[i]; break; default: printf("Data type %d not handled\n", datatype); break; } if (tmp < range[0]) { range[0] = tmp; } if (tmp > range[1]) { range[1] = tmp; } } } static int usage(void) { static const char msg[] = { "vff2mnc: Convert VFF file(s) to MINC2.0 format\n" "usage: vff2mnc [options] [ ...]\n" }; fprintf(stderr,"%s", msg); return (-1); } static void free_list(int num_files, const char **file_list) { int i; for (i = 0; i < num_files; i++) { if (file_list[i] != NULL) { free((void *) file_list[i]); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_2Dvff_files_header @INPUT : file_list (list of files with directory) num_files number of filenames m2 minc2 variables (struct) vattrs vff attributes to be added to minc2.0 @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: Function to read 2D vff files header information @METHOD : @GLOBALS : @CALLS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ void read_2Dvff_files_header(const char **file_list, int num_files, struct mnc_vars *m2, struct vff_attrs *vattrs) { FILE *fp ; int i; char linebuf[1024]; char temp[10]; char *pch; long rawsize; double element_size = 1; m2->mnc_steps[0] = 1; m2->mnc_steps[1] = 1; m2->mnc_steps[2] = 1; m2->mnc_starts[0] = 0; m2->mnc_starts[1] = 0; m2->mnc_starts[2] = 0; // set z dimension m2->mnc_count[2] = num_files; /* check files */ for (i=0; i < num_files; i++) { fp = fopen(file_list[i] , "rb" ) ; if( fp == NULL ) { exit(EXIT_FAILURE); /* bad open? */ } /* ensure file is VFF */ if(fgets(linebuf, sizeof(linebuf), fp) == NULL){ fprintf(stderr, "Error with fgets on line %d of %s\n", __LINE__, __FILE__); } if (strncmp(linebuf, "ncaa", 4) != 0) { fclose(fp); printf("File is not in vff format!!!\n"); exit(EXIT_FAILURE); } /* create a dictionary */ while (linebuf[0] != '\f') { if(fgets(linebuf, sizeof(linebuf), fp) == NULL){ fprintf(stderr, "Error with fgets on line %d of %s\n", __LINE__, __FILE__); } pch=strchr(linebuf, '='); if (pch != NULL && pch[1] !=';') { strncpy(temp, linebuf, pch-linebuf); temp[pch-linebuf]='\0'; if (strcmp(temp, "rank") == 0) { if (atoi(pch+1) != 2) { printf("Looking for 2D file got %d\n",atoi(pch+1)); exit(EXIT_FAILURE); } m2->mnc_ndims = atoi(pch+1); } else if (strcmp(temp, "size") == 0) { m2->mnc_count[0] = atoi(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); m2->mnc_count[1] = atoi(pch+1); } else if (strcmp(temp, "rawsize") == 0) { rawsize = atoi(pch+1); } else if (strcmp(temp, "spacing") == 0) { m2->mnc_steps[0] = atof(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); m2->mnc_steps[1] = atoi(pch+1); } else if (strcmp(temp, "origin") == 0) { m2->mnc_starts[0] = atof(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); m2->mnc_starts[1] = atof(pch+1); } else if (strcmp(temp, "bands") == 0) { vattrs->bands = atoi(pch+1); } else if (strcmp(temp, "bits") == 0) { switch(atoi(pch+1)) { case 8: m2->mnc_type = MI_TYPE_UBYTE; vattrs->bits = 8; break; case 16: m2->mnc_type = MI_TYPE_SHORT; vattrs->bits = 16; break; default: printf("Could not determine data type!!!\n"); exit(EXIT_FAILURE); } } else if (strcmp(temp, "date") == 0) { vattrs->day = atoi(pch+7); pch[7]='\0'; vattrs->month = atoi(pch+5); pch[5]='\0'; vattrs->year = atoi(pch+1); } else if (strcmp(temp, "min") == 0) { m2->mnc_srange[0] = atof(pch+1); } else if (strcmp(temp, "max") == 0) { m2->mnc_srange[1] = atof(pch+1); } else if (strcmp(temp, "elementsize") == 0) { element_size = atof(pch+1); for(i=0; imnc_steps[i] *= element_size; m2->mnc_starts[i] *= element_size; } } } } fclose(fp); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_2Dvff_files_image @INPUT : hvol minc volume handle file_list (list of files with directory) num_files number of filenames m2 minc2 variables (struct) vattrs vff attributes to be added to minc2.0 range (data min and max range) @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: Function to read the 2D vff files image @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 2007 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ void read_2Dvff_files_image(mihandle_t hvol, const char **file_list, int num_files, struct mnc_vars m2, struct vff_attrs vattrs, double range[2]) { FILE *fp ; int i,counts,r; void *buffer; int number_of_bits = vattrs.bits/8; unsigned long start[MAX_VFF_DIMS]; /* MINC data starts */ unsigned long count[MAX_VFF_DIMS]; double valid_range[2]; range[0] = DBL_MAX; range[1] = -DBL_MAX; start[1] = 0; start[2] = 0; count[1] = m2.mnc_count[1]; count[2] = m2.mnc_count[0]; counts = m2.mnc_count[0]*m2.mnc_count[1]; // allocate enough memory for one image (i.e, one slice) buffer = malloc(counts * number_of_bits); CHKMEM(buffer); for (i=0; i < num_files; i++) { // set start to the current slice start[0] = i; count[0] = 1; // open file fp = fopen(file_list[i] , "rb" ) ; if( fp == NULL ) { exit(EXIT_FAILURE); /* bad open? */ } // Set file position indicator to beginning of data r = fseek(fp,-counts* number_of_bits,SEEK_END); if ( r != 0) { printf(" fseek is reporting a problem!!\n"); exit(EXIT_FAILURE); } r = fread(buffer,sizeof(char),counts * number_of_bits,fp); if ( r == 0) { printf(" fread is reporting a problem.\n"); exit(EXIT_FAILURE); } if (G.little_endian && vattrs.bits==16) { /* default switch byte order of 16bit data */ swab(buffer, buffer, counts * number_of_bits); } // write the slice r = miset_voxel_value_hyperslab(hvol, m2.mnc_type, start, count, buffer); if (r != 0) { TESTRPT("can not write data with hperslab function",r); exit(EXIT_FAILURE); } //calculate min and max of slice computeScalarRange(m2.mnc_type,valid_range,count[1] * count[2],buffer); if (valid_range[0] < range[0]) { range[0] = valid_range[0]; } if (valid_range[1] > range[1]) { range[1] = valid_range[1]; } fclose(fp); } free(buffer); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_3Dvff_file_header @INPUT : filename (vff filename) m2 minc2 variables (struct) vattrs vff attributes to be added to minc2.0 @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: Function to read the 3D vff file header information. @METHOD : @GLOBALS : @CALLS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ void read_3Dvff_file_header(char *filename, struct mnc_vars *m2, struct vff_attrs *vattrs) { FILE *fp ; char linebuf[1024]; char temp[20]; double element_size; char *pch; int counter; int i; m2->mnc_steps[0]=1; m2->mnc_steps[1]=1; m2->mnc_steps[2]=1; /* open file */ fp = fopen(filename , "rb" ) ; if( fp == NULL ) { exit(EXIT_FAILURE); /* bad open? */ } /* ensure file is VFF */ if(fgets(linebuf, sizeof(linebuf), fp) == NULL){ fprintf(stderr, "Error with fgets on line %d of %s\n", __LINE__, __FILE__); } if (strncmp(linebuf, "ncaa", 4) != 0) { fclose(fp); printf("File is not in vff format!!!\n"); exit(EXIT_FAILURE); } /* create a dictionary */ while (linebuf[0] != '\f') { counter =0; if(fgets(linebuf, sizeof(linebuf), fp) == NULL){ fprintf(stderr, "Error with fgets on line %d of %s\n", __LINE__, __FILE__); } pch=strchr(linebuf, '='); if (pch != NULL && pch[1] !=';') { strncpy(temp, linebuf, pch-linebuf); temp[pch-linebuf]='\0'; if (strcmp(temp, "rank") == 0) { m2->mnc_ndims = atoi(pch+1); } else if (strcmp(temp, "size") == 0) { while(pch != NULL) { m2->mnc_count[counter] = atoi(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); counter++; } } else if (strcmp(temp, "origin") == 0) { while(pch != NULL) { m2->mnc_starts[counter] = atof(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); counter++; } } else if (strcmp(temp, "y_bin") == 0) { vattrs->y_bin = atoi(pch+1); } else if (strcmp(temp, "z_bin") == 0) { vattrs->z_bin = atoi(pch+1); } else if (strcmp(temp, "bands") == 0) { vattrs->bands = atoi(pch+1); } else if (strcmp(temp, "center_of_rotation") == 0) { vattrs->center_of_rotation = atof(pch+1); } else if (strcmp(temp, "central_slice") == 0) { vattrs->central_slice = atof(pch+1); } else if (strcmp(temp, "rfan_y") == 0) { vattrs->rfan_y = atof(pch+1); } else if (strcmp(temp, "rfan_z") == 0) { vattrs->rfan_z = atof(pch+1); } else if (strcmp(temp, "angle_increment") == 0) { vattrs->angle_increment = atof(pch+1); } else if (strcmp(temp, "bits") == 0) { switch(atoi(pch+1)) { case 8: m2->mnc_type = MI_TYPE_UBYTE; vattrs->bits = 8; break; case 16: m2->mnc_type = MI_TYPE_SHORT; vattrs->bits = 16; break; default: printf("Could not determine data type!!!\n"); exit(EXIT_FAILURE); } } else if (strcmp(temp, "date") == 0) { vattrs->day = atoi(pch+7); pch[7]='\0'; vattrs->month = atoi(pch+5); pch[5]='\0'; vattrs->year = atoi(pch+1); } else if (strcmp(temp, "reverse_order") == 0) { if (strcmp(pch+1,"no")) vattrs->reverse_order = 0; else vattrs->reverse_order = 1; } else if (strcmp(temp, "min") == 0) { m2->mnc_srange[0] = atof(pch+1); } else if (strcmp(temp, "max") == 0) { m2->mnc_srange[1] = atof(pch+1); } else if (strcmp(temp, "spacing") == 0) { while(pch != NULL) { m2->mnc_steps[counter] = atof(pch+1); pch = (char*) memchr (pch+1, ' ', strlen(linebuf)); counter++; } } else if (strcmp(temp, "elementsize") == 0) { element_size = atof(pch+1); for(i=0; imnc_steps[i] *= element_size; m2->mnc_starts[i] *= element_size; } } else if (strcmp(temp, "cmdLine") == 0) { strcpy(vattrs->cmd_line,linebuf); } } } fclose(fp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_3Dvff_file_image @INPUT : hvol minc volume handle filename (vff filename) m2 minc2 variables (struct) vattrs vff attributes to be added to minc2.0 range (data min and max range) @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: Function to read the 3D vff file image @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 2007 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ void read_3Dvff_file_image(mihandle_t hvol, char *filename, struct mnc_vars m2, struct vff_attrs vattrs, double range[2]) { FILE *fp ; int i,counts,r; void *buffer; int number_of_bits = vattrs.bits/8; unsigned long start[MAX_VFF_DIMS]; /* MINC data starts */ unsigned long count[MAX_VFF_DIMS]; double valid_range[2]; range[0] = DBL_MAX; range[1] = -DBL_MAX; start[1] = 0; start[2] = 0; count[1] = m2.mnc_count[1]; count[2] = m2.mnc_count[0]; counts = m2.mnc_count[0]*m2.mnc_count[1]; /* open file */ fp = fopen(filename , "rb" ); if( fp == NULL ) { exit(EXIT_FAILURE); /* bad open? */ } // Set file position indicator to beginning of image r = fseek(fp,-counts * m2.mnc_count[2] * number_of_bits,SEEK_END); if ( r != 0) { printf(" fseek is reporting a problem!!\n"); exit(EXIT_FAILURE); } // allocate memory for one slice only buffer = malloc( counts * number_of_bits); CHKMEM(buffer); for (i = 0; i < m2.mnc_count[2]; i++) { // set start to the current slice start[0] = i; count[0] = 1; // read data one slice at a time r = fread(buffer,sizeof(char),counts * number_of_bits,fp); if ( r == 0) { printf(" fread is reporting a problem leila.\n"); exit(EXIT_FAILURE); } if (G.little_endian && vattrs.bits==16) { swab(buffer, buffer, counts * number_of_bits); } // write the slice r = miset_voxel_value_hyperslab(hvol, m2.mnc_type, start, count, buffer); if (r != 0) { TESTRPT("can not write data with hperslab function",r); exit(EXIT_FAILURE); } //calculate min and max of slice computeScalarRange(m2.mnc_type,valid_range,counts,buffer); if (valid_range[0] < range[0]) { range[0] = valid_range[0]; } if (valid_range[1] > range[1]) { range[1] = valid_range[1]; } } free(buffer); fclose(fp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : add_vff_attributes_to_file @INPUT : hvol volume handle vattrs vff attributes to be added to minc2.0 @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: adds various attributes to minc file @METHOD : @GLOBALS : @CALLS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : ---------------------------------------------------------------------------- */ int add_vff_attribute_to_file(mihandle_t hvol,const struct vff_attrs *vattrs) { int ival; double dval; int r; ival = vattrs->y_bin; r = miset_attr_values(hvol, MI_TYPE_INT, "acquisition", "ybin",1 , &ival); if (r < 0) { TESTRPT("failed to add y_bin attribute", r); } ival = vattrs->z_bin; r = miset_attr_values(hvol, MI_TYPE_INT, "acquisition", "z_bin",1 , &ival); if (r < 0) { TESTRPT("failed to add z_bin attribute", r); } ival = vattrs->bands; r = miset_attr_values(hvol, MI_TYPE_INT, "acquisition", "bands",1 , &ival); if (r < 0) { TESTRPT("failed to add bands attribute", r); } dval = vattrs->center_of_rotation; r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "acquisition", "center_of_rotation",1 , &dval); if (r < 0) { TESTRPT("failed to add center_of_rotation attribute", r); } dval = vattrs->central_slice; r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "acquisition", "central_slice",1 , &dval); if (r < 0) { TESTRPT("failed to add central_slice attribute", r); } dval = vattrs->rfan_y; r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "acquisition", "rfan_y",1 , &dval); if (r < 0) { TESTRPT("failed to add rfan_y attribute", r); } dval = vattrs->rfan_z; r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "acquisition", "rfan_z",1 , &dval); if (r < 0) { TESTRPT("failed to add rfan_z attribute", r); } dval = vattrs->angle_increment; r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "acquisition", "angle_increment",1 , &dval); if (r < 0) { TESTRPT("failed to add angle_increment attribute", r); } ival = vattrs->reverse_order; r = miset_attr_values(hvol, MI_TYPE_INT, "acquisition", "reverse_order",1 , &ival); if (r < 0) { TESTRPT("failed to add reverse_order attribute", r); } ival = vattrs->year; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_year,1 , &ival); if (r < 0) { TESTRPT("failed to add date:year attribute", r); } ival = vattrs->month; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_month,1 , &ival); if (r < 0) { TESTRPT("failed to add date:month attribute", r); } ival = vattrs->day; r = miset_attr_values(hvol, MI_TYPE_INT, "study", MIstart_day,1 , &ival); if (r < 0) { TESTRPT("failed to add date:day attribute", r); } return(0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : add_attributes_from_files @INPUT : hvol volume handle @OUTPUT : (nothing) @RETURNS : @DESCRIPTION: adds a few files generated with vff file to minc file @METHOD : This method assumes the txt files are in the top directory @GLOBALS : @CALLS : @CREATED : Jul 2006 (Leila Baghdadi) @MODIFIED : This code is a bit ugly thanks for the wodnerful work of windows. ---------------------------------------------------------------------------- */ int add_attributes_from_files(mihandle_t hvol) { FILE *inf; char *strbuf; char *buffer; string_t fullpath_pro; string_t fullpath_des; string_t fullpath_par; char *p; int r,i; char **str; int found_protocol=0; int found_description=0; int found_parameters=0; /* first find files names --> different platforms */ find_filenames_first(fullpath_pro,fullpath_des,fullpath_par, &found_protocol,&found_description, &found_parameters); if (!found_protocol) { printf("could not find file with extension protocol\n"); } else { /* add *.protocol to "acquisition" protocol attribute */ inf = fopen(fullpath_pro,"r"); if (inf == NULL) { printf("Could not open file %s \n", fullpath_pro); exit(EXIT_FAILURE); } else { strbuf = malloc(MAX_BUF_LINE + 1); CHKMEM(strbuf); strbuf[0]='\0'; buffer = malloc(MAX_BUF_TEXT + 1); CHKMEM(buffer); buffer[0]='\0'; while (fgets(strbuf, MAX_BUF_LINE, inf) != NULL) { strcat(buffer,strbuf); } } fclose(inf); r = miset_attr_values(hvol, MI_TYPE_STRING, "acquisition", "protocol", strlen(buffer) ,buffer); free(buffer); free(strbuf); if (r < 0) { TESTRPT("failed to add protocol attribute", r); } } if (!found_description) { printf("Could not find file Description.txt\n"); } else { /* add Description.txt to patient name and study id */ inf = fopen(fullpath_des,"r"); if (inf == NULL) { printf("Could not open file %s \n", fullpath_des); exit(EXIT_FAILURE); } else { /* just need the first two lines of this file */ str = malloc(MAX_DESCRIPTION); CHKMEM(str); for (i=0; i < MAX_DESCRIPTION; i++) { str[i] = malloc(MAX_BUF_LINE + 1); CHKMEM(str[i]); if(fgets(str[i], MAX_BUF_LINE, inf) == NULL){ fprintf(stderr, "Error with fgets on line %d of %s\n", __LINE__, __FILE__); } for (p = str[i]; *p != '\0'; p++) { if (*p == '\r') { *p = '\0'; } } } r = miset_attr_values(hvol, MI_TYPE_STRING, "patient", "full_name",strlen(str[0]) , str[0]); if (r < 0) { TESTRPT("failed to add full_name attribute", r); } r = miset_attr_values(hvol, MI_TYPE_STRING, "study", "study_id",strlen(str[1]) , str[1]); if (r < 0) { TESTRPT("failed to add study_id attribute", r); } fclose(inf); for (i=0; i < MAX_DESCRIPTION; i++) { free(str[i]); } free(str); } } if (!found_parameters) { printf("Could not find file Parameters.txt\n"); } else { /* add Parameters.txt to "acquisition" scan_parameters */ inf = fopen(fullpath_par,"r"); if (inf == NULL) { printf("Could not open file %s \n", fullpath_par); exit(EXIT_FAILURE); } else { strbuf = malloc(MAX_BUF_LINE + 1); CHKMEM(strbuf); strbuf[0]='\0'; buffer = malloc(MAX_BUF_TEXT + 1); CHKMEM(buffer); buffer[0]='\0'; while (fgets(strbuf, MAX_BUF_LINE, inf) != NULL) { strcat(buffer,strbuf); } } fclose(inf); r = miset_attr_values(hvol, MI_TYPE_STRING, "acquisition", "scan_parameters",strlen(buffer) ,buffer); free(buffer); free(strbuf); if (r < 0) { TESTRPT("failed to add scan_parameters attribute", r); } } return(0); } int find_filenames_first(string_t fullpath_pro,string_t fullpath_des,string_t fullpath_par, int *found_protocol,int *found_description, int *found_parameters) { char *ptr; #if HAVE_DIRENT_H DIR *dp; struct dirent *np; dp = opendir(G.dirname); if (dp != NULL) { while ((np = readdir(dp)) != NULL) { if (np->d_name != "." && np->d_name != "..") { /* add *.protocol to "acquisition" protocol attribute */ ptr = strstr(np->d_name,"protocol"); if (ptr != NULL) { *found_protocol = 1; strcpy(fullpath_pro,np->d_name); } /* add Description.txt to patient name and study id */ ptr = strstr(np->d_name,"Description.txt"); if (ptr != NULL) { *found_description = 1; strcpy(fullpath_des,np->d_name); } /* add Parameters.txt to "acquisition" scan_parameters */ ptr = strstr(np->d_name,"Parameters.txt"); if (ptr != NULL) { *found_parameters=1; strcpy(fullpath_par,np->d_name); } } } } closedir(dp); #endif #if defined (_MSC_VER) WIN32_FIND_DATA FindFileData; HANDLE hFind = INVALID_HANDLE_VALUE; char DirSpec[MAX_BUF_LINE + 1]; char dirname[MAX_BUF_LINE + 1]; DWORD dwError; strncpy(DirSpec, G.dirname, MAX_BUF_LINE); DirSpec[MAX_BUF_LINE]='\0'; strncat(DirSpec, "\\*", 3); /* make sure the directory ends with '/' */ strncpy(dirname, G.dirname, MAX_BUF_LINE); dirname[MAX_BUF_LINE]='\0'; if (G.dirname[strlen(G.dirname)-1] != '/') { strcat(dirname,"/"); } hFind = FindFirstFile(DirSpec, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { printf(" Invalid file handle with Error %u \n", GetLastError()); return(1); } else { do { /* add *.protocol to "acquisition" protocol attribute */ ptr = strstr(FindFileData.cFileName,"protocol"); if (ptr != NULL) { *found_protocol = 1; strcpy(fullpath_pro, dirname); strcat(fullpath_pro,FindFileData.cFileName); } /* add Description.txt to patient name and study id */ ptr = strstr(FindFileData.cFileName,"Description.txt"); if (ptr != NULL) { *found_description = 1; strcpy(fullpath_des, dirname); strcat(fullpath_des,FindFileData.cFileName); } /* add Parameters.txt to "acquisition" scan_parameters */ ptr = strstr(FindFileData.cFileName,"Parameters.txt"); if (ptr != NULL) { *found_parameters=1; strcpy(fullpath_par, dirname); strcat(fullpath_par,FindFileData.cFileName); } } while (FindNextFile(hFind, &FindFileData) != 0); dwError = GetLastError(); FindClose(hFind); if (dwError != ERROR_NO_MORE_FILES) { printf(" Find next file %u\n", dwError); return(1); } } #endif return(0); } minc-tools-2.3.00+dfsg/conversion/vff2mnc/vff2mnc.man10000644000175000000620000000226612574624760021475 0ustar stevestaff.TH "vff2mnc" "1" "Jul 10 2006" "$Revision: 1.2 $" "" .SH "NAME" .B vff2mnc \- convert set of vff file(s) to one 3D MINC2.0 format file. .SH "SYNOPSIS" .B vff2mnc .I [] .B vff2mnc .I \-help .SH "DESCRIPTION" The .B vff2mnc command is used to convert vff format files to MINC2.0 format. "VFF" stands for "Vextractor File Format". This format is used for storing vector data between sessions. The vff file contains the image information at the very top in text format and the actual image right after the form feed character '\f' in binary. .SH "OPTIONS" .SH "Output file options" .TP .BI \-noswap Do not swap bytes when creating the MINC2.0 file. .TP .BI \-list Print list of series (don't create files). .SH "Generic options for all commands" .TP .BI \-help Print summary of command\-line options and abort .TP .BI \-version Print the program and library versions and abort .SH "AUTHORS" Leila Baghdadi (adopted from JG Sled Perl code) Please direct all complaints and inquiries to Leila Baghdadi .SH "BUGS" Probably many. But must discover them first .SH "SEE ALSO" .SH "COPYRIGHTS" Copyrights 1993\-2006 for the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/conversion/mnitominc/0002755000175000000620000000000012574624760020006 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/mnitominc/mnitominc.h0000644000175000000620000002020712574624760022153 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mnitominc.h @DESCRIPTION: Header file for mnitominc @GLOBALS : @CALLS : @CREATED : December 9, 1992 (Peter Neelin) @MODIFIED : * $Log: mnitominc.h,v $ * Revision 6.1 1999-10-29 17:52:06 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:28 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:05 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:02 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:33:18 neelin * Release of minc version 0.2 * * Revision 1.11 94/09/28 10:33:13 neelin * Pre-release * * Revision 1.10 94/09/26 08:58:12 neelin * Changed default to -noclobber. * * Revision 1.9 93/08/30 16:44:30 neelin * Added -slcstep option. * Fixed -field_of_view option. * Changed -xstep/-ystep to -colstep -rowstep. * * Revision 1.8 93/08/11 15:24:29 neelin * Added RCS logging to 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. ---------------------------------------------------------------------------- */ /* Some constants */ #define NORMAL_STATUS 0 #define ERROR_STATUS 1 #define TRUE 1 #define FALSE 0 #define MIN_VALID_CHAR ' ' #define MAX_VALID_CHAR '\177' #define PADDING_CHAR ' ' #define SEC_PER_MIN 60.0 #define EPSILON 100.0*FLT_EPSILON /* Constants for argument parsing */ #define MAX_VOL_IMGS 128 #define DEFAULT_NSLICES 15 #define IMAGE_DIMS 2 #define MAX_DIMS 4 #define DEF_ORIENT -1 #define TRANSVERSE 0 #define SAGITTAL 1 #define CORONAL 2 /* Constants for mni files */ #define BLOCK_SIZE 512 #define MNI_HEADER_SIZE BLOCK_SIZE #define MNI_FILE_TYPE 1 #define NIL_FILE_TYPE 2 #define MNI_STX_XBRAIN 67.0 #define MNI_STX_YBRAIN 86.0 #define MNI_STX_ZBRAIN 75.0 #define MNI_STX_PBRAIN 50.0 #define MNI_STX_XACLOC 0.0 #define MNI_STX_YACLOC 16.0 #define MNI_STX_NPIXEL 128 #define MNI_STX_FACTOR 100.0 #define MNI_STX_XFOV MNI_STX_NPIXEL*MNI_STX_XBRAIN/MNI_STX_PBRAIN #define MNI_STX_YFOV MNI_STX_NPIXEL*MNI_STX_YBRAIN/MNI_STX_PBRAIN #define MNI_STX_ZFOV MNI_STX_NPIXEL*MNI_STX_ZBRAIN/MNI_STX_PBRAIN #define MNI_STX_XSCALE MNI_STX_XBRAIN/MNI_STX_FACTOR #define MNI_STX_YSCALE MNI_STX_YBRAIN/MNI_STX_FACTOR #define MNI_STX_ZSCALE MNI_STX_ZBRAIN/MNI_STX_FACTOR #define MNI_FOV 256.0 #define NIL_FOV 250.0 #define MNI_DIR (1.0) #define NIL_DIR (-1.0) #define MNI_VMIN 0.0 #define MNI_VMAX 255.0 #define NIL_VMIN 0.0 #define NIL_VMAX 4095.0 #define MNI_MRI_SCAN 1 #define MNI_SCX_SCAN 2 #define MNI_STX_SCAN 3 #define MNI_POS_SCAN 4 #define MNI_TRANSVERSE 100 #define MNI_CORONAL 200 #define MNI_SAGITTAL 300 #define MNI_POSITOME_MASK 0 #define MNI_CORNER_XSIZE 12 #define MNI_CORNER_YSIZE 7 #define MNI_LOC_OFFSET 212 #define MNI_LOC_NIMAGES 230 #define MNI_LOC_MATYPE 238 #define MNI_LOC_SCANNER_ID 200 #define MNI_LOC_ORIENTATION 204 #define MNI_LOC_PATNAM 0 #define MNI_PATNAM_LENGTH 23 #define MNI_LOC_PATNUM 23 #define MNI_PATNUM_LENGTH 14 #define MNI_LOC_ACQDAT 42 #define MNI_ACQDAT_LENGTH 10 #define MNI_LOC_ACQTIM 52 #define MNI_ACQTIM_LENGTH 10 #define MNI_LOC_ISOTOPE 129 #define MNI_ISOTOPE_LENGTH 14 #define MNI_LOC_DOSE 143 #define MNI_DOSE_LENGTH 14 #define MNI_LOC_INJTIM 157 #define MNI_INJTIM_LENGTH 10 #define MNI_LOC_CORR_MASK 196 #define MNI_XLOC_QSC 0 #define MNI_YLOC_QSC 0 #define MNI_XLOC_ISEA 4 #define MNI_YLOC_ISEA 2 #define MNI_XLOC_ZPOS 8 #define MNI_YLOC_ZPOS 0 #define MNI_XLOC_TIME 0 #define MNI_YLOC_TIME 2 #define MNI_XLOC_TLEN 4 #define MNI_YLOC_TLEN 3 /* Structure containing information about orientation */ char *orientation_names[][IMAGE_DIMS+1] = { {MIzspace, MIyspace, MIxspace}, {MIxspace, MIzspace, MIyspace}, {MIyspace, MIzspace, MIxspace}, }; /* Structure containing information about signs */ char *sign_names[][6] = { {MI_SIGNED, MI_UNSIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED}, {MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED, MI_SIGNED}, {MI_SIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED, MI_UNSIGNED} }; /* Argument variables */ char *pname; char *infilename; char *outfilename; int clobber = FALSE; int verbose = TRUE; char *dimname[MAX_VAR_DIMS] = {MItime, NULL, NULL, NULL}; long dimlength[MAX_VAR_DIMS] = {0}; int dimfixed[MAX_VAR_DIMS] = {FALSE, FALSE, TRUE, TRUE}; long nframes; long nslices; int ndims; int orientation = DEF_ORIENT; double field_of_view = 0.0; double xstep = 0.0; double ystep = 0.0; double zstep = 0.0; /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *) NULL, (char *) NULL, "Options to specify image orientation. Default = -transverse."}, {"-transverse", ARGV_CONSTANT, (char *) TRANSVERSE, (char *) &orientation, "Transverse images"}, {"-sagittal", ARGV_CONSTANT, (char *) SAGITTAL, (char *) &orientation, "Sagittal images"}, {"-coronal", ARGV_CONSTANT, (char *) CORONAL, (char *) &orientation, "Coronal images"}, {NULL, ARGV_HELP, NULL, NULL, "Options to help specify number of slices and time frames."}, {"-nslices", ARGV_INT, (char *) 1, (char *) &dimlength[1], "Number of slices in volume."}, {"-nframes", ARGV_INT, (char *) 1, (char *) &dimlength[0], "Number of time frames."}, {NULL, ARGV_HELP, NULL, NULL, "Options for writing output file. Default = -noclobber."}, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file"}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file"}, {NULL, ARGV_HELP, NULL, NULL, "Options for logging progress. Default = -verbose."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "Write messages indicating progress"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not write log messages"}, {NULL, ARGV_HELP, NULL, NULL, "Other options."}, {"-field_of_view", ARGV_FLOAT, (char *) 1, (char *) &field_of_view, "Field of view of images (in mm)."}, {"-colstep", ARGV_FLOAT, (char *) 1, (char *) &xstep, "Distance between pixels along columns (in mm)."}, {"-rowstep", ARGV_FLOAT, (char *) 1, (char *) &ystep, "Distance between pixels along rows (in mm)."}, {"-slcstep", ARGV_FLOAT, (char *) 1, (char *) &zstep, "Distance between slices (in mm)."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Mni format file information */ typedef struct { FILE *fp; int file_type; short offset; short nimages; int npixels; int pix_size; double valid_range[2]; short scanner_id; double xstep; double ystep; double zstep_scale; double xstart; double ystart; short orientation; char patient_name[MNI_PATNAM_LENGTH + 1]; char patient_num[MNI_PATNUM_LENGTH + 1]; char start_time[MNI_ACQDAT_LENGTH + MNI_ACQTIM_LENGTH + 2]; char isotope[MNI_ISOTOPE_LENGTH + 1]; char dose_string[MNI_DOSE_LENGTH + 1]; double dose; char dose_units[10]; char injection_time[MNI_INJTIM_LENGTH + 1]; } mni_header_type; typedef struct { unsigned char *image; long npixels; long image_pix; int pix_size; long image_size; double minimum; double maximum; double zposition; double time; double time_length; } mni_image_type; /* Function declarations */ void parse_args(int argc, char *argv[], mni_header_type *mni_header); void usage_error(char *progname); int get_mni_header(char *file, mni_header_type *mni_header); int get_mni_image(mni_header_type *mni_header, mni_image_type *mni_image, int image_num); int dblcmp(double dbl1, double dbl2, double epsilon); minc-tools-2.3.00+dfsg/conversion/mnitominc/mnitominc.c0000644000175000000620000007464712574624760022167 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : mnitominc @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts an mni format file to a minc format file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 7, 1992 (Peter Neelin) @MODIFIED : * $Log: mnitominc.c,v $ * Revision 6.3 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.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.1 1999/10/29 17:52:06 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:28 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:05 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:02 neelin * Release of minc version 0.3 * * Revision 2.2 1995/01/23 09:06:40 neelin * changed ncclose to miclose * * Revision 2.1 95/01/23 09:01:40 neelin * Changed nccreate to micreate. * * Revision 2.0 94/09/28 10:33:17 neelin * Release of minc version 0.2 * * Revision 1.14 94/09/28 10:33:11 neelin * Pre-release * * Revision 1.13 93/08/30 16:43:47 neelin * Added -slcstep option. * Fixed -field_of_view option. * Changed -xstep/-ystep to -colstep -rowstep. * * Revision 1.12 93/08/11 15:24:20 neelin * Added RCS logging to 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 #include #include #include #include #include #include #include "mnitominc.h" /* Main program */ main(int argc, char *argv[]) { int cdfid, imgid, maxid, minid, patid, study, acqid; int dimvar[MAX_VAR_DIMS], twidth; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; long end[MAX_VAR_DIMS]; long temp_coord[MAX_VAR_DIMS]; int dim[MAX_VAR_DIMS]; int fastdim; int iframe, islice, image_num, i; mni_header_type mni_header; mni_image_type mni_image; int zdim, tdim; double last_z, diff_z, curr_diff_z, first_z; double last_t, diff_t, curr_diff_t, first_t; int z_regular, t_regular; char *tm_stamp; /* Get time stamp */ tm_stamp = time_stamp(argc, argv); /* Parse arguments and get header info from mni file */ parse_args(argc, argv, &mni_header); /* Create the file */ cdfid=micreate(outfilename, (clobber ? NC_CLOBBER : NC_NOCLOBBER)); (void) miattputstr(cdfid, NC_GLOBAL, MIhistory, tm_stamp); /* Create the dimensions */ zdim = tdim = -1; for (i=0; i=ndims-IMAGE_DIMS) || (zstep != 0.0)) { if (dimfixed[i]) dimvar[i] = micreate_std_variable(cdfid, dimname[i], NC_LONG, 0, NULL); else dimvar[i] = micreate_std_variable(cdfid, dimname[i], NC_DOUBLE, 1, &dim[i]); /* Add MIstep attribute */ if (i==ndims-1) { (void) miattputdbl(cdfid, dimvar[i], MIstep, xstep); if ((mni_header.file_type == MNI_FILE_TYPE) && (mni_header.scanner_id == MNI_STX_SCAN) && (orientation == TRANSVERSE) && ((mni_header.npixels == 128) || (mni_header.npixels == 256))) { (void) miattputdbl(cdfid, dimvar[i], MIstart, mni_header.xstart); } } else if (i==ndims-2) { (void) miattputdbl(cdfid, dimvar[i], MIstep, ystep); if ((mni_header.file_type == MNI_FILE_TYPE) && (mni_header.scanner_id == MNI_STX_SCAN) && (orientation == TRANSVERSE) && ((mni_header.npixels == 128) || (mni_header.npixels == 256))) { (void) miattputdbl(cdfid, dimvar[i], MIstart, mni_header.ystart); } } else { (void) miattputdbl(cdfid, dimvar[i], MIstep, 0.0); (void) miattputdbl(cdfid, dimvar[i], MIstart, 0.0); } /* If MItime, then create dimwidth variable */ if (STR_EQ(dimname[i], MItime)) { tdim=i; if (dimfixed[i]) twidth = micreate_std_variable(cdfid, MItime_width, NC_LONG, 0, NULL); else twidth = micreate_std_variable(cdfid, MItime_width, NC_DOUBLE, 1, &dim[i]); (void) miattputstr(cdfid, dimvar[i], MIunits, "seconds"); (void) miattputstr(cdfid, twidth, MIunits, "seconds"); } /* If not MItime, add attribute to describe spacetype */ else { (void) miattputstr(cdfid, dimvar[i], MIunits, "mm"); if (i= 0) && (zstep != 0.0) && (islice != 0)) mni_image.zposition = first_z + islice * zstep; /* Write the z position */ if ((zdim>=0) && (iframe==0)) { (void) ncvarput1(cdfid, dimvar[zdim], mitranslate_coords(cdfid, imgid, start, dimvar[zdim], miset_coords(MAX_DIMS, 0L, temp_coord)), &mni_image.zposition); if (islice>0) { curr_diff_z = mni_image.zposition - last_z; if ((islice>1) && !dblcmp(curr_diff_z, diff_z, EPSILON)) z_regular = FALSE; diff_z = curr_diff_z; } else first_z = mni_image.zposition; last_z=mni_image.zposition; } /* Write the image max and min and time */ if (mni_header.file_type==MNI_FILE_TYPE) { (void) ncvarput1(cdfid, minid, start, &mni_image.minimum); (void) ncvarput1(cdfid, maxid, start, &mni_image.maximum); if ((tdim>=0) && (islice==0)) { (void) ncvarput1(cdfid, dimvar[tdim], mitranslate_coords(cdfid, imgid, start, dimvar[tdim], miset_coords(MAX_DIMS, 0L, temp_coord)), &mni_image.time); (void) ncvarput1(cdfid, twidth, mitranslate_coords(cdfid, imgid, start, twidth, miset_coords(MAX_DIMS, 0L, temp_coord)), &mni_image.time_length); if (iframe>0) { curr_diff_t = mni_image.time - last_t; if ((iframe>1) && !dblcmp(curr_diff_t, diff_t, EPSILON)) t_regular = FALSE; diff_t = curr_diff_t; } else first_t = mni_image.time; last_t=mni_image.time; } } /* Increment the counters */ start[fastdim] += count[fastdim]; i=fastdim; while ((i>0) && (start[i]>=end[i])) { start[i] = 0; i--; start[i] += count[i]; } } } /* Write dimension attributes for z and t */ if (zdim>=0) { if (nslices>1) diff_z = (last_z - first_z) / (double) (nslices - 1); else diff_z = 0.0; (void) miattputdbl(cdfid, dimvar[zdim], MIstep, diff_z); (void) miattputdbl(cdfid, dimvar[zdim], MIstart, first_z); if (z_regular) (void) miattputstr(cdfid, dimvar[zdim], MIspacing, MI_REGULAR); else (void) miattputstr(cdfid, dimvar[zdim], MIspacing, MI_IRREGULAR); } if (tdim>=0) { if (nframes>1) diff_t = (last_t - first_t) / (double) (nframes - 1); else diff_t = 0.0; (void) miattputdbl(cdfid, dimvar[tdim], MIstep, diff_t); (void) miattputdbl(cdfid, dimvar[tdim], MIstart, first_t); if (t_regular) (void) miattputstr(cdfid, dimvar[tdim], MIspacing, MI_REGULAR); else (void) miattputstr(cdfid, dimvar[tdim], MIspacing, MI_IRREGULAR); } /* Print end of log message */ if (verbose) { (void) fprintf(stderr, "Done\n"); (void) fflush(stderr); } /* Free the memory */ FREE(mni_image.image); /* Close the file */ (void) miattputstr(cdfid, imgid, MIcomplete, MI_TRUE); (void) miclose(cdfid); exit(NORMAL_STATUS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : parse_args @INPUT : argc - number of command line arguments argv - array of arguments @OUTPUT : @RETURNS : (nothing). @DESCRIPTION: Parses command line arguments. @METHOD : @GLOBALS : pname - program name infilename - mni file to read outfilename - minc file to write clobber - overwrite? dimname - names of dimensions dimlength - lengths of dimensions ndims - number of dimensions orientation - orientation of dimensions type - type of output data signtype - sign of output data sign - string sign of output valid_range - valid range of output data vrange_set - valid range used? @CALLS : @CREATED : December 7, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ void parse_args(int argc, char *argv[], mni_header_type *mni_header) { int i, j, npix; /* Parse the command line */ pname=argv[0]; if (ParseArgv(&argc, argv, argTable, 0)) { usage_error(pname); } /* Check number of arguments */ if (argc!=3) usage_error(pname); /* Get filenames */ infilename = argv[1]; outfilename = argv[2]; /* Get mni file header info */ if (get_mni_header(infilename, mni_header)) { (void) fprintf(stderr, "%s: Error reading mni file header.\n", pname); usage_error(pname); } /* Get dimensions */ /* Get fastest two dim sizes */ for (i=0; inpixels; /* Try to guess slowest two dim sizes if user doesn't give info */ if ((dimlength[0]<=0) && (dimlength[1]<=0)) { /* If fewer than MAX_VOL_IMGS images, assume volume */ if (mni_header->nimages<=MAX_VOL_IMGS) { dimlength[0]=1; dimlength[1]=mni_header->nimages; } /* Otherwise, divide by DEFAULT_NSLICES to get nframes */ else { dimlength[1]=DEFAULT_NSLICES; dimlength[0]=mni_header->nimages / dimlength[1]; } } /* User gave nframes */ else if ((dimlength[0]>0) && (dimlength[1]<=0)) { dimlength[1]=mni_header->nimages / dimlength[0]; } /* User gave nslices */ else if ((dimlength[0]<=0) && (dimlength[1]>0)) { dimlength[0]=mni_header->nimages / dimlength[1]; } /* User gave both */ else { if (dimlength[0]*dimlength[1]!=mni_header->nimages) { (void) fprintf(stderr, "%s: * is not equal to # of images in file.\n", pname); exit(ERROR_STATUS); } } /* Check that nframes*nslices = nimages */ if ((dimlength[0]*dimlength[1])!=mni_header->nimages) { (void) fprintf(stderr, "%s: Cannot work out number of slices and frames.\n", pname); exit(ERROR_STATUS); } else if ((dimlength[0]<1) || (dimlength[1]<1)) { (void) fprintf(stderr, "%s: Number of frames and slices must be greater than zero.\n", pname); exit(ERROR_STATUS); } /* Get orientation */ if (orientation == DEF_ORIENT) { if (mni_header->orientation==MNI_CORONAL) orientation = CORONAL; else if (mni_header->orientation==MNI_SAGITTAL) orientation = SAGITTAL; else orientation = TRANSVERSE; } for (i=1; i=MAX_DIMS-IMAGE_DIMS) || (dimlength[i]>1)) { dimlength[j] = dimlength[i]; dimname[j] = dimname[i]; dimfixed[j] = dimfixed[i]; j++; } } ndims=j; /* Set x,y step for stereotaxic files */ if ((mni_header->file_type == MNI_FILE_TYPE) && (mni_header->scanner_id == MNI_STX_SCAN)) { npix = mni_header->npixels; switch (orientation) { case TRANSVERSE: mni_header->xstep = MNI_DIR * MNI_STX_XFOV / ((double) npix); mni_header->ystep = MNI_DIR * MNI_STX_YFOV / ((double) npix); mni_header->zstep_scale = MNI_STX_ZSCALE; if (npix==128) { mni_header->xstart = -(npix/2.0) * mni_header->xstep; mni_header->ystart = -(npix/2.0) * mni_header->ystep; } else if (npix==256) { mni_header->xstart = -(npix/2.0 + 0.5) * mni_header->xstep; mni_header->ystart = -(npix/2.0 + 0.5) * mni_header->ystep; } mni_header->xstart -= MNI_STX_XACLOC; mni_header->ystart -= MNI_STX_YACLOC; break; case SAGITTAL: mni_header->xstep = MNI_DIR * MNI_STX_YFOV / ((double) npix); mni_header->ystep = MNI_DIR * MNI_STX_ZFOV / ((double) npix); mni_header->zstep_scale = MNI_STX_XSCALE; break; case CORONAL: mni_header->xstep = MNI_DIR * MNI_STX_XFOV / ((double) npix); mni_header->ystep = MNI_DIR * MNI_STX_ZFOV / ((double) npix); mni_header->zstep_scale = MNI_STX_YSCALE; break; } } /* Field of view */ if (xstep==0.0) xstep = field_of_view / (double) mni_header->npixels; if (ystep==0.0) ystep = field_of_view / (double) mni_header->npixels; if (xstep==0.0) xstep = mni_header->xstep; if (ystep==0.0) ystep = mni_header->ystep; /* Set sign of step according to file type */ if (mni_header->file_type == MNI_FILE_TYPE) { xstep *= MNI_DIR; ystep *= MNI_DIR; } else if (mni_header->file_type == NIL_FILE_TYPE) { xstep *= NIL_DIR; ystep *= NIL_DIR; } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : usage_error @INPUT : progname - program name @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Prints a usage error message and exits. @METHOD : @GLOBALS : @CALLS : @CREATED : December 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void usage_error(char *progname) { (void) fprintf(stderr, "\nUsage: %s [] \n", progname); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(ERROR_STATUS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_mni_header @INPUT : file - name of mni format file @OUTPUT : mni_header - pointer to structure containing mni header info @RETURNS : TRUE if an error occurs @DESCRIPTION: Opens an mni format file and reads the header information @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_mni_header(char *file, mni_header_type *mni_header) { char buffer[MNI_HEADER_SIZE]; int matrix_type; short stemp; int i, j; if ((mni_header->fp=fopen(file, "r"))==NULL) { (void) fprintf(stderr, "Cannot open file %s\n", file); return(TRUE); } /* Read in the header block */ if (fseek(mni_header->fp, 0, SEEK_SET) || (fread(buffer, sizeof(char), MNI_HEADER_SIZE, mni_header->fp) !=MNI_HEADER_SIZE)) { (void) fprintf(stderr, "Cannot read from file.\n"); return(TRUE); } /* Get interesting information */ /* Get offset to data */ get_vax_short(1, &buffer[MNI_LOC_OFFSET], &(mni_header->offset)); /* Get number of images */ get_vax_short(1, &buffer[MNI_LOC_NIMAGES], &(mni_header->nimages)); /* Get image size and file type info */ matrix_type=buffer[MNI_LOC_MATYPE]; switch (matrix_type) { case '3': mni_header->npixels=64; mni_header->file_type=MNI_FILE_TYPE; break; case '4': mni_header->npixels=64; mni_header->file_type=NIL_FILE_TYPE; break; case '5': mni_header->npixels=128; mni_header->file_type=MNI_FILE_TYPE; break; case '6': mni_header->npixels=128; mni_header->file_type=NIL_FILE_TYPE; break; case '7': mni_header->npixels=256; mni_header->file_type=MNI_FILE_TYPE; break; case '8': mni_header->npixels=256; mni_header->file_type=NIL_FILE_TYPE; break; default: (void) fprintf(stderr, "Unknown matrix format.\n"); return(TRUE); } /* Get pixel size and valid range info from file type. */ if (mni_header->file_type==MNI_FILE_TYPE) { mni_header->pix_size=1; mni_header->valid_range[0]=MNI_VMIN; mni_header->valid_range[1]=MNI_VMAX; } else { mni_header->pix_size=2; mni_header->valid_range[0]=NIL_VMIN; mni_header->valid_range[1]=NIL_VMAX; } /* Get image type */ get_vax_short(1, &buffer[MNI_LOC_SCANNER_ID], &(mni_header->scanner_id)); get_vax_short(1, &buffer[MNI_LOC_CORR_MASK], &stemp); if (stemp==MNI_POSITOME_MASK) mni_header->scanner_id=MNI_POS_SCAN; /* Get step sizes */ if (mni_header->file_type==MNI_FILE_TYPE) { /* We have to deal with step for stereotaxic files after knowing orientation (i.e. after parsing args) */ if (mni_header->scanner_id != MNI_STX_SCAN) { mni_header->xstep = mni_header->ystep = MNI_FOV / ((double) mni_header->npixels); } } else mni_header->xstep = mni_header->ystep = NIL_FOV / ((double) mni_header->npixels); /* Get image orientation */ get_vax_short(1, &buffer[MNI_LOC_ORIENTATION], &(mni_header->orientation)); /* Get Patient name */ for (i=0; ipatient_name[i]=buffer[MNI_LOC_PATNAM+i]; if ((mni_header->patient_name[i] < MIN_VALID_CHAR) || (mni_header->patient_name[i] > MAX_VALID_CHAR)) mni_header->patient_name[i] = PADDING_CHAR; } mni_header->patient_name[i]='\0'; /* Get Patient number */ for (i=0; ipatient_num[i]=buffer[MNI_LOC_PATNUM+i]; if ((mni_header->patient_num[i] < MIN_VALID_CHAR) || (mni_header->patient_num[i] > MAX_VALID_CHAR)) mni_header->patient_num[i] = PADDING_CHAR; } mni_header->patient_num[i]='\0'; /* Get acquisition time and date */ for (i=0, j=0; istart_time[j]=buffer[MNI_LOC_ACQDAT+i]; if ((mni_header->start_time[j] < MIN_VALID_CHAR) || (mni_header->start_time[j] > MAX_VALID_CHAR)) mni_header->start_time[j] = PADDING_CHAR; } mni_header->start_time[j++]=' '; for (i=0; istart_time[j]=buffer[MNI_LOC_ACQTIM+i]; if ((mni_header->start_time[j] < MIN_VALID_CHAR) || (mni_header->start_time[j] > MAX_VALID_CHAR)) mni_header->start_time[j] = PADDING_CHAR; } mni_header->start_time[j]='\0'; /* Get isotope name */ for (i=0; iisotope[i]=buffer[MNI_LOC_ISOTOPE+i]; if ((mni_header->isotope[i] < MIN_VALID_CHAR) || (mni_header->isotope[i] > MAX_VALID_CHAR)) mni_header->isotope[i] = PADDING_CHAR; } mni_header->isotope[i]='\0'; /* Get dose */ for (i=0; idose_string[i]=buffer[MNI_LOC_DOSE+i]; if ((mni_header->dose_string[i] < MIN_VALID_CHAR) || (mni_header->dose_string[i] > MAX_VALID_CHAR)) mni_header->dose_string[i] = PADDING_CHAR; } mni_header->dose_string[i]='\0'; mni_header->dose = atof(mni_header->dose_string); (void) strcpy(mni_header->dose_units, "mCurie"); /* Get injection time */ for (i=0; iinjection_time[i]=buffer[MNI_LOC_INJTIM+i]; if ((mni_header->injection_time[i] < MIN_VALID_CHAR) || (mni_header->injection_time[i] > MAX_VALID_CHAR)) mni_header->injection_time[i] = PADDING_CHAR; } mni_header->injection_time[i]='\0'; return(FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_mni_image @INPUT : mni_header - pointer to structure containing file header info and file pointer. image_num - number of image to read (counting from zero). @OUTPUT : mni_image - pointer to structure containing image information (space for the image must already be allocated). @RETURNS : TRUE if an error occurs. @DESCRIPTION: Read an image from an mni format file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_mni_image(mni_header_type *mni_header, mni_image_type *mni_image, int image_num) { float qsc, zpos, time; short isea, tlen; long i, j, nrow, nread, image_bytes, bytes_read; unsigned char *image; double zero, denom, *valid_range; /* Check the image number */ if (image_num >= mni_header->nimages) { (void) fprintf(stderr, "Tried to read past last image.\n"); return(TRUE); } /* Read the image */ if (fseek(mni_header->fp, mni_header->offset*BLOCK_SIZE + image_num*mni_image->image_size, SEEK_SET)) { (void) fprintf(stderr, "Cannot seek to image.\n"); return(TRUE); } nread=fread(mni_image->image, mni_header->pix_size, mni_image->image_pix, mni_header->fp); if (nread<=0) { (void) fprintf(stderr, "Cannot read image from file.\n"); return(TRUE); } /* Check for read of only part of image (happens to last image of files created by cnvmri) */ if (nreadimage_pix) { bytes_read = nread * mni_header->pix_size; image_bytes = mni_image->image_pix * mni_header->pix_size; for (i = bytes_read; i < image_bytes; i++) mni_image->image[i]=0; } image = mni_image->image; nrow=mni_image->npixels*mni_image->pix_size; /* Get interesting stuff from the image */ if (mni_header->file_type==MNI_FILE_TYPE) { get_vax_float(1, &image[MNI_XLOC_QSC+MNI_YLOC_QSC*nrow], &qsc); get_vax_short(1, &image[MNI_XLOC_ISEA+MNI_YLOC_ISEA*nrow], &isea); get_vax_float(1, &image[MNI_XLOC_ZPOS+MNI_YLOC_ZPOS*nrow], &zpos); get_vax_float(1, &image[MNI_XLOC_TIME+MNI_YLOC_TIME*nrow], &time); get_vax_short(1, &image[MNI_XLOC_TLEN+MNI_YLOC_TLEN*nrow], &tlen); mni_image->minimum = (MNI_VMIN-isea) * qsc; mni_image->maximum = (MNI_VMAX-isea) * qsc; mni_image->zposition = ((mni_header->scanner_id != MNI_STX_SCAN) ? zpos : zpos * mni_header->zstep_scale); mni_image->time = time*SEC_PER_MIN; mni_image->time_length = tlen; } else { mni_image->minimum = NIL_VMIN; mni_image->maximum = NIL_VMAX; mni_image->zposition = 0.0; mni_image->time = 0.0; mni_image->time_length = 0.0; } /* Do byte swap if needed */ if (mni_image->pix_size==2) { get_vax_short(mni_image->image_pix, image, (short *) image); } /* Zero the fruit salad */ valid_range = mni_header->valid_range; denom = mni_image->maximum - mni_image->minimum; if (denom == 0.0) { zero = valid_range[0]; } else { zero = valid_range[0] - mni_image->minimum * (valid_range[1] - valid_range[0]) / denom; } if (zero < valid_range[0]) zero = valid_range[0]; if (zero > valid_range[1]) zero = valid_range[1]; zero = ROUND(zero); for (j=0; j < MNI_CORNER_YSIZE; j++) for (i=0; i < MNI_CORNER_XSIZE; i += mni_image->pix_size) if (mni_image->pix_size == 1) { *((unsigned char *) &image[j*nrow+i]) = zero; } else if (mni_image->pix_size == 2) { *((short *) &image[j*nrow+i]) = zero; } return(FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : dblcmp @INPUT : dbl1 - first value dbl2 - second value epsilon - maximum difference @OUTPUT : (none) @RETURNS : TRUE if values are the same within epsilon, FALSE otherwise. @DESCRIPTION: Compares two double precision values. If their normalized difference is less than epsilon, then return TRUE, otherwise FALSE. Returns the following : abs((dbl1-dbl2)/(dbl1+dbl2)) < epsilon @METHOD : @GLOBALS : @CALLS : @CREATED : December 14, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int dblcmp(double dbl1, double dbl2, double epsilon) { double diff, sum; diff = dbl1 - dbl2; sum = dbl1 + dbl2; diff = (diff < 0.0) ? -diff : diff; sum = (sum < 0.0) ? -sum : sum; if (sum==0.0) sum = 1.0; return((diff/sum) <= epsilon); } minc-tools-2.3.00+dfsg/conversion/mnitominc/Makefile0000644000175000000620000000063712574624760021452 0ustar stevestaff# -------------------------------------------------------------------- # # MINC Makefile # ROOT = ../../minc include $(ROOT)/Make_machine_specific include $(ROOT)/Make_configuration # Executable names PROGS = mnitominc EXTRA_OBJS = HEADERS = $(PROGS:=.h) CDEFINES = -DDEBUG # cpp defines LDOPT = $(PROG_LDOPT) MANSECT = 1 #MANPAGES = $(PROGS).$(MANSECT) include $(ROOT)/progs/Make_progs minc-tools-2.3.00+dfsg/conversion/mif2mnc/0002755000175000000620000000000012574624760017344 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/mif2mnc/mif4.doc.gz0000644000175000000620000003523012574624760021312 0ustar stevestaff9<mif4.doc\p}_F+>!SGo~8(SY:YZ?;WBnuЦnt %ILI:$mi &L!$0S2`h´L Ґڸ{oN'# 7Z{߷2O?%VyB[<V{.=u\}Z)[$ 5gI}mS۔EWmIԧׅ ٽK݇|ɉ_ʚjMw+J+ؔhwL;p?FŽ/-?tί+.=( O*i5঻R]?D'Sy,՘7o_ok@ZO.GtL)!g?7-^JOY#_:ZƧ4>twIt_N#GהlZew|#˰͟5eL'oiM CVR0 6aCST 0je*2t,9UӚʙ6ifN`3yFtƍa^gwy$h·g.-84fMtِ`PԾ^]Xjr|hj0 i|ΝSW`?mڎͬ^p 3QF|ޚ32lzYEH͂Ų(QX7#v;Ӌ4;::Ŵс4eYmU|Ys>ÚMaiL12b=D%.cY f*yZX#& wW 7Ӱ93as s6kDsiXly +yGa<_L(,QaWJGֳCZD#o8ԵlfsG*[ņ=ȻLJ1;t⎀Kn<s҅Qp ~e# Y4 s\T%چe_FWM N!O>Vt,lZ& iP*9,)Di i5~s,ݽuCƌY4IkIG/;t^'X~: JUC֧szY T/1qn:lTSшUrcS]Ѩv*;;$奤S $X zvўʴYW ZGLW,sV]mrfBT+-.DA8 Qu2Oe6ʳdki52l+4;@(b*i5VD)Q;5wS o{J$voL+ӖNx#7i3]4r7w2A=~ky m+>5A4U* Ȳ@4CVtHDzpSc㩁h4!m8HjГ^' &Kt/S"7 r1z>:Yj}a5uKrSN ǦF'O5 ZML= uGZt:azQi~D*\#p}Yf5t KߛL T&Mܠݬ)̀GSp`S=qb\uݱ{士(ԾmS6?c籶~;^Gp_LjfDߢ%#GƓkn2$lΘrE T2# WQ޽@_tؼoc,F(_j0!#/P =NZF4`Ugf($2UqYvQxZIHuJbV}v-@.){e פB,3+2N3" Ÿ#ބi L.<FD6Λ_C}{)/6# [2^'7eAPlқ^"#%?v5$ǵIYkV 0N K~tIQg1cBؒ{՘4ݤIs> _1iLQ=Sٚ>mxNfysOw/Ohy ȷcr+cӬ4o"C 6NHvVxy HhY^bt`_<)TQMTƼz&Kit(WWB(4:vjX׎ejؠUYSR'PKvCjv4W"6쎪2/kͼ1{EGY4nx;-'.⻆- DIMaO6B(PD!᝾I'f.<1l6HcF74XUOT4Mj=]NO0\QᒄϢ`!5RCy97Y<=b-91hK-j@hcGߓbX‹s'v ,bG/Bh-н$]gZ^!TL/6ZDVe,]d*?'1Ox(Q O8NSe3fdV(>XySwWW̹Y!lhڭ%z,ZSnכ9YMI~}D4h@w;މv}J1$]9ޖaw&%id3bE//Ч/'ĴPH/NJ+t /3|~ٖUj?XCMSgQ8 AE[[)5-%b gCylaȂː "XK @yY!o|~R߶59.ڏh*9%]T\vc"il6ylb(,.=*Ĺ~]& &qb^~ bU,gfsYamj=j^+p;woimm rM~"OOծӇLxnf~ >bEG.F|e${.?I-t]ײX(z^Wk\ \ 졿H&_J>|r5 Y%?65+Ϸ(gZ=go x x!pxxx8ݠ(*6[Pf9`8 |ρ_^^^'@2pHo_νuߕsGgϏb +藲6)5F57*c~E9l@y|oo9Jk —,Tk}byˣvqnr3۶)1CkBn՚啖vпg|Ϯf 3r~J/} YX:u[]M\\[yh5\-\>|m[.^m5]@ bcmځ_[7Qaj WX'׬Tܯ(O\|uG[}柬5]\ר݁uֲ\Hn/]CV8cLi -fj*/ZOr5 (RO+Ag~[6 !}6-eU޽E]{$.x#"zM4QW,5 "Xi^ݲ4m uRRKiYdzT3#qD^[\3A/8 $<^8T5|1 iX&da+v7W\&0g\Q(^-8%jZ@Y3A2_:$#Ϸygě|ۏ_^1|pkx}xT|ẋoVlN?'>vA.PR8kd]k"mJ#k]A k^#zG2̭؆Өj]~uF׸ϑuòN̟WVE[*30:3" w;z =k~c\ G"R*?6ۘ>׹Nt9?iDK +µr]B=tpmT!E :iCdPo-0ݸ0&XW*}|w8/cO@KBkt@8SY4,D)\(e͑IdLH3Xױ+oc-2q(@!JT{ p8(oh -; }x]a|$è|ËR_J^}_盋NI\(/mk\2| pK : u"Y2zIWtck:l`?kÿ N"_k?;?k +p q%r|Z 6_Y9;W,cLy='2GDc ^8{9u2=eyM=Z4p"3l_G>x3Cۮ@UՆ{ƾP}!}Aylu fqP, PӲvQmd^u)&R֤"=dmPFu3^8#SDS3Ϳd?FOg# sX ؆9vbN @sF8' јL i<xk؀8C8n~qxɘ'9xۑ8p7oG'tFOABEXeX (į(ET{*[{t RwTYgdd]1SGۤ:|X_l_GWN6ѝwH[moMa>xd鋍o)2cd?xX:G1nqiA;3,3יu}q̆&}8<Dg\03 Y]cu1\wR}0q\ifS6u`>2K*^؉]_bws؍=^ `U̝]uG#~FJuA$5F}S&N0:Lu).!;;VQkb̖uu\ֻ[/Პ%sy?ȿ?O֭_7d #z 9/PG(z [u1eXW(#:wCd?WאJHųX%Xx{؀;>.dc/rp"pm?#MшA? P p#gg<瑁/c%Zv|؅/؇8?$ ZۜuJz*/XGv6;y㢆r]߱ s.dtO1{t_`7 {q%¡*˜\lq%x7ݟ.m% >o#'?_*{omE5`hU]SʧrB uYvg:0͘dO7qY8L2W :x'8tG1te\󿃜qp O>'B1PȿQo_Z}LCiwɱ>9~+{G2/$~9B9c8wzΏcc_]$K${s5@'܎?tEz =LST<9H\<XA.0|c8~A1Ρp.78 74<[]n =(#x ɘHӘHC:bΨ3EP}vGUeO]>ƸI%;$HqXHۡ3Fm&sR/ӥ>fT(#; igH;Oׯ]=^yO;uv8Ky(@!~EN4΄޻<qR\e\!2c;v3pc_ppFȸ6RƳxvcSj|`6# {lhp$uzc9؋S() 7|-~qF^h_܆p܎.]( ` P$ LÓTa1K2ߤԙW8G yHޒ_Kɭh]!R7AkRt?_mK;̗v󔴗Ny$}:JEHtW[)^e2E-OxeSdm?gz`2[ ؇8\ n@!q+:#q;+" w?`c2`*a:f`60xXXuzlGb{px-c!XClF|#(yݢT#1"oQs/?]|NgLa2Vr_/yoI%_m\X-1I꫗ ~Ku cwigH,4iWӤ=ͨbF_ϐq_"ag*?ϘNd|sP6JB2f#6N}0@FyTipƤk}7 q72~ mk^>-چuWv>l>xr>nJngr_vɥM<55LY<җׇ>T@U'~ZU؋˞LC}3^Qò1 >w83&3>Su}߬={O-R[+e ;NR$ubĨ0ŵS墢M\՟Mheǣo0OFIE)SwYݠUeNTTn؆+L+?U'w\(%W*6Ji\Ces@\LWʼN-eZtL\L i*e)se2}e6iC%,%YK4K*`E %oeVSX;[RL7)ʜHn6[VZo]Uo[5%H;)w0?qQZoԤXy"*ቺ4[men3d˚ȖYȿI|Կ+lgcKEN|Fd2py8B!XG'Ģ5w B7Fa^q^>|P = x(A B,$bcD Xxb5` ~Bpb1c!MR]o`/| #}1C0 QiE[OnA;t@'tA  $EB~aI"ۻ(|չHA#r(T $r#⁀dB"Ir@T ,. ..(" ^C,<Շ' w0I Y*>_z{SVb.m8ò( -B$LVc'vcG_+|8op^DSltHxPiyx˰?|Cr?m!HGrpcVc-6`3^D  >.\HD7FR1;0q7`6`#6c ^VwqD}Q! ]7fx0 (x/wjlG ^7P* xLJ<5XMt燻0C0pf`.A,x[Qm؎؉7 %xhIC!}~(F2ca1R<'ucҷFtc;SO%'bޖ>pWki ;10uF68*{8sٯG4S3?>\,2cc+-~|/[ )S#ʤ_舫uv܉"f~w`'ނuu9}#8P1nLFE<:+ 101Oax/8{&Ch  00",_bF<[ /f"-qֱӒuL1;0S0t%_ ؄E[*WrbDbq.Z "$$ct\:k"bl18*]ݡwa&`V5\Ylns׾,bn)mO|Z6L ))*t{jGpeJ)SW);Eb)٪2^2SjRNδ|ҟo/wLk>K^ٜoǔ^/?ԇf11LyLyLy~3"SSSseccL-Kg~Trʰ!6f}Ϧ<<<<<<<(SԩO/V=|j-Jhk v$@1*Dj+}='BBc6UJznZIBP^^$>R9T㔓h'Ja ґhzٵH1 T#"RXF(EhN\Hy?s"w܃7C~؊u ORjlU +LmH~x*UVnKQ}DW3dT&.O=K}tXI== H"(8JBjjRdR;`Qu!$pK\VJb~9DYkwR'q"%aȉ ģ:#:3@HT)] =נZoSÑ0+sq)'6D[0 )TA⡍@2!1}GBbsfÃQ oX6܎;>.%Tޞ.LQNuk?y1Eez0l;6-)/ӎb;q؞Tu$ `d>\n>/1 j*z6aFR:{zRƲT'OnM=? 6seL^Uv淮 *̸ <; u{+g][=r I)cQ~kl׮Ǯ捩uƴYuYߗ׏5+|hPEyy{Tܣ+ZߨWT0O #JJ@EXqJ酭 ,+R腃_xV¾C]X|>1co}Ջ_{٥ yJ>{q㺍]f.Su|ǎuێ%ʁ Y/|z/\1KY>fO> ]njvkz/z$-I%o_0{w~ѯqr˩Rd%~{JN $!5^+ߜy홡S7Ohy_FSnz)&aO;l",Tkc?}g_\^q.>w+un]dž/?K '8gjskplߢ.bhļ[>)Znt2wLbST*=͕-yސג|à(f{\i$H n0P娑u8ka顲[Z=Κ!`Aa!A!So;(壳PשL*}lqaZvP*aA?6+B_uz]}y@_vTFsU"JDwfS*ʋ*QOU"߉}DLRNWir%^Tr2^H̿=31 1c.@MG­oqauWu(~dr[2vdss¤2I,|+W6׿MLminc-tools-2.3.00+dfsg/conversion/mif2mnc/mif2mnc0000755000175000000620000002047512574624760020633 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - rotor@bic.mni.mcgill.ca # McConnell Brain Imaging Center # Montreal Neurological Institute # # MINC is a free file format from the MNI: www.bic.mni.mcgill.ca # MIF4 is a file format of a lab from UBC - www.medicine.ubc.ca # The specs for MIF4 were kindly provided by Andrew Riddehough # # NB: this is not a complete translation only fields that seemed # pertinent at the time of writing this are used... # # Mon Jan 7 14:18:37 EST 2002 - initial version # Wed Feb 13 16:22:05 EST 2002 - Added byte_swapping aka ana2mnc require 5.0; use strict; use warnings "all"; use Getopt::Tabular; $SIG{__DIE__} = sub { &cleanup; die $_[0]; }; my(%Unpack_codes) = ('a', 1, 'A', 1, 's', 2, 'S', 2, 'i', 4, 'I', 4, 'f', 4, 'd', 8, 'l', 4, 'L', 4, ); my(%dtypes) = ( 2 => ['-short', '-unsigned'], 4 => ['-int', '-unsigned'], 8 => ['-float'], 16 => ['-double'], ); chomp(my($me) = `basename $0`); my($tmpfile) = "/tmp/$me-$$.raw"; my($clobber) = 0; my($verbose) = 0; my(@opt_table) = ( ["-verbose", "boolean", 0, \$verbose, "Be Verbose"], ["-clobber", "boolean", 0, \$clobber, "clobber existing files"], ); my($Help) = <$tmpfile"); my($c, $position, $pix_size_x, $pix_size_y, $size_x, $size_y, $type, $thickness, $data); for($c=0; $c<$h{Num_Slices}; $c++){ read(HDR, my($hdr), 256); $position = destruct(\$hdr, 109,'f', $bs); $pix_size_x = destruct(\$hdr, 113,'l', $bs) / 1000; $pix_size_y = destruct(\$hdr, 113,'l', $bs) / 1000; $size_x = destruct(\$hdr, 121,'s', $bs); $size_y = destruct(\$hdr, 123,'s', $bs); $type = destruct(\$hdr, 125,'s', $bs); $thickness = destruct(\$hdr, 127,'l', $bs) / 1000; if($verbose){ print STDOUT "Slice $c pos: $position $thickness $size_x:$size_y $pix_size_x:$pix_size_y\n"; } read(HDR, $data, $size_x*$size_y*$type); syswrite(TMP, $data, $size_x*$size_y*$type); } close(HDR); close(TMP); # byte swapping time @args = ('dd', "if=$tmpfile", "of=$tmpfile.swp", 'conv=swab'); if ($verbose){ print STDOUT "@args\n"; } system(@args) == 0 or die "Died during byte swapping\n"; # Check the data type if (!defined($dtypes{$h{Bytes_per_Pix}})){ die "Unknown data type: $h{Bytes_per_Pix}\n"; } # Set up rawtominc command @args = ('rawtominc'); if($clobber){ push(@args, "-clobber"); } push(@args, @{$dtypes{$h{Bytes_per_Pix}}}); # Get step info if (($h{Orientation} eq "O") || ($h{Orientation} eq "T")){ # Transverse push(@args, '-transverse'); } elsif ($h{Orientation} eq "C"){ # Coronal push(@args, '-coronal'); } elsif ($h{Orientation} eq "S"){ # Sagittal push(@args, '-sagittal'); } else{ # Unknown warn "Unknown data orientation: assuming transverse\n"; push(@args, '-transverse'); } # range push(@args, '-scan_range', '-range', 0, 4095); # figger out starts and origins $pix_size_y *= -1; push(@args, '-xstep', $pix_size_x, '-ystep', $pix_size_y, '-zstep', $thickness+$h{Def_Slice_Gap}, '-xstart', -$pix_size_x*$h{Image_Dim_x}/2, '-ystart', -$pix_size_y*$h{Image_Dim_y}/2, '-zstart', -$thickness*$h{Num_Slices}/2, '-sattribute', ":history=$history", '-input', "$tmpfile.swp", $mncfile, "$h{Num_Slices}", "$h{Image_Dim_x}", "$h{Image_Dim_y}"); if ($verbose){ print STDOUT join(" ", @args) . "\n"; } system(@args) == 0 or die "Died during rawtominc system command\n"; # insert all the MIF stuff in the header undef(@args); push(@args, "minc_modify_header"); foreach (sort(keys(%h))){ push(@args, ("-sinsert", "MIF4:$_=$h{$_}")); } push(@args, $mncfile); if ($verbose){ print STDOUT join(" ", @args) . "\n"; } system(@args) == 0 or die "Died during minc_modify_header command\n"; &cleanup; # Unpack a value from a string (passed by reference) # A legendary bit of code nicked from dicom_to_minc sub destruct{ my($stringref, $offset, $type, $bs) = @_; my($code, $number, $size, $tempstring, $iloop, $max); # Check for byte swapping if($bs){ if($type !~ /^\s*([a-zA-Z])(\d+)?\s*$/){ die "Unrecognized data type \"$type\" on little-endian machine.\n"; } $code = $1; $number =(defined($2) ? $2 : 1); if(!defined($Unpack_codes{$code})){ die "Unrecognized unpack code \"$code\" on little-endian machine.\n"; } $size = $Unpack_codes{$code}; $tempstring = substr($$stringref, $offset, $number * $size); if($size > 1){ $max = $number * $size; for($iloop=0; $iloop < $max; $iloop+=$size){ substr($tempstring, $iloop, $size) = reverse(substr($tempstring, $iloop, $size)); } } return unpack("$type", $tempstring); } # No byte swapping required else{ return unpack("x$offset $type", $$stringref); } } # cleanup before dying sub cleanup { if ($verbose){ print STDOUT "Cleaning up ......\n"; } my @args = ('rm', '-r', '-f', "$tmpfile", "$tmpfile.swp"); system @args; } minc-tools-2.3.00+dfsg/conversion/mri_to_minc/0002755000175000000620000000000012574624760020310 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/mri_to_minc/mri_to_minc.pl0000644000175000000620000010476212574624760023154 0ustar stevestaff#! /usr/local/bin/perl # # Routines for converting mri files to minc format. Must be included # with routines for reading in data structures, specific to the input # file format. # # Global variable for indicating byte order $Little_endian = unpack("c",substr(pack("s",1),0,1)); %Unpack_codes = ('a', 1, 'A', 1, 's', 2, 'S', 2, 'i', 4, 'I', 4, 'f', 4, 'd', 8, ); sub numeric_order { $a <=> $b;} # Routine to take absolute value sub abs { local(@new, $val); foreach $val (@_) { push(@new, ($val<=>0) * $val); } return (scalar(@new) > 1) ? @new : $new[0]; } # Subroutine to clean up files and exit sub cleanup_and_die { # Get message to print and exit status local($message,$status) = @_; if (!defined($status)) {$status = 0;} if (defined($message)) { print STDERR $message; if ($message !~ /\n$/) {print STDERR "\n";} } $SIG{'INT'} = 'IGNORE'; $SIG{'TERM'} = 'IGNORE'; $SIG{'QUIT'} = 'IGNORE'; # Check for temp files if (defined($tmpdir) && -e $tmpdir) { print STDERR "Cleaning up temporary files.\n"; system "rm -rf $tmpdir"; } exit($status); } # Subroutine to execute commands and check return status sub execute { local($status); if ($print_execution_statements) { print join(' ',@_),"\n"; } $status = system @_; if ($status != 0) { &cleanup_and_die("Error executing command \"".join(" ",@_)."\".\n", $status); } } # Subroutine to remove a list of files sub remove_file { unlink @_; } # Subroutine to create a temporary directory (global variable $tmpdir # is created) sub create_tmpdir { local($subdir); if (scalar(@_) >= 1) { $subdir = $_[0]; } else { $subdir = $$; } if (defined($ENV{TMPDIR})) { $tmpdir = $ENV{TMPDIR}; } else { $tmpdir = "/usr/tmp"; } $tmpdir .= "/$subdir"; mkdir($tmpdir,0777) || die "Unable to create directory $tmpdir: $!"; $SIG{'INT'} = 'cleanup_and_die'; $SIG{'TERM'} = 'cleanup_and_die'; $SIG{'QUIT'} = 'cleanup_and_die'; } # Routine to execute external tape commands, returning the error sub catch_tape_command { local($tapedrive) = shift(@_); close(TAPEHANDLE); local($status) = system(@_); open(TAPEHANDLE, $tapedrive); return $status; } # Routine to execute external tape commands, dying on error sub execute_tape_command { local($status) = &catch_tape_command(@_); if ($status != 0) { shift(@_); &cleanup_and_die("Error executing command \"".join(" ",@_)."\".\n", $status); } } # Routine to get the current tape position sub get_tape_position { if (defined($counter_for_read_next_file)) { return $counter_for_read_next_file; } else { return 0; } } # Routine to set the current tape position sub set_tape_position { local($tapedrive, $position) = @_; if (length($tapedrive) <= 0) {return;} # Loop to position tape local($repos_sleep) = 1; local($nreposition_retries) = 4; local($tmp_status); foreach $reposloop (0..$nreposition_retries-1) { select(undef, undef, undef, $repos_sleep); $tmp_status = &catch_tape_command($tapedrive, "mt -t $tapedrive rewind"); select(undef, undef, undef, $repos_sleep); if ($position > 0) { $tmp_status = &catch_tape_command($tapedrive, "mt -t $tapedrive fsf $position") unless ($tmp_status != 0); } if ($tmp_status == 0) {last;} print STDERR "Error repositioning tape - trying again.\n"; } if ($tmp_status != 0) { warn "\n\nWARNING!!!!! Unable to reposition the tape.\n\n"; } # Set counter $counter_for_read_next_file = $position; return $tmp_status; } # Subroutine to read a file from tape sub read_next_file { local($tapedrive, *input_list) = @_; if (!defined($tapedrive) && !defined(@input_list)) { $tapedrive = "/dev/nrtape"; } # Constants $tape_block_size = 8192; # $tape_sleep = 1; $tape_sleep = 0; $retry_sleep = 1; $nretries = 4; # Get next value from list if no tape drive if (length($tapedrive) == 0) { local($filename) = shift(@input_list); if (length($filename) > 0) { print "Reading header for file $filename\n"; } return $filename; } # Create file counting variable if it does not exist and rewind tape # drive. Note that the rewind implicitly opens the TAPEHANDLE if (!defined($counter_for_read_next_file)) { print STDERR "Rewinding tape drive $tapedrive\n"; &execute_tape_command($tapedrive, "mt -t $tapedrive rewind"); $counter_for_read_next_file = 0; } # Create the filename local($cur_file_number) = $counter_for_read_next_file; $counter_for_read_next_file++; local($filename) = "$tmpdir/datafile_".$cur_file_number; # Try reading from the tape drive. We will try repeatedly if necessary print STDERR "Retrieving file $filename from drive $tapedrive\n"; local($status, $oldsep, $tapemessage); foreach $retryloop (0..$nretries-1) { # Sleep for a moment, then read from tape drive (don't ask me why, # it just works!) select(undef, undef, undef, $tape_sleep); $oldsep = $/; undef($/); close(TAPEHANDLE); $status = 1; if (open(TP, "dd if=$tapedrive of=$filename bs=1000000 2>&1 |")) { $tapemessage = ; close(TP); $status = $?; if (($status != 0) && (-z $filename) && ($tapemessage =~ /^Read error:\s*No space left on device/)) { $status = 0; } } open(TAPEHANDLE, $tapedrive); $/ = $oldsep; if ($status == 0) {last;} # If we get to here then the read failed. Try to reposition the tape. print STDERR "Error reading from tape - trying again.\n"; if (&set_tape_position($tapedrive, $cur_file_number) != 0) {last;} $counter_for_read_next_file++; # Sleep to let things settle after repositioning select(undef, undef, undef, $retry_sleep); } # We've finished trying to read. Test to see if we failed or if # we've reached the end of the tape. if (($status!=0) || (-z $filename)) { &remove_file($filename); if ($status != 0) { warn "\n\nWARNING!!!!! ". "Error occurred while reading tape. Giving up.\n\n\n"; &cleanup_and_die("Not creating last minc file.\n", $status); } else { print STDERR "End of tape.\n"; } return ""; } else { return $filename; } } # Subroutine to unpack a value from a string sub unpack_value { local(*string, $offset, $type) = @_; # Check for byte swapping if ($Little_endian) { if ($type !~ /^\s*([a-zA-Z])(\d+)?\s*$/) { die "Unrecognized data type \"$type\" on little-endian machine.\n"; } local($code) = $1; local($number) = (defined($2) ? $2 : 1); if (!defined($Unpack_codes{$code})) { die "Unrecognized unpack code \"$code\" on little-endian machine.\n"; } local($size) = $Unpack_codes{$code}; local($tempstring) = substr($string, $offset, $number * $size); if ($size > 1) { local($iloop); local($max) = $number * $size; for ($iloop=0; $iloop < $max; $iloop+=$size) { substr($tempstring, $iloop, $size) = reverse(substr($tempstring, $iloop, $size)); } } return unpack("$type", $tempstring); } # No byte swapping required else { return unpack("x$offset $type", $string); } } # Subroutine to get a direction cosine from a vector, correcting for # magnitude and direction if needed (the direction cosine should point # along the positive direction of the nearest axis) sub get_dircos { if (scalar(@_) != 3) { die "Argument error in get_dircos\n"; } local($xcos, $ycos, $zcos) = @_; # Get magnitude local($mag) = sqrt($xcos**2 + $ycos**2 + $zcos**2); if ($mag <= 0) {$mag = 1}; # Make sure that direction cosine is pointing along positive axis local($max) = $xcos; if (&abs($ycos) > &abs($max)) {$max= $ycos;} if (&abs($zcos) > &abs($max)) {$max= $zcos;} if ($max < 0) {$mag *= -1;} # Correct components $xcos /= $mag; $ycos /= $mag; $zcos /= $mag; return ($xcos, $ycos, $zcos); } # Subroutine to add an optional attribute to an array sub add_optional_attribute { if (scalar(@_) != 4) { die "Argument error in add_optional_attribute\n"; } local(*array, $type, $name, $value) = @_; local($key); if ($type eq "string") {$key = "-sattribute";} elsif ($type eq "double") {$key = "-dattribute";} elsif ($type eq "none") {$key = "-attribute";} else {die "Unknown type \"$type\" in add_optional_attribute\n";} if (length($value) > 0) { push(@array, $key, $name."=".$value); } } # Subroutine to create a minc file sub create_mincfile { if (scalar(@_) != 5) { die "Argument error in create_mincfile"; } local($mincfile, *file_list, *mincinfo, $image_list, $echo) = @_; # Sort images by z position local($cur_image, %pos_to_image, @positions, $cur_slicepos); foreach $cur_image (split($;, $image_list)) { $cur_slicepos = $mincinfo{$cur_image, 'slicepos'}; if (!defined($pos_to_image{$cur_slicepos})) { $pos_to_image{$cur_slicepos} = $cur_image; push(@positions, $cur_slicepos); } else { warn "Duplicate slice position: " . "ignoring file $file_list{$cur_image}\n"; } } @positions = sort(numeric_order @positions); # Get step size and get nslices and ordered list of images local(@image_list, $slicestep, $nslices, $slicestart, $slicerange); if (scalar(@positions) > 1) { # Find smallest step size $slicestep = $positions[1] - $positions[0]; local(@difflist, $diff); foreach $i (2..$#positions) { $diff = $positions[$i] - $positions[$i-1]; push(@difflist, $diff); if (($diff < $slicestep) && ($diff > 0)) { $slicestep = $diff; } } # Find average step, accounting for stepping over multiple slices # (based on rounded ratio to smallest step) local($ndiffs) = 0; local($totaldiff) = 0; foreach $diff (@difflist) { $totaldiff += $diff; $ndiffs += int($diff/$slicestep + 0.5); } if ($ndiffs > 0) { $slicestep = $totaldiff / $ndiffs; } # Work out number of slices and get the ordered list of images if ($slicestep <= 0) { $nslices = scalar(@positions); $slicestep = 1; @image_list = @pos_to_image{@positions}; } else { local($first_image, $last_image); $first_image = $pos_to_image{$positions[0]}; $last_image = $pos_to_image{$positions[$#positions]}; $slicerange = $mincinfo{$last_image, 'slicepos'} - $mincinfo{$first_image, 'slicepos'}; $nslices = int($slicerange / $slicestep + 1.5); # Improve accuracy of $slicestep $slicestep = $slicerange / ($nslices - 1); foreach $i (0..$nslices) { $image_list[$i] = ''; } foreach $position (@positions) { local($slice) = int(($position - $positions[0]) / $slicestep + 0.5); @image_list[$slice] = $pos_to_image{$position}; } } } else { $slicestep = 1; $nslices = 1; @image_list = @pos_to_image{@positions}; } $slicestart = $positions[0]; # Check for existence of file - create a new name if it exists local($filebase); if ($mincfile =~ /^(.*)\.mnc$/) { $filebase = $1; } else { $filebase = $mincfile; } local($version) = 1; local(@glob); while (scalar(@glob=<$mincfile*>) > 0) { $version++; $mincfile = $filebase."_version$version.mnc"; print STDERR "Minc file already exists. Trying name \"$mincfile\".\n"; } # Set up rawtominc command # Dimension info local($nrows, $ncols, $orientation, $pixel_size); local($xstep, $ystep, $zstep); local($colstart, $rowstart); $pixel_size = $mincinfo{'pixel_size'}; $nrows = $mincinfo{'height'}; $ncols = $mincinfo{'width'}; $xstep = $mincinfo{'colstep'}; $ystep = $mincinfo{'rowstep'}; $zstep = $slicestep; $colstart = $mincinfo{'colstart'}+0; $rowstart = $mincinfo{'rowstart'}+0; # Orientation local($xstart, $ystart, $zstart); local(%world_axes); local($orient_flag) = $mincinfo{'orientation'}; if ($orient_flag eq 'sagittal') { # Sagittal ($ystep, $zstep, $xstep) = ($mincinfo{'colstep'}, $mincinfo{'rowstep'}, $slicestep); ($ystart, $zstart, $xstart) = ($colstart, $rowstart, $slicestart); $orientation = "-sagittal"; %world_axes = ('col', 'y', 'row', 'z', 'slc', 'x'); } elsif ($orient_flag eq 'coronal') { # Coronal ($xstep, $zstep, $ystep) = ($mincinfo{'colstep'}, $mincinfo{'rowstep'}, $slicestep); ($xstart, $zstart, $ystart) = ($colstart, $rowstart, $slicestart); $orientation = "-coronal"; %world_axes = ('col', 'x', 'row', 'z', 'slc', 'y'); } else { # Transverse ($xstep, $ystep, $zstep) = ($mincinfo{'colstep'}, $mincinfo{'rowstep'}, $slicestep); ($xstart, $ystart, $zstart) = ($colstart, $rowstart, $slicestart); $orientation = "-transverse"; %world_axes = ('col', 'x', 'row', 'y', 'slc', 'z'); } local(@dircos_options) = (); foreach $axis ('col', 'row', 'slc') { if (defined($mincinfo{"${axis}_dircos_x"})) { push(@dircos_options,"-${world_axes{$axis}}dircos"); foreach $component ('x', 'y', 'z') { push(@dircos_options, $mincinfo{"${axis}_dircos_$component"}); } } } # Optional attributes local(@optional_attributes) = (); &add_optional_attribute(*optional_attributes, "double", "acquisition:repetition_time", $mincinfo{'tr'}); &add_optional_attribute(*optional_attributes, "double", "acquisition:echo_time", $mincinfo{'te',$echo}); &add_optional_attribute(*optional_attributes, "double", "acquisition:inversion_time", $mincinfo{'ti'}); &add_optional_attribute(*optional_attributes, "double", "acquisition:flip_angle", $mincinfo{'mr_flip'}); &add_optional_attribute(*optional_attributes, "string", "acquisition:scanning_sequence", $mincinfo{'scan_seq'}); &add_optional_attribute(*optional_attributes, "string", "study:study_id", $mincinfo{'exam'}); &add_optional_attribute(*optional_attributes, "string", "study:acquisition_id", $mincinfo{'series'}); &add_optional_attribute(*optional_attributes, "string", "study:start_time", $mincinfo{'start_time'}); &add_optional_attribute(*optional_attributes, "string", "study:institution", $mincinfo{'institution'}); &add_optional_attribute(*optional_attributes, "string", "patient:birthdate", $mincinfo{'patient_birthdate'}); &add_optional_attribute(*optional_attributes, "double", "patient:age", $mincinfo{'patient_age'}); &add_optional_attribute(*optional_attributes, "string", "patient:sex", $mincinfo{'patient_sex'}); &add_optional_attribute(*optional_attributes, "string", "patient:identification", $mincinfo{'patient_id'}); # Check for history if (length($history) > 0) { &add_optional_attribute(*optional_attributes, "string", ":history", $mincinfo{'history'}); } # GE specific stuff local(@ge_specific_attributes); @ge_specific_attributes = (); &add_optional_attribute(*ge_specific_attributes, "string", "ge_mrimage:pseq", $mincinfo{'ge_pseq'}); &add_optional_attribute(*ge_specific_attributes, "string", "ge_mrimage:pseqmode", $mincinfo{'ge_pseqmode'}); # Dicom specific stuff local(@dicom_specific_attributes); @dicom_specific_attributes = (); local(@elements) = sort(grep(/^dicom_/, keys(%mincinfo))); local($element); foreach $element (@elements) { &add_optional_attribute(*dicom_specific_attributes, "string", $element, $mincinfo{$element}); } # Run rawtominc with appropriate arguments $| = 1; local(@typeinfo); if ($mincinfo{'pixel_size'} == 1) { @typeinfo = ("-byte", "-unsigned", "-range", "0", "255"); } else { @typeinfo = ("-short", "-signed", "-range", "0", "4095"); } local(@minccommand) = ("rawtominc", $mincfile, $nslices, $nrows, $ncols, "-noclobber", "-scan_range", @typeinfo, $orientation, "-xstep", $xstep, "-ystep", $ystep, "-zstep", $zstep, "-xstart", $xstart, "-ystart", $ystart, "-zstart", $zstart, @dircos_options, "-mri", "-attribute", "patient:full_name=".$mincinfo{'patient_name'}, @optional_attributes, @ge_specific_attributes, @dicom_specific_attributes ); open(MINC, "|-") || exec(@minccommand); # Get keys for machine specific file info local(@specific_keys, %specific_keys, $key); undef(%specific_keys); foreach $key (keys(%mincinfo)) { $key =~ /$;specific$;([^$;]*)$/; $specific_keys{$1} = ''; } @specific_keys = keys(%specific_keys); # Loop through slices local($cur_file, %specific_file_info, $cur_image); foreach $slice (0..$nslices-1) { # Get image number $cur_image = $image_list[$slice]; # Get file name $cur_file = $file_list{$cur_image}; # Print log message if (length($cur_file) > 0) { print STDERR "Getting data from file \"$cur_file\" for slice $slice.\n"; } else { print STDERR "Using blank image for slice $slice.\n"; } # Set up variables for getting the image $image_data_len = $nrows * $ncols * $pixel_size; # Read the image (if not defined, create a blank image) if (length($cur_file) <= 0) { $image_data = pack("x$image_data_len",()); } else { # Get machine specific file info undef(%specific_file_info); foreach $key (@specific_keys) { $specific_file_info{$key} = $mincinfo{$cur_image, 'specific', $key}; } local($image_cmd) = &get_image_cmd($cur_file, *specific_file_info); if ($Little_endian) { $image_cmd .= " | byte_swap"; } open(GEDAT, $image_cmd . " |"); read(GEDAT, $image_data, $image_data_len); local($dummy, $nread); do { $nread = read(GEDAT, $dummy, 8192); } while ($nread == 8192); close(GEDAT); if ($? != 0) { warn("Error or signal while reading image.\n"); if ($ignore_image_errors) { warn "Using blank image instead.\n"; $image_data = pack("x$image_data_len",()); } else { &cleanup_and_die("Quitting.\n", $?); } } if (length($image_data) < $image_data_len) { warn "Error reading image from \"$cur_file\"\n"; if ($ignore_image_errors) { warn "Using blank image instead.\n"; $image_data = pack("x$image_data_len",()); } else { close(MINC); return; } } elsif (length($image_data) > $image_data_len) { warn "More image data than expected - truncating.\n"; $image_data = substr($image_data, 0, $image_data_len); } } # Write out the image print MINC $image_data; } # Close the minc file close(MINC); if ($? != 0) { &cleanup_and_die("Error or signal while writing image.\n", $?); } return $mincfile; } # Routine to add to the list file sub save_list_info { local($listfile, $mincfile, *mincinfo, $image_list, $echo) = @_; local($pos) = $mincinfo{'tape_position'}; local($pat_name) = '"'.substr($mincinfo{'patient_name'},0,30).'"'; local($exam) = $mincinfo{'exam'}; local($acq) = $mincinfo{'series'}; local($nslc) = scalar(split($;, $image_list)); local($date) = '"'.substr($mincinfo{'start_time'},0,30).'"'; local($inst) = '"'.substr($mincinfo{'institution'},0,30).'"'; open(LIST, ">>$listfile") || warn "Error writing to list file \"$listfile\" ($!)\n"; write LIST; close(LIST); format LIST = @>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>> @>> @> @>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $pos $pat_name $exam $acq $echo $nslc $date $inst . } # Routine to get argument values sub get_arguments { # Usage line $0 =~ /([^\/]+)$/; local($prog) = $1; local($usage) = "Usage: $prog [ ...] []\n"; # Set default values local($outputdir) = undef; local($tapedrive) = ''; local($listfile) = ''; local($nominc) = 0; local($tape_position) = 0; local($tape_end) = -1; local(@input_list) = (); local($do_compression) = 0; local($need_diskfiles) = 0; local($ignore_image_errors) = 0; local($inputdir) = ''; # Loop through arguments while (@_) { $_ = shift; if (/^-list$/) { $listfile = shift;} elsif (/^-tape$/) { $tapedrive = shift;} elsif (/^-notape$/) {$need_diskfiles = 1;} elsif (/^-inputdir$/) {$inputdir = shift;} elsif (/^-nominc$/) { $nominc = 1;} elsif (/^-first$/) { $tape_position = shift;} elsif (/^-last$/) {$tape_end = shift;} elsif (/^-compress$/) {$do_compression = 1;} elsif (/^-nocompress$/) {$do_compression = 0;} elsif (/^-ignore_image_errors$/) {$ignore_image_errors = 1;} elsif (/^-h(|elp)$/) { die "Command-specific options: -list:\t\t\tSpecify a file for listing contents of tape -tape:\t\t\tSpecify an input tape drive (default=/dev/nrtape if no files given) -notape:\t\tDo not try to use the tape drive -inputdir:\t\tSpecify directory from which input file names should be read -nominc:\t\tDo not produce output minc files -first:\t\tSpecify a starting tape position (# files to skip) -last:\t\t\tSpecify an ending tape position -compress:\t\tCompress the output minc files -nocompress:\t\tDo not compress the output minc files (default) -ignore_image_errors:\tIgnore errors when reading images Generic options for all commands: -help:\t\t\tPrint summary of comand-line options and abort $usage "; } elsif (/^-./) { die "Unknown option \"$_\"\n"; } else { if (!defined($outputdir)) { $outputdir = $_; } else { push(@input_list, $_); } } } # Get input file names from inputdir if needed if ((length($inputdir) > 0) && (scalar(@input_list) > 0)) { die "Do not use -inputdir option with input files\n"; } if (length($inputdir) > 0) { opendir(DIR, $inputdir) || die "Error opening input directory \"$inputdir\": $!\n"; @input_list = sort(grep(/^[^\.]/, readdir(DIR))); closedir(DIR); local($file); foreach $file (@input_list) { $file = "$inputdir/$file"; } } # Check expected values if ($need_diskfiles && (scalar(@input_list) == 0)) { die "Please specify disk files.\n"; } if ((length($tapedrive) > 0) && (scalar(@input_list) > 0)) { die "You cannot specify both a tape drive and a file list.\n"; } elsif ((length($tapedrive) <= 0) && (scalar(@input_list) <= 0)) { $tapedrive = "/dev/nrtape"; } if (!defined($outputdir)) { die $usage; } if ($tape_position < 0) { die "Tape position must be >= 0\n"; } if (!defined($tape_end)) {$tape_end = -1;} # Return values return($outputdir, $tapedrive, $listfile, $nominc, $tape_position, $tape_end, $do_compression, $ignore_image_errors, @input_list); } # Subroutine to do all the work - loops through files collecting info, # then calls routine to create minc file. # Because this was the main program, it uses global variables sub mri_to_minc { $| = 1; # Save arguments for history @saved_args = @ARGV; # Get arguments ($outputdir, $tapedrive, $listfile, $nominc, $tape_position, $tape_end, $do_compression, $ignore_image_errors, @input_list) = &get_arguments(@_); # Save history chop($history = `date`); $0 =~ /([^\/]+)$/; $history .= ">>>> $1"; foreach $file (@input_list[0..$#input_list]) { $bad_args{$file} = 1; } foreach $arg (@saved_args) { if (! $bad_args{$arg}) { $history .= " $arg"; } } if (scalar(@input_list) > 0) { $history .= " $input_list[0]"; } if (scalar(@input_list) > 1) { $history .= " ... $input_list[$#input_list]"; } $history .= "\n"; # Check that we can write to the output directory if (! -d $outputdir) { die "Directory \"$outputdir\" does not exist.\n"; } if (! -w $outputdir) { die "Cannot write to \"$outputdir\".\n"; } # Should we delete the files? $delete_files = (length($tapedrive) > 0); # Create a temporary directory &create_tmpdir("mri_to_minc_$$"); # Check if list file already exists if (length($listfile) > 0) { if (-e $listfile) { die "List file \"$listfile\" already exists.\n"; } open(LIST_START, ">$listfile") || die "Unable to write to list file \"$listfile\" ($!)\n"; write LIST_START; close(LIST_START); format LIST_START = @>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>> @>> @> @>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 'File' 'Patient Name' 'Exam' 'Acq' 'E#' 'Slc' 'Date' 'Institution' . } # Rewind and initialize the tape if (length($tapedrive) > 0) { &initialize_tape_drive($tapedrive); if ($tape_position > 0) { &set_tape_position($tapedrive, $tape_position); } } # Loop through files on tape $keep_looping = 1; while ($keep_looping) { # Save current tape position $tape_position = &get_tape_position; # Check for reaching last tape file requested if (($tape_end >= 0) && ($tape_position > $tape_end)) { warn "Reached last file requested (file number $tape_end).\n"; $keep_looping = 0; } # Get next file if ($keep_looping) { $nextfile = &read_next_file($tapedrive, *input_list); if (length($nextfile) <= 0) { $keep_looping = 0; } } # Read in headers if ($keep_looping) { undef(%file_info); undef(%specific_file_info); if (&read_file_info($nextfile, *file_info, *specific_file_info)) { warn "Error reading file \"$nextfile\". Skipping to next.\n"; next; } # Get interesting values $cur_numechos = $file_info{'numechos'}; $cur_exam = $file_info{'exam'}; $cur_series = $file_info{'series'}; $cur_echo = $file_info{'echo'}; $cur_image = $file_info{'image'}; if ($cur_numechos > 1) { $cur_image = $cur_image * $cur_numechos + (($cur_echo <= $cur_numechos) ? $cur_echo : 1); } $cur_width = $file_info{'width'}; $cur_height = $file_info{'height'}; $cur_slicepos = $file_info{'slicepos'}; } # Check if number exam or series has changed or if this is the # last file if ((scalar(keys(%file_list)) > 0) && (!$keep_looping || ($mincinfo{'exam'} ne $cur_exam) || ($mincinfo{'series'} != $cur_series))) { # Loop through echos @echos = sort(numeric_order keys(%echo_list)); if (scalar(@echos) > $mincinfo{'numechos'}) { $mincinfo{'numechos'} = scalar(@echos); } foreach $echo (@echos) { # Create minc file ($patient_name = $mincinfo{'patient_name'}) =~ tr/a-zA-Z0-9_/_/cs; # Use only legal chars $patient_name =~ tr/A-Z/a-z/; # Lowercase $patient_name =~ s/_*(.*[^_])_*$/$1/; # Remove _ on ends $mincfile = "$outputdir/".$patient_name."_". $mincinfo{'exam'}."_".$mincinfo{'series'}; if ($mincinfo{'numechos'} > 1) { $mincfile .= "_e$echo"; } $mincfile .= "_mri.mnc"; if (length($listfile) > 0) { &save_list_info($listfile, $mincfile, *mincinfo, $image_list{$echo}, $echo); } if (!$nominc) { print STDERR "Creating minc file \"$mincfile\"\n"; $mincfile = &create_mincfile($mincfile, *file_list, *mincinfo, $image_list{$echo}, $echo); if ($do_compression) { print STDERR "Compressing file \"$mincfile\"\n"; system("gzip $mincfile"); } } else { print STDERR "Not creating minc file \"$mincfile\"\n"; } } # Delete files (if needed) and reset variables if ($delete_files) { &remove_file(values(%file_list)); } undef(%file_list); undef(%echo_list); undef(%image_list); undef(%mincinfo); } # Break out here if stopping loop if (!$keep_looping) {next;} # If first file, then save appropriate info if (scalar(keys(%file_list)) <= 0) { $mincinfo{'history'} = $history; $mincinfo{'tape_position'} = $tape_position; $mincinfo{'numechos'} = $cur_numechos; $mincinfo{'exam'} = $cur_exam; $mincinfo{'series'} = $cur_series; $mincinfo{'width'} = $cur_width; $mincinfo{'height'} = $cur_height; $mincinfo{'pixel_size'} = $file_info{'pixel_size'}; $mincinfo{'patient_name'} = $file_info{'patient_name'}; $mincinfo{'orientation'} = $file_info{'orientation'}; $mincinfo{'tr'} = $file_info{'tr'}; $mincinfo{'ti'} = $file_info{'ti'}; $mincinfo{'mr_flip'} = $file_info{'mr_flip'}; $mincinfo{'scan_seq'} = $file_info{'scan_seq'}; $mincinfo{'ge_pseq'} = $file_info{'ge_pseq'}; $mincinfo{'ge_pseqmode'} = $file_info{'ge_pseqmode'}; $mincinfo{'start_time'} = $file_info{'start_time'}; $mincinfo{'institution'} = $file_info{'institution'}; $mincinfo{'patient_birthdate'} = $file_info{'patient_birthdate'}; $mincinfo{'patient_age'} = $file_info{'patient_age'}; $mincinfo{'patient_sex'} = $file_info{'patient_sex'}; $mincinfo{'patient_id'} = $file_info{'patient_id'}; $mincinfo{'colstep'} = $file_info{'colstep'}; $mincinfo{'rowstep'} = $file_info{'rowstep'}; $mincinfo{'colstart'} = $file_info{'colstart'}; $mincinfo{'rowstart'} = $file_info{'rowstart'}; $mincinfo{'col_dircos_x'} = $file_info{'col_dircos_x'}; $mincinfo{'col_dircos_y'} = $file_info{'col_dircos_y'}; $mincinfo{'col_dircos_z'} = $file_info{'col_dircos_z'}; $mincinfo{'row_dircos_x'} = $file_info{'row_dircos_x'}; $mincinfo{'row_dircos_y'} = $file_info{'row_dircos_y'}; $mincinfo{'row_dircos_z'} = $file_info{'row_dircos_z'}; $mincinfo{'slc_dircos_x'} = $file_info{'slc_dircos_x'}; $mincinfo{'slc_dircos_y'} = $file_info{'slc_dircos_y'}; $mincinfo{'slc_dircos_z'} = $file_info{'slc_dircos_z'}; # Save dicom element information local(@elements) = sort(grep(/^dicom_/, keys(%file_info))); local($element); foreach $element (@elements) { $mincinfo{$element} = $file_info{$element}; } } else { if (($cur_width != $mincinfo{'width'}) || # ($cur_height != $mincinfo{'height'})) { warn "Width or height do not match first file ". "for file $nextfile. Skipping to next.\n"; next; } } # Save echo number if (!defined($mincinfo{'te', $cur_echo})) { $mincinfo{'te', $cur_echo} = $file_info{'te'}; } # Save info for all files foreach $key (keys(%specific_file_info)) { $mincinfo{$cur_image, 'specific', $key} = $specific_file_info{$key}; } $mincinfo{$cur_image, 'slicepos'} = $cur_slicepos; # Keep track of files, image numbers and echo numbers $file_list{$cur_image} = $nextfile; $echo_list{$cur_echo} = ''; if (defined($image_list{$cur_echo})) {$image_list{$cur_echo} .= $; ;} $image_list{$cur_echo} .= $cur_image; } # Rewind the tape if (length($tapedrive) > 0) { &execute_tape_command($tapedrive, "mt -t $tapedrive rewind"); } &cleanup_and_die(); } minc-tools-2.3.00+dfsg/conversion/mri_to_minc/siemens_to_minc.pl0000644000175000000620000006373312574624760024032 0ustar stevestaff# Subroutines for Siemens mri machines # Routine to get a VAX integer sub unpack_int { if (scalar(@_) != 2) { die "Argument error in unpack_int\n"; } local(*string, $offset) = @_; return unpack("i", scalar(reverse(substr($string, $offset, 4)))); } # Routine to get a VAX short sub unpack_short { if (scalar(@_) != 2) { die "Argument error in unpack_short\n"; } local(*string, $offset) = @_; return unpack("s", scalar(reverse(substr($string, $offset, 2)))); } # Routine to get a VAX integer sub unpack_float { if (scalar(@_) != 2) { die "Argument error in unpack_float\n"; } local(*string, $offset) = @_; return unpack("f", scalar(reverse(substr($string, $offset, 2))). scalar(reverse(substr($string, $offset+2, 2)))) / 4.0; } # Routines to do vector and matrix operations sub vector_normalize { local(*vector) = @_; local(@result); $#result = 2; local($scale); local($mag) = 0; for $i (0..2) { $mag += ($vector[$i] ** 2); } $mag = sqrt($mag); if ($mag > 0) {$scale = 1 / $mag;} else {$scale = 1;} for $i (0..2) { $result[$i] = $scale * $vector[$i]; } return @result; } sub vector_equal { local(*vec1, *vec2) = @_; local($result) =1; for $i (0..2) { $result = ($result && ($vec1[$i] == $vec2[$i])); } return $result; } sub vector_dot_product { local(*vec1, *vec2) = @_; local(@result); $#result = 2; for $i (0..2) { $result[$i] = $vec1[$i] * $vec2[$i]; } return @result; } sub vector_cross_product { local(*vec1, *vec2) = @_; local(@result); $#result = 2; $result[0] = $vec1[1] * $vec2[2] - $vec1[2] * $vec2[1]; $result[1] = $vec1[2] * $vec2[0] - $vec1[0] * $vec2[2]; $result[2] = $vec1[0] * $vec2[1] - $vec1[1] * $vec2[0]; return @result; } sub matrix_multiply { local(*mat1, *mat2) = @_; local(@result); $#result = 8; local($temp); for $i (0..2) { for $j (0..2) { $temp = 0; for $k (0..2) { $temp += $mat1[$i*3 + $k] * $mat2[$k*3 + $j]; } $result[$i*3+$j] = $temp; } } return @result; } sub matrix_vector_multiply { local(*matrix, *vector) = @_; local(@result); $#result = 2; local($temp); for $i (0..2) { $temp = 0; for $j (0..2) { $temp += $matrix[$i*3 + $j] * $vector[$j]; } $result[$i] = $temp; } return @result; } sub matrix_transpose { local(*matrix) = @_; local(@result); $#result = 8; for $i (0..2) { for $j (0..2) { $result[$i*3 + $j] = $matrix[$j*3 + $i]; } } return @result; } sub matrix_create_transform { local(*vec1, *vec2, *vec3) = @_; local(@result); $#result = 8; for $i (0..2) { $result[$i*3+0] = $vec1[$i]; $result[$i*3+1] = $vec2[$i]; $result[$i*3+2] = $vec3[$i]; } return @result; } sub matrix_identity { return (1,0,0, 0,1,0, 0,0,1); } sub matrix_xrot { local($angle) = @_; local(@result); local($sin, $cos); $sin = sin($angle); $cos = cos($angle); return (1,0,0, 0,$cos,$sin, 0,-$sin,$cos); } sub matrix_yrot { local($angle) = @_; local(@result); local($sin, $cos); $sin = sin($angle); $cos = cos($angle); return ($cos,0,-$sin, 0,1,0, $sin,0,$cos); } sub matrix_zrot { local($angle) = @_; local(@result); local($sin, $cos); $sin = sin($angle); $cos = cos($angle); return ($cos,$sin,0, -$sin,$cos,0, 0,0,1); } sub matrix_extract_row { local(*matrix, $row) = @_; local($offset) = $row*3; return @matrix[$offset..$offset+2]; } sub get_transform_from_normal { local(*major, *normal) = @_; local(@result); local(@a, @b, @bprime); local(@z, @zprime); local(@first, @second); local(@zero_vector) = (0,0,0); @z = &vector_normalize(*major); @zprime = &vector_normalize(*normal); @a = &vector_cross_product(*z, *zprime); @a = &vector_normalize(*a); if (&vector_equal(*a, *zero_vector)) { return &matrix_identity(); } @b = &vector_cross_product(*z, *a); @bprime = &vector_cross_product(*zprime, *a); @first = &matrix_create_transform(*a, *b, *z); @first = &matrix_transpose(*first); @second = &matrix_create_transform(*a, *bprime, *zprime); @result = &matrix_multiply(*second, *first); return @result; } sub old_get_direction_cosines { local($orientation, *normal, *col_dircos, *row_dircos, *slc_dircos) = @_; # Get transformation from col,row,slice (axis) to x,y,z (world) local(@axis_to_world, @world_to_axis); if ($orientation eq "transverse") { @world_to_axis = (1,0,0, 0,1,0, 0,0,1); } elsif ($orientation eq "sagittal") { @world_to_axis = (0,1,0, 0,0,1, 1,0,0); } elsif ($orientation eq "coronal") { @world_to_axis = (1,0,0, 0,0,1, 0,1,0); } else {die "Unknown orientation \"$orientation\"";} @axis_to_world = &matrix_transpose(*world_to_axis); # Transform normal and set closest axis local(@new_normal) = &matrix_vector_multiply(*world_to_axis, *normal); local(@major) = (0,0,1); # Get transform local(@transform) = &get_transform_from_normal(*major, *new_normal); # Extract direction cosines local(@transpose) = &matrix_transpose(*transform); @col_dircos = @transpose[0..2]; @row_dircos = @transpose[3..5]; @slc_dircos = @transpose[6..8]; # Transform the direction cosines back to x,y,z (world) coordinates @col_dircos = &matrix_vector_multiply(*axis_to_world, *col_dircos); @row_dircos = &matrix_vector_multiply(*axis_to_world, *row_dircos); @slc_dircos = &matrix_vector_multiply(*axis_to_world, *slc_dircos); @col_dircos = &get_dircos(@col_dircos); @row_dircos = &get_dircos(@row_dircos); @slc_dircos = &get_dircos(@slc_dircos); } sub get_transform_from_axes { local($first, $second, $angle) = @_; $angle *= 3.14159265358979323846/180.0; if (($first eq 'tra') && ($second eq 'sag')) { return &matrix_yrot($angle); } elsif (($first eq 'sag') && ($second eq 'tra')) { return &matrix_yrot(-$angle); } elsif (($first eq 'sag') && ($second eq 'cor')) { return &matrix_zrot($angle); } elsif (($first eq 'cor') && ($second eq 'sag')) { return &matrix_zrot(-$angle); } elsif (($first eq 'cor') && ($second eq 'tra')) { return &matrix_xrot($angle); } elsif (($first eq 'tra') && ($second eq 'cor')) { return &matrix_xrot(-$angle); } else { return &matrix_identity(); } } sub get_transform_from_orient_strings { local($orient_str1, $orient_str2) = @_; local(@result); # Parse the orientation strings local($axis_list) = 'tra|sag|cor'; $orient_str1 =~ tr/A-Z/a-z/; $orient_str2 =~ tr/A-Z/a-z/; if ($orient_str1 =~ /^\s*($axis_list)>($axis_list)\s*(\S*)/) { $major_axis = $1; $first_axis = $2; $first_angle = $3; if ($orient_str2 =~ /^\s*>($axis_list)\s*(\S*)/) { $second_axis = $1; $second_angle = $2; } else { $second_axis = ""; $second_angle = 0; } } else { $major_axis = ""; $first_axis = ""; $first_angle = 0; $second_axis = ""; $second_angle = 0; } # Create rotation matrix local(@mat1, @mat2); @mat1 = &get_transform_from_axes($major_axis, $first_axis, $first_angle); @mat2 = &get_transform_from_axes($major_axis, $second_axis, $second_angle); @result = &matrix_multiply(*mat1, *mat2); return @result; } sub get_direction_cosines { local($orientation, $orient_str1, $orient_str2, *normal, *col_dircos, *row_dircos, *slc_dircos) = @_; # Get transformation from col,row,slice (axis) to x,y,z (world) local(@axis_row); if ($orientation eq "transverse") { @axis_row = (0,1,2); } elsif ($orientation eq "sagittal") { @axis_row = (1,2,0); } elsif ($orientation eq "coronal") { @axis_row = (0,2,1); } else {die "Unknown orientation \"$orientation\"";} # Extract rotation angles from strings local(@transform) = &get_transform_from_orient_strings($orient_str1, $orient_str2); # Extract direction cosines local(@transpose) = &matrix_transpose(*transform); @col_dircos = &matrix_extract_row(*transpose, $axis_row[0]); @row_dircos = &matrix_extract_row(*transpose, $axis_row[1]); @slc_dircos = &matrix_extract_row(*transpose, $axis_row[2]); # Fix up the coordinates @col_dircos = &convert_coordinates(@col_dircos); @row_dircos = &convert_coordinates(@row_dircos); @slc_dircos = &convert_coordinates(@slc_dircos); @col_dircos = &get_dircos(@col_dircos); @row_dircos = &get_dircos(@row_dircos); @slc_dircos = &get_dircos(@slc_dircos); # Compare to normal @normal = &get_dircos(@normal); local($diff) = 0; foreach $i (0..2) { $diff += ($normal[$i] - $slc_dircos[$i]) ** 2; } $diff = sqrt($diff); if ($diff > 1.0e-6) { warn "\n\nWARNING!!!!! ". "Calculated image normal doesn't match file value.\n". "Something went wrong in dircos calculation for Numaris 2!\n\n\n"; &cleanup_and_die("Sorry!\n", -1); } } sub convert_coordinates { local(@coords) = @_; $coords[0] *= -1; $coords[2] *= -1; return @coords; } # NUMARIS 2 ROUTINES # Subroutine to read the siemens Numaris 2 file headers sub numaris2_read_headers { # Set constants for reading file local($header_off) = 0; local($header_len) = 8192; local($magic_off) = 244+42; local($magic_len) = 12; local($magic_value) = "SIEMENS MED "; # Check arguements if (scalar(@_) != 2) { &cleanup_and_die("Argument error in numaris2_read_headers",1); } local($filename, *header) = @_; # Open the file if (!open(SMNF, "<".$filename)) { warn "Can't open file $filename: $!"; return 1; } # Read in the header if (!seek(SMNF, $header_off, 0) || (read(SMNF, $header, $header_len) != $header_len)) { return 1; } # Check the image file magic number if (substr($header, $magic_off, $magic_len) ne $magic_value) { warn "Bad image file magic number in \"$filename\""; return 1; } # Close input file close(SMNF); return 0; } # Routine to get Numaris 2 file info sub numaris2_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local($header); if (&numaris2_read_headers($filename, *header)) { return 1; } # Get the file headers local($blksize) = 512; local($ident_hdr) = substr($header, 0, 244); local($relat_hdr) = substr($header, 1*$blksize+162, 166); local($patient_hdr) = substr($header, 6*$blksize, 512); local($meas_hdr) = substr($header, 7*$blksize, 3072); local($image_hdr) = substr($header, 13*$blksize, 512); local($text_hdr) = substr($header, 14*$blksize, 512); # Get interesting values $file_info{'numechos'} = &unpack_int(*meas_hdr, 512); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} # We cheat and use exam date for exam, getting rid of weird characters $file_info{'exam'} = &unpack_value(*ident_hdr, 52, 'A10'); if (!$Use_date_for_exam) { $file_info{'exam'} .= "_" . substr(&unpack_value(*ident_hdr, 70, 'A14'),0,8); } $file_info{'exam'} =~ s/\W//g; $file_info{'series'} = &unpack_int(*patient_hdr, 112); $file_info{'image'} = &unpack_value(*relat_hdr, 56, 'A6') + 0; $file_info{'echo'} = &unpack_int(*meas_hdr, 516); $file_info{'width'} = &unpack_short(*image_hdr, 138); $file_info{'height'} = &unpack_short(*image_hdr, 140); $file_info{'pixel_size'} = 2; $file_info{'patient_name'} = &unpack_value(*patient_hdr, 0, "A26"); local(@normal) = &convert_coordinates(&unpack_float(*meas_hdr, 380), &unpack_float(*meas_hdr, 384), &unpack_float(*meas_hdr, 388)); local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local($fovx, $fovy); local(@col_dircos, @row_dircos, @slc_dircos); $fovx = &unpack_float(*meas_hdr, 400); $fovy = &unpack_float(*meas_hdr, 400); if ($fovy == 0) {$fovy = $fovx}; $file_info{'colstep'} = -$fovx / $file_info{'width'}; $file_info{'rowstep'} = -$fovy / $file_info{'height'}; local($orient_str1) = &unpack_value(*text_hdr, 332, "A11"); local($orient_str2) = &unpack_value(*text_hdr, 343, "A11"); &get_direction_cosines($file_info{'orientation'}, $orient_str1, $orient_str2, *normal, *col_dircos, *row_dircos, *slc_dircos); local($xcentre, $ycentre, $zcentre) = &convert_coordinates(&unpack_float(*meas_hdr, 368), &unpack_float(*meas_hdr, 372), &unpack_float(*meas_hdr, 376)); $file_info{'slicepos'} = $xcentre * $slc_dircos[0] + $ycentre * $slc_dircos[1] + $zcentre * $slc_dircos[2]; $file_info{'colstart'} = ($xcentre * $col_dircos[0] + $ycentre * $col_dircos[1] + $zcentre * $col_dircos[2]) - $file_info{'colstep'} * ($file_info{'width'} - 1) / 2; $file_info{'rowstart'} = ($xcentre * $row_dircos[0] + $ycentre * $row_dircos[1] + $zcentre * $row_dircos[2]) - $file_info{'rowstep'} * ($file_info{'height'} - 1) / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &unpack_float(*meas_hdr, 268)/1000; $file_info{'te'} = &unpack_float(*meas_hdr, 520)/1000; $file_info{'ti'} = &unpack_float(*meas_hdr, 264)/1000; $file_info{'mr_flip'} = &unpack_int(*meas_hdr, 284); $file_info{'scan_seq'} = &unpack_value(*meas_hdr, 172, 'a8'); $file_info{'scan_seq'} =~ s/\0.*$//; $file_info{'scan_seq'} =~ s/\n//; ($file_info{'patient_birthdate'} = &unpack_value(*patient_hdr, 40, 'a10')) =~ s/\./-/g; local($sex_flag) = &unpack_value(*patient_hdr, 52, 'a2'); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &unpack_value(*patient_hdr, 28, 'A12'); $file_info{'institution'} = &unpack_value(*ident_hdr, 144, 'A26'); local($study_date, $study_time); ($study_date = &unpack_value(*ident_hdr, 52, 'A10')) =~ s/\./-/g; $study_time = &unpack_value(*ident_hdr, 70, 'A14'); $file_info{'start_time'} = "$study_date $study_time"; # Get specific file info $specific_file_info{'pixel_data_offset'} = 8192; $specific_file_info{'pixel_data_len'} = $file_info{'width'} * $file_info{'height'} * 2; return 0; } sub numaris2_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in numaris2_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract " . $specific_file_info{'pixel_data_offset'} . " " . $specific_file_info{'pixel_data_len'} . " " . $cur_file . " | byte_swap "; return $cmd; } # NUMARIS 3 ROUTINES # Routine to get a string from the header sub acr_find_string { if (scalar(@_) != 3) { die "Argument error in numaris3_find_string"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'string'}; } # Routine to get an array of values from the header sub acr_find_numeric { local(@values) = split(/\\/, &acr_find_string(@_)); foreach $value (@values) { $value += 0; } return (scalar(@values) > 1) ? @values : $values[0]; } # Routine to get an integer from the header sub acr_find_int { if (scalar(@_) != 3) { die "Argument error in numaris3_find_int"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'short'}; } # Subroutine to read the siemens Numaris 3 file headers sub numaris3_read_headers { # Set constants for reading file local($header_maxid) = "0x0029"; # Check arguements if (scalar(@_) != 2) { &cleanup_and_die("Argument error in numaris3_read_headers",1); } local($filename, *header) = @_; # Dump the header local($group, $element, $data); open(DUMP, "dump_acr_nema $filename $header_maxid|"); while () { chop; if (/^\s*(0x[\da-f]{4,4})\s+(0x[\da-f]{4,4})\s+length = \d+ :(.*)$/) { $group = $1; $element = $2; $data = $3; if ($data =~ /string = "(.*)"$/) { $header{$group, $element, 'string'} = $1; } if ($data =~ /short = (\d+)/) { $header{$group, $element, 'short'} = $1; } } } close(DUMP); # Check the return status if ($? != 0) { warn "Error dumping header for file $filename"; return 1; } return 0; } # Routine to get Numaris 3 file info sub numaris3_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local(%header); undef(%header); if (&numaris3_read_headers($filename, *header)) { return 1; } # Get interesting values $file_info{'numechos'} = &acr_find_numeric(*header, 0x21, 0x1370); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} # We cheat and use study date for exam, getting rid of weird characters $file_info{'exam'} = &acr_find_string(*header, 0x8, 0x22); if (!$Use_date_for_exam) { $file_info{'exam'} .= "_" . substr(&acr_find_string(*header, 0x8, 0x32),0,8); } $file_info{'exam'} =~ s/\W//g; if (length(&acr_find_string(*header, 0x20, 0x11)) > 0) { $file_info{'series'} = &acr_find_numeric(*header, 0x20, 0x11); } else { $file_info{'series'} = &acr_find_numeric(*header, 0x20, 0x10); } $file_info{'image'} = &acr_find_numeric(*header, 0x20, 0x13); $file_info{'echo'} = &acr_find_numeric(*header, 0x18, 0x86); $file_info{'width'} = &acr_find_int(*header, 0x28, 0x11); $file_info{'height'} = &acr_find_int(*header, 0x28, 0x10); local($bits_alloc) = &acr_find_int(*header, 0x28, 0x100); if ($bits_alloc != 16) { warn "Wrong number of bits allocated per image ($bits_alloc)\n"; return 1; } $file_info{'pixel_size'} = 2; $file_info{'patient_name'} = &acr_find_string(*header, 0x10, 0x10); local(@normal) = &convert_coordinates(&acr_find_numeric(*header, 0x21, 0x1161)); if (scalar(@normal) != 3) { @normal = (0,0,1); } local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local(@col_dircos, @row_dircos, @slc_dircos); ($file_info{'rowstep'}, $file_info{'colstep'}) = &acr_find_numeric(*header, 0x28, 0x30); if (length($file_info{'rowstep'}) <= 0) { $file_info{'colstep'} = $file_info{'rowstep'} = 1; } $file_info{'colstep'} *= -1.0; $file_info{'rowstep'} *= -1.0; @col_dircos = &get_dircos (&convert_coordinates(&acr_find_numeric(*header, 0x21, 0x116a))); @row_dircos = &get_dircos (&convert_coordinates(&acr_find_numeric(*header, 0x21, 0x116b))); @slc_dircos = &get_dircos(@normal); local($xcentre, $ycentre, $zcentre) = &convert_coordinates(&acr_find_numeric(*header, 0x21, 0x1160)); $file_info{'slicepos'} = $xcentre * $slc_dircos[0] + $ycentre * $slc_dircos[1] + $zcentre * $slc_dircos[2]; $file_info{'colstart'} = ($xcentre * $col_dircos[0] + $ycentre * $col_dircos[1] + $zcentre * $col_dircos[2]) - $file_info{'colstep'} * ($file_info{'width'} - 1) / 2; $file_info{'rowstart'} = ($xcentre * $row_dircos[0] + $ycentre * $row_dircos[1] + $zcentre * $row_dircos[2]) - $file_info{'rowstep'} * ($file_info{'height'} - 1) / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &acr_find_numeric(*header, 0x18, 0x80)/1000; $file_info{'te'} = &acr_find_numeric(*header, 0x18, 0x81)/1000; $file_info{'ti'} = &acr_find_numeric(*header, 0x18, 0x82)/1000; $file_info{'mr_flip'} = &acr_find_numeric(*header, 0x19, 0x1260); # $file_info{'scan_seq'} = &acr_find_string(*header, 0x18, 0x20); # $file_info{'scan_seq'} =~ s/\0.*$//; # $file_info{'scan_seq'} =~ s/\n//; ($file_info{'patient_birthdate'} = &acr_find_string(*header, 0x10, 0x30)) =~ s/\./-/g; local($sex_flag) = &acr_find_string(*header, 0x10, 0x40); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &acr_find_string(*header, 0x10, 0x20); $file_info{'institution'} = &acr_find_string(*header, 0x8, 0x80); local($study_date, $study_time); ($study_date = &acr_find_string(*header, 0x8, 0x22)) =~ s/\./-/g; $study_time = &acr_find_string(*header, 0x8, 0x32); $file_info{'start_time'} = "$study_date $study_time"; # Get specific file info local($compression_code) = &acr_find_string(*header, 0x28, 0x60); if (($compression_code ne "NONE") && ($compression_code ne "")) { warn "File is compressed\n"; return 1; } $specific_file_info{'pixel_data_group'} = "0x7fe0"; $specific_file_info{'pixel_data_element'} = "0x10"; return 0; } sub numaris3_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in numaris3_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract_acr_nema " . $cur_file . " " . $specific_file_info{'pixel_data_group'} . " " . $specific_file_info{'pixel_data_element'} . " " . " | byte_swap "; return $cmd; } # GENERAL SIEMENS ROUTINES # Routine to initialize tape drive sub siemens_initialize_tape_drive { &cleanup_and_die("siemens_to_minc does not support tape reading.\n",1); } # Routine to read the file info sub siemens_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; local($numaris2_magic) = "SIEMENS MED "; local($numaris2_magic_id) = "0x0009 0x0011"; local($numaris3_magic) = "SIEMENS CM VA0 CMS "; local($numaris3_magic_id) = "0x0009 0x0012"; local($magic); # Check that the file exists and is readable if (! -r $filename) { warn "Unable to open file \"$filename\""; return 1; } # Check for a numaris 2 file $magic = `extract_acr_nema $filename $numaris2_magic_id 2>/dev/null`; if ($magic eq $numaris2_magic) { $specific_file_info{'siemens_file_type'} = "Numaris2"; return &numaris2_read_file_info(@_); } # Check for a numaris 3 file $magic = `extract_acr_nema $filename $numaris3_magic_id 2>/dev/null`; if ($magic eq $numaris3_magic) { $specific_file_info{'siemens_file_type'} = "Numaris3"; return &numaris3_read_file_info(@_); } # Can't figure out file type warn "Unable to identify siemens file type in \"$filename\""; return 1; } # Routine to get the image sub siemens_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in siemens_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; if ($specific_file_info{'siemens_file_type'} eq "Numaris2") { return &numaris2_get_image_cmd(@_); } elsif ($specific_file_info{'siemens_file_type'} eq "Numaris3") { return &numaris3_get_image_cmd(@_); } else { die "Unknown siemens file type"; } } # MAIN PROGRAM *initialize_tape_drive = *siemens_initialize_tape_drive; *read_file_info = *siemens_read_file_info; *get_image_cmd = *siemens_get_image_cmd; # Check for siemens-specific arguments $Use_date_for_exam = 0; @args_to_remove = (); foreach $i (0..$#ARGV) { $_ = $ARGV[$i]; if (/^-h(elp)?$/) { warn "Siemens-specific options: -use_date_for_exam:\tUse only the date to identify the exam, not the time "; } elsif (/^-use_date_for_exam$/) { $Use_date_for_exam = 1; push(@args_to_remove, $i); } } foreach $i (reverse(@args_to_remove)) { splice(@ARGV, $i, 1); } &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/ge_uncompress.c0000644000175000000620000000316212574624760023325 0ustar stevestaff#include #include /* Constants for handling byte order - First is 0 for big-endian, and 1 for little endian; Second is the other way around */ int idummy = 1; char *dummy = (char *) &idummy; int First = 0; int Second = 1; int main(int argc, char *argv[]) { union { unsigned char b[2]; short s; } conv; int first_byte; int second_byte; int base; FILE *fpin, *fpout; /* Figure out byte order for machine */ if ((int) dummy[0] == 1) { /* Little-endian */ First = 1; Second = 0; } else { /* Big-endian */ First = 0; Second = 1; } /* Loop through bytes */ base = 0; fpin = stdin; fpout = stdout; while ((first_byte = getc(fpin)) != EOF) { /* Look for single byte data */ if (first_byte < 0x40) { base += first_byte; } else if (first_byte < 0x80) { base += first_byte - 0x80; } /* Otherwise, look for two or three-byte form */ else { second_byte = getc(fpin); if (first_byte < 0xa0) { base += ((first_byte & 0x1f) << 8) + second_byte; } else if (first_byte < 0xc0) { base += ((first_byte | 0xc0) << 8) + second_byte; } /* We have a three-byte form */ else { conv.b[First] = second_byte; conv.b[Second] = getc(fpin); base = conv.s; } } /* Write out the data */ conv.s = base; (void) putc((int) conv.b[First], fpout); (void) putc((int) conv.b[Second], fpout); } return EXIT_SUCCESS; } minc-tools-2.3.00+dfsg/conversion/mri_to_minc/ge5_to_minc.pl0000644000175000000620000002777712574624760023057 0ustar stevestaff# Subroutines for GE mri machines, version 5.x # Routine to convert unix time to date string sub time_to_string { if (scalar(@_) != 1) { die "Argument error in time_to_string\n"; } local(@months) = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(@_[0]); if ($year >= 70) {$year += 1900;} else {$year += 2000;} return sprintf("%02d-%s-%04d %02d:%02d:%02d GMT", $mday, $months[$mon], $year, $hour, $min, $sec); } # Routine to initialize tape drive sub ge5_initialize_tape_drive { local($tapedrive) = @_; local($nextfile) = &read_next_file($tapedrive); if ($delete_files) { &remove_file($nextfile); } } # Subroutine to read the ge 5.x file headers sub ge5_read_headers { # Check arguements if (scalar(@_) != 8) { &cleanup_and_die("Argument error in ge5_read_headers",1); } local($filename, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len) = @_; # Set constants for reading DAT files local($magic_string) = "IMGF"; local($suite_hdr_len) = 114; local($suite_hdr_off) = 0; local($exam_hdr_len) = 1024; local($exam_hdr_off) = $suite_hdr_off + $suite_hdr_len; local($series_hdr_len) = 1020; local($series_hdr_off) = $exam_hdr_off + $exam_hdr_len; local($image_hdr_len) = 1022; local($image_hdr_off) = $series_hdr_off + $series_hdr_len; local($DAT_pixel_hdr_prefix_len) = 124; local($DAT_pixel_hdr_off) = $image_hdr_off + $image_hdr_len; local($DAT_pixdatsize_len) = 4; # Set pixel header constants for disk files local($DISK_pixel_hdr_prefix_len) = 156; local($DISK_pixel_hdr_off) = 0; # Open file if (!open(GEF, "<".$filename)) { warn "Can't open file $filename: $!\n"; return 1; } # Check to see if this is a disk file or DAT file return 1 if (read(GEF, $pixel_hdr, $DISK_pixel_hdr_prefix_len) != $DISK_pixel_hdr_prefix_len); local($is_DISK_file) = (substr($pixel_hdr, 0, 4) eq $magic_string); # For disk file, read header positions and lengths if ($is_DISK_file) { $suite_hdr_off = &unpack_value(*pixel_hdr, 124, 'i'); $suite_hdr_len = &unpack_value(*pixel_hdr, 128, 'i'); $exam_hdr_off = &unpack_value(*pixel_hdr, 132, 'i'); $exam_hdr_len = &unpack_value(*pixel_hdr, 136, 'i'); $series_hdr_off = &unpack_value(*pixel_hdr, 140, 'i'); $series_hdr_len = &unpack_value(*pixel_hdr, 144, 'i'); $image_hdr_off = &unpack_value(*pixel_hdr, 148, 'i'); $image_hdr_len = &unpack_value(*pixel_hdr, 152, 'i'); $pixel_data_offset = &unpack_value(*pixel_hdr, 4, 'i'); $pixel_data_len = -1; } # For DAT file read in pixel header and get image position and length else { return 1 if (!seek(GEF, $DAT_pixel_hdr_off, 0)); return 1 if (read(GEF, $pixel_hdr, $DAT_pixel_hdr_prefix_len) != $DAT_pixel_hdr_prefix_len); # Check the image file magic number if (substr($pixel_hdr, 0, 4) ne $magic_string) { warn "Bad image file magic number in \"$filename\"\n"; return 1; } # Get the length of the pixel header local($pixel_hdr_len) = &unpack_value(*pixel_hdr, 4, "i"); # Calculate the offset to pixel data local($pixdatsize_len) = $DAT_pixdatsize_len; local($pixdatsize_offset) = $suite_hdr_len + $exam_hdr_len + $series_hdr_len + $image_hdr_len + $pixel_hdr_len; $pixel_data_offset = $pixdatsize_offset + $pixdatsize_len; # Read in the length of the pixel data local($pixdatsize); return 1 if (!seek(GEF, $pixdatsize_offset, 0)); return 1 if (read(GEF, $pixdatsize, $pixdatsize_len) != $pixdatsize_len); $pixel_data_len = &unpack_value(*pixdatsize, 0, "i"); } # Read in file headers return 1 if (!seek(GEF, $suite_hdr_off, 0)); return 1 if (read(GEF, $suite_hdr, $suite_hdr_len) != $suite_hdr_len); return 1 if (!seek(GEF, $exam_hdr_off, 0)); return 1 if (read(GEF, $exam_hdr, $exam_hdr_len) != $exam_hdr_len); return 1 if (!seek(GEF, $series_hdr_off, 0)); return 1 if (read(GEF, $series_hdr, $series_hdr_len) != $series_hdr_len); return 1 if (!seek(GEF, $image_hdr_off, 0)); return 1 if (read(GEF, $image_hdr, $image_hdr_len) != $image_hdr_len); # Close input file close(GEF); return 0; } # Routine to get file info sub ge5_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local($suite_hdr, $exam_hdr, $series_hdr, $image_hdr, $pixel_hdr, $pixel_data_offset, $pixel_data_len); if (&ge5_read_headers($filename, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len)) { return 1; } # Get interesting values $file_info{'numechos'} = &unpack_value(*image_hdr, 210, 's'); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} $file_info{'exam'} = &unpack_value(*image_hdr, 8, 'S'); $file_info{'series'} = &unpack_value(*image_hdr, 10, 's'); $file_info{'image'} = &unpack_value(*image_hdr, 12, 's'); $file_info{'echo'} = &unpack_value(*image_hdr, 212, 's'); $file_info{'width'} = &unpack_value(*pixel_hdr, 8, 'i'); $file_info{'height'} = &unpack_value(*pixel_hdr, 12, 'i'); $file_info{'pixel_size'} = 2; $file_info{'patient_name'} = &unpack_value(*exam_hdr, 97, "A25"); local($orient_flag) = &unpack_value(*image_hdr, 114, 's'); if ($orient_flag == 2) { # Transverse $file_info{'orientation'} = 'transverse'; } elsif ($orient_flag == 4) { # Sagittal $file_info{'orientation'} = 'sagittal'; } elsif ($orient_flag == 8) { # Coronal $file_info{'orientation'} = 'coronal'; } elsif ($orient_flag == 16) { # Oblique (check normal vector) local($norm_r, $norm_a, $norm_s) = &abs(&unpack_value(*image_hdr, 142, 'f3')); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; } else { # Assume transverse print STDERR "orient_flag = $orient_flag, assuming transverse\n"; $file_info{'orientation'} = 'transverse'; } # Crude hack to look for multiple scans with identical series number # Check for new exam/series to reset values. If we have the same # series, then check whether the raw data run number has changed. # If so, then increment the runid and modify the series string that # we are using # Uses globals $GE5_CUR{EXAM,SERIES,RUN} and $GE5_{SERIESID,RUNID} local($thisrun) = &unpack_value(*image_hdr, 383, 'i'); if (!defined($GE5_CUREXAM) || ($file_info{'exam'} != $GE5_CUREXAM) || ($file_info{'series'} != $GE5_CURSERIES)) { $GE5_CUREXAM = $file_info{'exam'}; $GE5_CURSERIES = $file_info{'series'}; $GE5_CURRUN = $thisrun; $GE5_RUNID = ''; $GE5_SERIESID = $GE5_CURSERIES; } elsif ($thisrun != $GE5_CURRUN) { $GE5_CURRUN = $thisrun; $GE5_RUNID++; $GE5_SERIESID = $GE5_CURSERIES . "." . $GE5_RUNID; } $file_info{'series'} = $GE5_SERIESID; # Get coordinate information local($fovx, $fovy); $fovx = &unpack_value(*image_hdr, 34, 'f'); $fovy = &unpack_value(*image_hdr, 38, 'f'); if ($fovy == 0) {$fovy = $fovx}; $file_info{'colstep'} = -$fovx / $file_info{'width'}; # Being fancy doesn't work - just assume square pixels # $file_info{'rowstep'} = -$fovy / $file_info{'height'}; $file_info{'rowstep'} = $file_info{'colstep'}; local($xcentre, $ycentre, $zcentre) = &unpack_value(*image_hdr, 130, 'f3'); local($xnorm, $ynorm, $znorm) = &unpack_value(*image_hdr, 142, 'f3'); local($xtoplh, $ytoplh, $ztoplh) = &unpack_value(*image_hdr, 154, 'f3'); local($xtoprh, $ytoprh, $ztoprh) = &unpack_value(*image_hdr, 166, 'f3'); local($xbotrh, $ybotrh, $zbotrh) = &unpack_value(*image_hdr, 178, 'f3'); local($xnorm, $ynorm, $znorm) = &get_dircos($xnorm, $ynorm, $znorm); local($x_col_dircos, $y_col_dircos, $z_col_dircos) = &get_dircos($xtoprh - $xtoplh, $ytoprh - $ytoplh, $ztoprh - $ztoplh); local($x_row_dircos, $y_row_dircos, $z_row_dircos) = &get_dircos($xbotrh - $xtoprh, $ybotrh - $ytoprh, $zbotrh - $ztoprh); $file_info{'slicepos'} = $xcentre * $xnorm + $ycentre * $ynorm + $zcentre * $znorm; $file_info{'colstart'} = ($xcentre * $x_col_dircos + $ycentre * $y_col_dircos + $zcentre * $z_col_dircos) - $file_info{'colstep'} * ($file_info{'width'} - 1) / 2; $file_info{'rowstart'} = ($xcentre * $x_row_dircos + $ycentre * $y_row_dircos + $zcentre * $z_row_dircos) - $file_info{'rowstep'} * ($file_info{'height'} - 1) / 2; $file_info{'col_dircos_x'} = $x_col_dircos; $file_info{'col_dircos_y'} = $y_col_dircos; $file_info{'col_dircos_z'} = $z_col_dircos; $file_info{'row_dircos_x'} = $x_row_dircos; $file_info{'row_dircos_y'} = $y_row_dircos; $file_info{'row_dircos_z'} = $z_row_dircos; $file_info{'slc_dircos_x'} = $xnorm; $file_info{'slc_dircos_y'} = $ynorm; $file_info{'slc_dircos_z'} = $znorm; # Get other info $file_info{'tr'} = &unpack_value(*image_hdr, 194, 'i')/1000000; $file_info{'te'} = &unpack_value(*image_hdr, 202, 'i')/1000000; $file_info{'ti'} = &unpack_value(*image_hdr, 198, 'i')/1000000; $file_info{'mr_flip'} = &unpack_value(*image_hdr, 254, 's'); $file_info{'scan_seq'} = &unpack_value(*image_hdr, 308, 'a33'); $file_info{'scan_seq'} =~ s/\0.*$//; $file_info{'scan_seq'} =~ s/\n//; $file_info{'ge_pseq'} = &unpack_value(*image_hdr, 304, 's'); $file_info{'ge_pseqmode'} = &unpack_value(*image_hdr, 306, 's'); $file_info{'patient_age'} = &unpack_value(*exam_hdr, 122, 's'); local($sex_flag) = &unpack_value(*exam_hdr, 126, 's'); if ($sex_flag == 1) { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag == 2) { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &unpack_value(*exam_hdr, 84, 'A13'); $file_info{'institution'} = &unpack_value(*exam_hdr, 10, 'A33'); $file_info{'start_time'} = &time_to_string(&unpack_value(*image_hdr, 14, 'i')); # Get GE specific stuff $specific_file_info{'pixel_data_offset'} = $pixel_data_offset; $specific_file_info{'pixel_data_len'} = $pixel_data_len; local($compress) = &unpack_value(*pixel_hdr, 20, 'i'); $specific_file_info{'compress'} = $compress; if (($compress != 1) && ($compress != 3)) { warn "Unusable compression scheme ($compress).\n"; return 1; } local($depth) = &unpack_value(*pixel_hdr, 16, 'i'); $specific_file_info{'depth'} = $depth; if ($depth != 16) { warn "Unusable pixel depth ($depth).\n"; return 1; } return 0; } sub ge5_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in ge5_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract " . $specific_file_info{'pixel_data_offset'} . " " . $specific_file_info{'pixel_data_len'} . " " . $cur_file; if ($specific_file_info{'compress'} == 3) { $cmd .= " | ge_uncompress"; } return $cmd; } # MAIN PROGRAM *initialize_tape_drive = *ge5_initialize_tape_drive; *read_file_info = *ge5_read_file_info; *get_image_cmd = *ge5_get_image_cmd; &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/ge4_to_minc.old0000755000175000000620000004107112574624760023203 0ustar stevestaff#! /usr/local/bin/perl # # Script to read files from a GE signa 4.x DAT tape and convert to minc # format # # Based on an original written for GE signa 5.x by Peter Neelin. # Modification for 4.x by Gabriel Leger. # sub numeric_order { $a - $b;} # Subroutine to clean up files and exit sub cleanup_and_die { # Get message to print and exit status local($message,$status) = @_; if (!defined($status)) {$status = 0;} if (defined($message)) { print STDERR $message; if ($message !~ /\n$/) {print STDERR "\n";} } $SIG{'INT'} = 'IGNORE'; $SIG{'TERM'} = 'IGNORE'; $SIG{'QUIT'} = 'IGNORE'; # Check for temp files if (defined($tmpdir) && -e $tmpdir) { print STDERR "Cleaning up temporary files.\n"; system "rm -rf $tmpdir"; } exit($status); } # Subroutine to execute commands and check return status sub execute { local($status); if ($print_execution_statements) { print join(' ',@_),"\n"; } $status = system @_; if ($status != 0) { &cleanup_and_die("Error executing command \"".join(" ",@_)."\".\n", $status); } } # Subroutine to remove a list of files sub remove_file { unlink @_; } # Subroutine to create a temporary directory (global variable $tmpdir # is created) sub create_tmpdir { local($subdir); if (scalar(@_) >= 1) { $subdir = $_[0]; } else { $subdir = $$; } if (defined($ENV{TMPDIR})) { $tmpdir = $ENV{TMPDIR}; } else { $tmpdir = "/usr/tmp"; } $tmpdir .= "/$subdir"; mkdir($tmpdir,0777) || die "Unable to create directory $tmpdir: $!"; $SIG{'INT'} = 'cleanup_and_die'; $SIG{'TERM'} = 'cleanup_and_die'; $SIG{'QUIT'} = 'cleanup_and_die'; } # Subroutine to read a file from tape sub read_next_file { local($tapedrive, *input_list) = @_; if (!defined($tapedrive) && !defined(@input_list)) { $tapedrive = "/dev/tape"; } # Constants $tape_block_size = 8192; $tape_sleep = 1; # Get next value from list if no tape drive if (length($tapedrive) == 0) { return shift(@input_list); } # Create file counting variable if it does not exist if (!defined($counter_for_read_next_file)) { $counter_for_read_next_file = 0; } # Create the filename local($filename) = "$tmpdir/datafile_".$counter_for_read_next_file; $counter_for_read_next_file++; # Sleep for a moment, then read from tape drive (don't ask me why, # it just works!) print STDERR "Retrieving file $filename from drive $tapedrive\n"; select(undef, undef, undef, $tape_sleep); local($status) = system("dd if=$tapedrive of=$filename ". "ibs=$tape_block_size >/dev/null 2>/dev/null"); if (($status!=0) || -z $filename) { print STDERR "End of tape.\n"; &remove_file($filename); return ""; } else { return $filename; } } # Subroutine to read the ge DAT headers sub read_gedat_headers { # Set constants for reading file local($suite_hdr_len) = 3072; local($study_hdr_len) = 1024; local($series_hdr_len) = 1024; local($image_hdr_len) = 1024; local($raw_hdr_len) = 2048; # Check arguements if (scalar(@_) != 7) { &cleanup_and_die("Argument error in read_gedat_headers",1); } local($filename, *suite_hdr, *study_hdr, *series_hdr, *image_hdr, *raw_hdr, *pixel_data_offset) = @_; # Read in file headers if (!open(GEF, "<".$filename)) { warn "Can't open file $filename: $!"; return 1; } return 1 if (read(GEF, $suite_hdr, $suite_hdr_len) != $suite_hdr_len); return 1 if (read(GEF, $study_hdr, $study_hdr_len) != $study_hdr_len); return 1 if (read(GEF, $series_hdr, $series_hdr_len) != $series_hdr_len); return 1 if (read(GEF, $image_hdr, $image_hdr_len) != $image_hdr_len); return 1 if (read(GEF, $raw_hdr, $raw_hdr_len) != $raw_hdr_len); # Calculate the offset to pixel data $pixel_data_offset = 14336; # Close input file close(GEF); return 0; } # Routine to convert data general float to ieee sub convert_dg_float_to_ieee { local($packed_float, $ival, $exponent, $mantissa, @result, $int_result); @result = (); foreach $packed_float (@_) { $ival = unpack('L',$packed_float); if ($ival == 0.0) { push(@result, 0.0); } else { $exponent = (((($ival >> 24) & 0x7f) - 64) * 4) + 127; $mantissa = $ival & 0x00ffffff; while ($mantissa && (!( $mantissa & 0x00800000))) { $exponent--; $mantissa = $mantissa << 1; } $exponent--; $exponent = $exponent << 23; $mantissa &= 0x007fffff; $int_result = ($ival & 0x80000000) | $exponent | $mantissa; push(@result, unpack('f',pack('L', $int_result))); } } return @result; } # Subroutine to unpack a value from a string sub unpack_value { local(*string, $offset, $type) = @_; if ($dump_unpack_value) { print "unpack_value: length, offset, type = ",length($string), " $offset $type\n"; } return unpack("x$offset $type", $string); } # Subroutine to unpack a data general float from a string sub unpack_float { local(*string, $offset) = @_; if ($dump_unpack_value) { print "unpack_value: length, offset = ",length($string), " $offset\n"; } return &convert_dg_float_to_ieee(substr($string, $offset, 4)); } # Subroutine to create a minc file sub create_mincfile { if (scalar(@_) != 6) { die "Argument error in create_mincfile"; } local($mincfile, *file_list, *fileinfo, $firstslice, $lastslice, $echo) = @_; # Get number of slices local($nslices) = $lastslice - $firstslice + 1; if ($nslices <= 0) { warn "No images to copy for minc file \"$mincfile\""; return; } # Check for existence of file - create a new name if it exists local($filebase); if ($mincfile =~ /^(.*)\.mnc$/) { $filebase = $1; } else { $filebase = $mincfile; } local($version) = 1; while (-e $mincfile) { $version++; $mincfile = $filebase."_version$version.mnc"; print STDERR "Minc file already exists. Trying name \"$mincfile\".\n"; } # Set up rawtominc command local($nrows, $ncols, $orientation, $pixel_size, $slicestep); local($xstep, $ystep, $zstep, $xstart, $ystart, $zstart); local($orient_flag); $pixel_size = $fileinfo{'depth'} / 8; if ($nslices > 1) { $slicestep = ($fileinfo{'sliceend'} - $fileinfo{'slicestart'}) / ($nslices - 1); } else { $slicestep = 1; } $nrows = $fileinfo{'height'}; $ncols = $fileinfo{'width'}; $xstep = $fileinfo{'colstep'}; $ystep = $fileinfo{'rowstep'}; $zstep = $slicestep; $orient_flag = $fileinfo{'orientation'}; if ($orient_flag == 1) { # Sagittal ($ystep, $zstep, $xstep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($ystart, $zstart, $xstart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-sagittal"; } elsif ($orient_flag == 2) { # Coronal ($xstep, $zstep, $ystep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($xstart, $zstart, $ystart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-coronal"; } else { # Transverse ($xstep, $ystep, $zstep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($xstart, $ystart, $zstart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-transverse"; } open(MINC, "|itof |rawtominc $mincfile $nslices $nrows $ncols -noclobber ". "-float -obyte $orientation ". "-xstep $xstep -ystep $ystep -zstep $zstep ". "-xstart $xstart -ystart $ystart -zstart $zstart ". "-mri -attribute patient:full_name='".$fileinfo{'patient_name'}."' ". "-attribute acquisition:repetition_time='".$fileinfo{'tr'}."' ". "-attribute acquisition:echo_time='".$fileinfo{'te',$echo}."' ". "-attribute acquisition:inversion_time='".$fileinfo{'ti'}."' ". "-attribute acquisition:flip_angle='".$fileinfo{'mr_flip'}."' ". "-attribute acquisition:scanning_sequence='".$fileinfo{'psdname'}. "' ". "-attribute ge_mrimage:pseq='".$fileinfo{'pseq'}."' ". "-attribute ge_mrimage:pseqmode='".$fileinfo{'pseqmode'}."' ". ""); # Loop through slices local($cur_file); foreach $slice ($firstslice..$lastslice) { # Get file name $cur_file = $file_list{$slice, $echo}; # Print log message if (length($cur_file) > 0) { print STDERR "Getting data from file \"$cur_file\" for slice $slice.\n"; } else { print STDERR "Using blank image for slice $slice.\n"; } # Set up variables for getting the image $image_data_len = $nrows * $ncols * $pixel_size; if ($fileinfo{'compress'} == 0) { # No compression $decompress_cmd = ""; } else { close(MINC); warn "Bad compression code while creating minc file \"$mincfile\""; return; } # Read the image (if not defined, create a blank image) if (length($cur_file) <= 0) { $image_data = pack("x$image_data_len",()); } else { open(GEDAT, "extract ". $fileinfo{$slice, $echo, 'pixel_data_offset'} . " " . $fileinfo{$slice, $echo, 'pixel_data_len'} . " " . $cur_file . $decompress_cmd . " |"); read(GEDAT, $image_data, $image_data_len); close(GEDAT); if (length($image_data) != $image_data_len) { close(MINC); warn "Error decompresssing from $cur_file ". "while creating minc file \"$mincfile\""; return; } } # Write out the image print MINC $image_data; } # Close the minc file close(MINC); } # MAIN PROGRAM $| = 1; $outputdir = shift; if (!defined($outputdir)) { die "Usage: $0 [/dev/ | ...]"; } # Get arguments if (!defined($ARGV[0])) { $tapedrive = "/dev/nrtape"; @input_list = (); } elsif ($ARGV[0] =~ m+^/dev/+) { $tapedrive = shift; @input_list = (); if (scalar(@ARGV) > 0) { die "Do not specify tapedrive and input file list, stopped"; } } else { $tapedrive = ''; @input_list = @ARGV; } # Should we delete the files? $delete_files = (length($tapedrive) > 0); # Create a temporary directory &create_tmpdir("read_ge_dat_$$"); # Rewind the tape if (length($tapedrive) > 0) { &execute("mt -t $tapedrive rewind"); } # Skip the first file on the tape $nextfile = &read_next_file($tapedrive); if ($delete_files) { &remove_file($nextfile); } # Loop through files on tape $keep_looping = 1; while ($keep_looping) { # Get next file $nextfile = &read_next_file($tapedrive, *input_list); if ($nextfile eq "") { $keep_looping = 0; } # Read in headers if ($keep_looping) { $image_hdr = 1; if (&read_gedat_headers($nextfile, *suite_hdr, *study_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset)) { warn "Error reading file \"$nextfile\". Skipping to next."; next; } # Get interesting values $cur_numechos = &unpack_value(*image_hdr, 196, 's'); if ($cur_numechos <= 0) {$cur_numechos = 1;} $cur_study = &unpack_value(*study_hdr, 64, 'A6')+0; $cur_series = &unpack_value(*image_hdr, 92, 'A4')+0; $cur_image = int((&unpack_value(*image_hdr, 88, 'A4')-1)/$cur_numechos); $cur_echo = &unpack_value(*image_hdr, 198, 's'); $cur_width = &unpack_value(*image_hdr, 274, 's'); $cur_height = &unpack_value(*image_hdr, 276, 's'); $cur_depth = &unpack_value(*image_hdr, 284, 's'); if ($cur_depth <= 0) {$cur_depth = 16;} $cur_compress = &unpack_value(*image_hdr, 282, 's'); $cur_slicepos = &unpack_float(*image_hdr, 146); } # Check if number study or series has changed or if this is the last file if ((scalar(keys(%file_list)) > 0) && (!$keep_looping || ($fileinfo{'study'} != $cur_study) || ($fileinfo{'series'} != $cur_series))) { # Loop through echos @echos = sort(numeric_order keys(%echo_list)); foreach $echo (@echos) { # Create minc file ($patient_name = $fileinfo{'patient_name'}) =~ tr/a-zA-Z0-9_\-/_/cs; $patient_name =~ tr/A-Z/a-z/; $mincfile = "$outputdir/".$patient_name."_". $fileinfo{'study'}."_".$fileinfo{'series'}; if (scalar(@echos) > 1) { $mincfile .= "_e$echo"; } $mincfile .= "_mri.mnc"; print STDERR "Creating minc file \"$mincfile\"\n"; &create_mincfile($mincfile, *file_list, *fileinfo, $fileinfo{'firstslice'}, $fileinfo{'lastslice'}, $echo); } # Delete files (if needed) and reset variables if ($delete_files) { &remove_file(values(%file_list)); } undef(%file_list, %echo_list, %fileinfo); } # Break out here if stopping loop if (!$keep_looping) {next;} # Check for unusable compression code and depth if ($cur_compress != 0) { warn "Unusable compression scheme ($cur_compress) in \"$nextfile\". ". "Skipping."; next; } if ($cur_depth != 16) { warn "Unusable pixel depth ($cur_depth) in \"$nextfile\". Skipping."; next; } # Save info for all files $fileinfo{$cur_image, $cur_echo, 'pixel_data_offset'} = $pixel_data_offset; $fileinfo{$cur_image, $cur_echo, 'pixel_data_len'} = $cur_width * $cur_height * $cur_depth; # If first file, then save appropriate info if (scalar(keys(%file_list)) <= 0) { $fileinfo{'study'} = $cur_study; $fileinfo{'series'} = $cur_series; $fileinfo{'width'} = $cur_width; $fileinfo{'height'} = $cur_height; $fileinfo{'depth'} = $cur_depth; $fileinfo{'compress'} = $cur_compress; $fileinfo{'patient_name'} = &unpack_value(*study_hdr, 108, "A25"); $fileinfo{'orientation'} = &unpack_value(*series_hdr, 276, 's'); $fovx = &unpack_float(*series_hdr, 302); $fovy = $fovx; $fileinfo{'colstep'} = -$fovx / $fileinfo{'width'}; $fileinfo{'rowstep'} = -(($fovy != 0) ? $fovy : $fovx) / $fileinfo{'height'}; $fileinfo{'firstslice'} = $cur_image; $fileinfo{'slicestart'} = $cur_slicepos; $fileinfo{'lastslice'} = $cur_image; $fileinfo{'sliceend'} = $cur_slicepos; $fileinfo{'tr'} = &unpack_float(*image_hdr, 164)/1000000; $fileinfo{'ti'} = &unpack_float(*image_hdr, 176)/1000000; $fileinfo{'mr_flip'} = &unpack_value(*image_hdr, 350, 's'); $fileinfo{'pseq'} = &unpack_value(*series_hdr, 298, 's'); $fileinfo{'pseqmode'} = &unpack_value(*series_hdr, 408, 's'); $fileinfo{'psdname'} = &unpack_value(*series_hdr, 410, 'A12'); $fileinfo{'psdname'} =~ s/\0.*$//; $fileinfo{'psdname'} =~ s/\n//; } else { if (($cur_width != $fileinfo{'width'}) || ($cur_height != $fileinfo{'height'}) || ($cur_compress != $fileinfo{'compress'})) { warn "Width, height or compression do not match first file ". "for file $nextfile. Skipping to next."; next; } if ($cur_image < $fileinfo{'firstslice'}) { $fileinfo{'firstslice'} = $cur_image; $fileinfo{'slicestart'} = $cur_slicepos; } if ($cur_image > $fileinfo{'lastslice'}) { $fileinfo{'lastslice'} = $cur_image; $fileinfo{'sliceend'} = $cur_slicepos; } } if (!defined($fileinfo{'te', $cur_echo})) { $fileinfo{'te', $cur_echo} = &unpack_float(*image_hdr, 172)/1000000; } # Keep track of files, image numbers and echo numbers $file_list{$cur_image, $cur_echo} = $nextfile; $echo_list{$cur_echo} = ''; } # Rewind the tape if (length($tapedrive) > 0) { &execute("mt -t $tapedrive rewind"); } &cleanup_and_die; minc-tools-2.3.00+dfsg/conversion/mri_to_minc/ge5_to_minc.old0000755000175000000620000004022112574624760023200 0ustar stevestaff#! /usr/local/bin/perl # # Script to read files from a GE signa 5.x DAT tape and convert to minc # format # sub numeric_order { $a - $b;} # Subroutine to clean up files and exit sub cleanup_and_die { # Get message to print and exit status local($message,$status) = @_; if (!defined($status)) {$status = 0;} if (defined($message)) { print STDERR $message; if ($message !~ /\n$/) {print STDERR "\n";} } $SIG{'INT'} = 'IGNORE'; $SIG{'TERM'} = 'IGNORE'; $SIG{'QUIT'} = 'IGNORE'; # Check for temp files if (defined($tmpdir) && -e $tmpdir) { print STDERR "Cleaning up temporary files.\n"; system "rm -rf $tmpdir"; } exit($status); } # Subroutine to execute commands and check return status sub execute { local($status); if ($print_execution_statements) { print join(' ',@_),"\n"; } $status = system @_; if ($status != 0) { &cleanup_and_die("Error executing command \"".join(" ",@_)."\".\n", $status); } } # Subroutine to remove a list of files sub remove_file { unlink @_; } # Subroutine to create a temporary directory (global variable $tmpdir # is created) sub create_tmpdir { local($subdir); if (scalar(@_) >= 1) { $subdir = $_[0]; } else { $subdir = $$; } if (defined($ENV{TMPDIR})) { $tmpdir = $ENV{TMPDIR}; } else { $tmpdir = "/usr/tmp"; } $tmpdir .= "/$subdir"; mkdir($tmpdir,0777) || die "Unable to create directory $tmpdir: $!"; $SIG{'INT'} = 'cleanup_and_die'; $SIG{'TERM'} = 'cleanup_and_die'; $SIG{'QUIT'} = 'cleanup_and_die'; } # Subroutine to read a file from tape sub read_next_file { local($tapedrive, *input_list) = @_; if (!defined($tapedrive) && !defined(@input_list)) { $tapedrive = "/dev/tape"; } # Constants $tape_block_size = 8192; $tape_sleep = 1; # Get next value from list if no tape drive if (length($tapedrive) == 0) { return shift(@input_list); } # Create file counting variable if it does not exist if (!defined($counter_for_read_next_file)) { $counter_for_read_next_file = 0; } # Create the filename local($filename) = "$tmpdir/datafile_".$counter_for_read_next_file; $counter_for_read_next_file++; # Sleep for a moment, then read from tape drive (don't ask me why, # it just works!) print STDERR "Retrieving file $filename from drive $tapedrive\n"; select(undef, undef, undef, $tape_sleep); local($status) = system("dd if=$tapedrive of=$filename ". "ibs=$tape_block_size >/dev/null 2>/dev/null"); if (($status!=0) || -z $filename) { print STDERR "End of tape.\n"; &remove_file($filename); return ""; } else { return $filename; } } # Subroutine to read the ge DAT headers sub read_gedat_headers { # Set constants for reading file local($suite_hdr_len) = 114; local($exam_hdr_len) = 1024; local($series_hdr_len) = 1020; local($image_hdr_len) = 1022; local($pixel_hdr_prefix_len) = 124; local($pixdatsize_len) = 4; # Check arguements if (scalar(@_) != 8) { &cleanup_and_die("Argument error in read_gedat_headers",1); } local($filename, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len) = @_; # Read in file headers if (!open(GEF, "<".$filename)) { warn "Can't open file $filename: $!"; return 1; } return 1 if (read(GEF, $suite_hdr, $suite_hdr_len) != $suite_hdr_len); return 1 if (read(GEF, $exam_hdr, $exam_hdr_len) != $exam_hdr_len); return 1 if (read(GEF, $series_hdr, $series_hdr_len) != $series_hdr_len); return 1 if (read(GEF, $image_hdr, $image_hdr_len) != $image_hdr_len); return 1 if (read(GEF, $pixel_hdr, $pixel_hdr_prefix_len) != $pixel_hdr_prefix_len); # Check the image file magic number if (unpack("a4",$pixel_hdr) ne "IMGF") { warn "Bad image file magic number in \"$filename\""; return 1; } # Get the length of the pixel header local($pixel_hdr_len) = unpack("L", substr($pixel_hdr, 4, 4)); # Calculate the offset to pixel data local($pixdatsize_offset) = $suite_hdr_len + $exam_hdr_len + $series_hdr_len + $image_hdr_len + $pixel_hdr_len; $pixel_data_offset = $pixdatsize_offset + $pixdatsize_len; # Read in the length of the pixel data local($pixdatsize); seek(GEF, $pixdatsize_offset, 0); return 1 if (read(GEF, $pixdatsize, $pixdatsize_len) != $pixdatsize_len); $pixel_data_len = unpack("L",$pixdatsize); # Close input file close(GEF); return 0; } # Subroutine to unpack a value from a string sub unpack_value { local(*string, $offset, $type) = @_; if ($dump_unpack_value) { print "unpack_value: length, offset, type = ",length($string), " $offset $type\n"; } return unpack("x$offset $type", $string); } # Subroutine to create a minc file sub create_mincfile { if (scalar(@_) != 6) { die "Argument error in create_mincfile"; } local($mincfile, *file_list, *fileinfo, $firstslice, $lastslice, $echo) = @_; # Get number of slices local($nslices) = $lastslice - $firstslice + 1; if ($nslices <= 0) { warn "No images to copy for minc file \"$mincfile\""; return; } # Check for existence of file - create a new name if it exists local($filebase); if ($mincfile =~ /^(.*)\.mnc$/) { $filebase = $1; } else { $filebase = $mincfile; } local($version) = 1; while (-e $mincfile) { $version++; $mincfile = $filebase."_version$version.mnc"; print STDERR "Minc file already exists. Trying name \"$mincfile\".\n"; } # Set up rawtominc command local($nrows, $ncols, $orientation, $pixel_size, $slicestep); local($xstep, $ystep, $zstep, $xstart, $ystart, $zstart); local($orient_flag); $pixel_size = $fileinfo{'depth'} / 8; if ($nslices > 1) { $slicestep = ($fileinfo{'sliceend'} - $fileinfo{'slicestart'}) / ($nslices - 1); } else { $slicestep = 1; } $nrows = $fileinfo{'height'}; $ncols = $fileinfo{'width'}; $xstep = $fileinfo{'colstep'}; $ystep = $fileinfo{'rowstep'}; $zstep = $slicestep; $orient_flag = $fileinfo{'orientation'}; if ($orient_flag == 4) { # Sagittal ($ystep, $zstep, $xstep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($ystart, $zstart, $xstart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-sagittal"; } elsif ($orient_flag == 8) { # Coronal ($xstep, $zstep, $ystep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($xstart, $zstart, $ystart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-coronal"; } else { # Transverse ($xstep, $ystep, $zstep) = ($fileinfo{'colstep'}, $fileinfo{'rowstep'}, $slicestep); ($xstart, $ystart, $zstart) = (0, 0, $fileinfo{'slicestart'}); $orientation = "-transverse"; } open(MINC, "|itof |rawtominc $mincfile $nslices $nrows $ncols -noclobber ". "-float -obyte $orientation ". "-xstep $xstep -ystep $ystep -zstep $zstep ". "-xstart $xstart -ystart $ystart -zstart $zstart ". "-mri -attribute patient:full_name='".$fileinfo{'patient_name'}."' ". "-attribute acquisition:repetition_time='".$fileinfo{'tr'}."' ". "-attribute acquisition:echo_time='".$fileinfo{'te',$echo}."' ". "-attribute acquisition:inversion_time='".$fileinfo{'ti'}."' ". "-attribute acquisition:flip_angle='".$fileinfo{'mr_flip'}."' ". "-attribute acquisition:scanning_sequence='".$fileinfo{'psdname'}. "' ". "-attribute ge_mrimage:pseq='".$fileinfo{'pseq'}."' ". "-attribute ge_mrimage:pseqmode='".$fileinfo{'pseqmode'}."' ". ""); # Loop through slices local($cur_file); foreach $slice ($firstslice..$lastslice) { # Get file name $cur_file = $file_list{$slice, $echo}; # Print log message if (length($cur_file) > 0) { print STDERR "Getting data from file \"$cur_file\" for slice $slice.\n"; } else { print STDERR "Using blank image for slice $slice.\n"; } # Set up variables for getting the image $image_data_len = $nrows * $ncols * $pixel_size; if ($fileinfo{'compress'} == 1) { # No compression $decompress_cmd = ""; } elsif ($fileinfo{'compress'} == 3) { # Compression $decompress_cmd = " | ge_uncompress"; } else { close(MINC); warn "Bad compression code while creating minc file \"$mincfile\""; return; } # Read the image (if not defined, create a blank image) if (length($cur_file) <= 0) { $image_data = pack("x$image_data_len",()); } else { open(GEDAT, "extract ". $fileinfo{$slice, $echo, 'pixel_data_offset'} . " " . $fileinfo{$slice, $echo, 'pixel_data_len'} . " " . $cur_file . $decompress_cmd . " |"); read(GEDAT, $image_data, $image_data_len); close(GEDAT); if (length($image_data) != $image_data_len) { close(MINC); warn "Error decompresssing from $cur_file ". "while creating minc file \"$mincfile\""; return; } } # Write out the image print MINC $image_data; } # Close the minc file close(MINC); } # MAIN PROGRAM $| = 1; $outputdir = shift; if (!defined($outputdir)) { die "Usage: $0 [/dev/ | ...]"; } # Get arguments if (!defined($ARGV[0])) { $tapedrive = "/dev/nrtape"; @input_list = (); } elsif ($ARGV[0] =~ m+^/dev/+) { $tapedrive = shift; @input_list = (); if (scalar(@ARGV) > 0) { die "Do not specify tapedrive and input file list, stopped"; } } else { $tapedrive = ''; @input_list = @ARGV; } # Should we delete the files? $delete_files = (length($tapedrive) > 0); # Create a temporary directory &create_tmpdir("read_ge_dat_$$"); # Rewind the tape if (length($tapedrive) > 0) { &execute("mt -t $tapedrive rewind"); } # Skip the first file on the tape $nextfile = &read_next_file($tapedrive); if ($delete_files) { &remove_file($nextfile); } # Loop through files on tape $keep_looping = 1; while ($keep_looping) { # Get next file $nextfile = &read_next_file($tapedrive, *input_list); if ($nextfile eq "") { $keep_looping = 0; } # Read in headers if ($keep_looping) { $image_hdr = 1; if (&read_gedat_headers($nextfile, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len)) { warn "Error reading file \"$nextfile\". Skipping to next."; next; } # Get interesting values $cur_numechos = &unpack_value(*image_hdr, 210, 's'); if ($cur_numechos <= 0) {$cur_numechos = 1;} $cur_exam = &unpack_value(*image_hdr, 8, 'S'); $cur_series = &unpack_value(*image_hdr, 10, 's'); $cur_image = int((&unpack_value(*image_hdr, 12, 's')-1)/$cur_numechos); $cur_echo = &unpack_value(*image_hdr, 212, 's'); $cur_width = &unpack_value(*pixel_hdr, 8, 'i'); $cur_height = &unpack_value(*pixel_hdr, 12, 'i'); $cur_depth = &unpack_value(*pixel_hdr, 16, 'i'); $cur_compress = &unpack_value(*pixel_hdr, 20, 'i'); $cur_slicepos = &unpack_value(*image_hdr, 126, 'f'); } # Check if number exam or series has changed or if this is the last file if ((scalar(keys(%file_list)) > 0) && (!$keep_looping || ($fileinfo{'exam'} != $cur_exam) || ($fileinfo{'series'} != $cur_series))) { # Loop through echos @echos = sort(numeric_order keys(%echo_list)); foreach $echo (@echos) { # Create minc file ($patient_name = $fileinfo{'patient_name'}) =~ tr/a-zA-Z0-9_\-/_/cs; $patient_name =~ tr/A-Z/a-z/; $mincfile = "$outputdir/".$patient_name."_". $fileinfo{'exam'}."_".$fileinfo{'series'}; if (scalar(@echos) > 1) { $mincfile .= "_e$echo"; } $mincfile .= "_mri.mnc"; print STDERR "Creating minc file \"$mincfile\"\n"; &create_mincfile($mincfile, *file_list, *fileinfo, $fileinfo{'firstslice'}, $fileinfo{'lastslice'}, $echo); } # Delete files (if needed) and reset variables if ($delete_files) { &remove_file(values(%file_list)); } undef(%file_list, %echo_list, %fileinfo); } # Break out here if stopping loop if (!$keep_looping) {next;} # Check for unusable compression code and depth if (($cur_compress != 1) && ($cur_compress != 3)) { warn "Unusable compression scheme ($cur_compress) in \"$nextfile\". ". "Skipping."; next; } if ($cur_depth != 16) { warn "Unusable pixel depth ($cur_depth) in \"$nextfile\". Skipping."; next; } # Save info for all files $fileinfo{$cur_image, $cur_echo, 'pixel_data_offset'} = $pixel_data_offset; $fileinfo{$cur_image, $cur_echo, 'pixel_data_len'} = $pixel_data_len; # If first file, then save appropriate info if (scalar(keys(%file_list)) <= 0) { $fileinfo{'exam'} = $cur_exam; $fileinfo{'series'} = $cur_series; $fileinfo{'width'} = $cur_width; $fileinfo{'height'} = $cur_height; $fileinfo{'depth'} = $cur_depth; $fileinfo{'compress'} = $cur_compress; $fileinfo{'patient_name'} = &unpack_value(*exam_hdr, 97, "A25"); $fileinfo{'orientation'} = &unpack_value(*image_hdr, 114, 's'); $fovx = &unpack_value(*image_hdr, 34, 'f'); $fovy = &unpack_value(*image_hdr, 38, 'f'); $fileinfo{'colstep'} = -$fovx / $fileinfo{'width'}; $fileinfo{'rowstep'} = -(($fovy != 0) ? $fovy : $fovx) / $fileinfo{'height'}; $fileinfo{'firstslice'} = $cur_image; $fileinfo{'slicestart'} = $cur_slicepos; $fileinfo{'lastslice'} = $cur_image; $fileinfo{'sliceend'} = $cur_slicepos; $fileinfo{'tr'} = &unpack_value(*image_hdr, 194, 'i')/1000000; $fileinfo{'ti'} = &unpack_value(*image_hdr, 198, 'i')/1000000; $fileinfo{'mr_flip'} = &unpack_value(*image_hdr, 254, 's'); $fileinfo{'pseq'} = &unpack_value(*image_hdr, 304, 's'); $fileinfo{'pseqmode'} = &unpack_value(*image_hdr, 306, 's'); $fileinfo{'psdname'} = &unpack_value(*image_hdr, 308, 'a33'); $fileinfo{'psdname'} =~ s/\0.*$//; $fileinfo{'psdname'} =~ s/\n//; } else { if (($cur_width != $fileinfo{'width'}) || ($cur_height != $fileinfo{'height'}) || ($cur_compress != $fileinfo{'compress'})) { warn "Width, height or compression do not match first file ". "for file $nextfile. Skipping to next."; next; } if ($cur_image < $fileinfo{'firstslice'}) { $fileinfo{'firstslice'} = $cur_image; $fileinfo{'slicestart'} = $cur_slicepos; } if ($cur_image > $fileinfo{'lastslice'}) { $fileinfo{'lastslice'} = $cur_image; $fileinfo{'sliceend'} = $cur_slicepos; } } if (!defined($fileinfo{'te', $cur_echo})) { $fileinfo{'te', $cur_echo} = &unpack_value(*image_hdr, 202, 'i')/1000000; } # Keep track of files, image numbers and echo numbers $file_list{$cur_image, $cur_echo} = $nextfile; $echo_list{$cur_echo} = ''; } # Rewind the tape if (length($tapedrive) > 0) { &execute("mt -t $tapedrive rewind"); } &cleanup_and_die; minc-tools-2.3.00+dfsg/conversion/mri_to_minc/dicom_to_minc.pl0000644000175000000620000003523612574624760023457 0ustar stevestaff# Code for reading generic dicom files for conversion to minc. # Does not make use of any shadow groups. # Should read both a stream of dicom groups, or a proper dicom file with # preamble. ######################################################################## # DICOM 3 CONSTANTS # Dicom file offset sub dc3_file_offset {return 128;} # Length of magic and length field sub dc3_preamble_length {return 16;} # Dicom magic sub dc3_magic_string {return "DICM";} # Offset to header length value sub dc3_offset_to_length {return 12;} # Maximum group number needed for header info sub dc3_header_maxid {return "0x0029";} # Exam is unique id of session in scanner (study) sub dc3_exam {return (0x20, 0x10);} # Series is id of scan within a session (acquisition) sub dc3_series {return (0x20, 0x11);} sub dc3_acquisition {return (0x20, 0x12);} # Image is image number sub dc3_image {return (0x20, 0x13);} # Echo number sub dc3_echo {return (0x18, 0x86);} # Width of image sub dc3_width {return (0x28, 0x11);} # Height of image sub dc3_height {return (0x28, 0x10);} # Bits allocated sub dc3_bits_alloc {return (0x28, 0x100);} # Patient name sub dc3_patient_name {return (0x10, 0x10);} # Pixel size sub dc3_pixel_size {return (0x28, 0x30);} # Image position sub dc3_image_position {return (0x20, 0x32);} # Image orientation sub dc3_image_orientation {return (0x20, 0x37);} # Repetition, echo and inversion times in ms, plus flip angle in degrees sub dc3_tr {return (0x18, 0x80);} sub dc3_te {return (0x18, 0x81);} sub dc3_ti {return (0x18, 0x82);} sub dc3_flip {return (0x19, 0x0);} # Patient birthdate, age, sex, id, institution sub dc3_patient_birthdate {return (0x10, 0x30);} sub dc3_patient_age {return (0x10, 0x1010);} sub dc3_patient_sex {return (0x10, 0x40);} sub dc3_patient_id {return (0x10, 0x20);} sub dc3_institution {return (0x8, 0x80);} # Study date and time sub dc3_study_date {return (0x8, 0x22);} sub dc3_study_time {return (0x8, 0x32);} # Compression code sub dc3_compression {return (0x28, 0x60);} # Pixel data location sub dc3_pixel_data {return ("0x7fe0", "0x10");} ######################################################################## # DICOM 3 ROUTINES # Get a list of all dicom element numbers sub acr_get_element_numbers { if (scalar(@_) != 1) { die "Argument error in acr_get_element_numbers"; } local(*header) = @_; local(@keys) = grep(/string$/, keys(%header)); local(@elements) = (); local($key); foreach $key (@keys) { local(@fields) = split($;, $key); local($newkey) = $fields[0] . $; . $fields[1]; push(@elements, $newkey) } @elements = sort(@elements); return @elements; } # Routine to get a string from the header given group and element as strings sub acr_find_string_with_string { if (scalar(@_) != 3) { die "Argument error in acr_find_string_with_string"; } local(*header, $grstr, $elstr) = @_; return $header{$grstr, $elstr, 'string'}; } # Routine to get a string from the header sub acr_find_string { if (scalar(@_) != 3) { die "Argument error in numaris3_find_string"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'string'}; } # Routine to get an array of values from the header sub acr_find_numeric { local(@values) = split(/\\/, &acr_find_string(@_)); foreach $value (@values) { $value += 0; } return (scalar(@values) > 1) ? @values : $values[0]; } # Routine to get an integer from the header sub acr_find_int { if (scalar(@_) != 3) { die "Argument error in numaris3_find_int"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'short'}; } # Routine to convert world coordinates sub convert_coordinates { local(@coords) = @_; $coords[0] *= -1; $coords[1] *= -1; return @coords; } # Routine to compute a dot product sub vector_dot_product { local(*vec1, *vec2) = @_; local($result, $i); $result = 0; for $i (0..2) { $result += $vec1[$i] * $vec2[$i]; } return $result; } # Routine to compute a vector cross product sub vector_cross_product { local(*vec1, *vec2) = @_; local(@result); $#result = 2; $result[0] = $vec1[1] * $vec2[2] - $vec1[2] * $vec2[1]; $result[1] = $vec1[2] * $vec2[0] - $vec1[0] * $vec2[2]; $result[2] = $vec1[0] * $vec2[1] - $vec1[1] * $vec2[0]; return @result; } # Subroutine to read the Dicom 3 file headers sub dicom3_read_headers { # Set constants for reading file local($header_maxid) = &dc3_header_maxid; # Check arguements if (scalar(@_) != 2) { &cleanup_and_die("Argument error in dicom3_read_headers",1); } local($filename, *header) = @_; # Check that the file exists and is readable if (! -r $filename) { warn "Unable to open file \"$filename\""; return 1; } # Figure out how much preamble to skip by looking for the DICOM magic # and reading the first element (length) value local($file_offset) = 0; if (!open(FILE, $filename)) { warn "Unable to open file \"$filename\"\n"; return 1; } local($buffer) = ''; if (!seek(FILE, &dc3_file_offset, 0) || (read(FILE, $buffer, &dc3_preamble_length) != &dc3_preamble_length)) { warn "Error checking file type on file \"$filename\"\n"; return 1; } # Look for the magic string if (substr($buffer, 0, length(&dc3_magic_string)) eq &dc3_magic_string) { local($value) = substr($buffer, &dc3_offset_to_length, 4); $file_offset = unpack("L", $value); if ($file_offset > 65535) { $file_offset = unpack("L", reverse($value)); } # Read all of the meta information to make sure that the offset # is right local($offset) = &dc3_file_offset + length(&dc3_magic_string); local($length) = ($file_offset + 12) * 2; $value = `extract $offset $length $filename | extract_acr_nema -i 2 0`; if (length($value) == 4) { $file_offset = unpack("L", $value); if ($file_offset > 65535) { $file_offset = unpack("L", reverse($value)); } } # Add the other offsets $file_offset += &dc3_file_offset + &dc3_preamble_length; } # Save the file offset $header{'file_offset'} = $file_offset; # Dump the header local($group, $element, $data); open(DUMP, "extract $file_offset -1 $filename | " . "dump_acr_nema -i - $header_maxid|"); while () { chop; if (/^\s*(0x[\da-f]{4,4})\s+(0x[\da-f]{4,4})\s+length = \d+ :(.*)$/) { $group = $1; $element = $2; $data = $3; if ($data =~ /(string|value) = "(.*)"$/) { $header{$group, $element, 'string'} = $2; } if ($data =~ /short = (\d+)/) { $header{$group, $element, 'short'} = $1; } } } close(DUMP); # Check the return status if ($? != 0) { warn "Error dumping header for file $filename"; return 1; } return 0; } # Routine to get Dicom 3 file info sub dicom3_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local(%header); undef(%header); if (&dicom3_read_headers($filename, *header)) { return 1; } # Get interesting values $file_info{'numechos'} = 1; if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} $file_info{'exam'} = &acr_find_string(*header, &dc3_exam); if (length($file_info{'exam'}) == 0) { $file_info{'exam'} = &acr_find_string(*header, &dc3_study_date); } $file_info{'exam'} =~ s/\W//g; local($series) = &acr_find_numeric(*header, &dc3_series); local($acquisition) = &acr_find_numeric(*header, &dc3_acquisition); local($the_series); if (($series > 0) && ($acquisition > 0)) { $the_series = $series*1000+$acquisition; } elsif ($series > 0) {$the_series = $series;} else {$the_series = $acquisition;} $file_info{'series'} = $the_series; local($the_image) = &acr_find_numeric(*header, &dc3_image); if (!defined($the_image) || (length($the_image) == 0)) { if (!defined($Image_Counter)) { # Global variable $Image_Counter = 1; } $the_image = $Image_Counter++; } $file_info{'image'} = $the_image; $file_info{'echo'} = &acr_find_numeric(*header, &dc3_echo); $file_info{'width'} = &acr_find_int(*header, &dc3_width); $file_info{'height'} = &acr_find_int(*header, &dc3_height); local($bits_alloc) = &acr_find_int(*header, &dc3_bits_alloc); if ($bits_alloc == 16) { $file_info{'pixel_size'} = 2; } elsif ($bits_alloc == 8) { $file_info{'pixel_size'} = 1; } else { warn "Wrong number of bits allocated per image ($bits_alloc)\n"; return 1; } $file_info{'patient_name'} = &acr_find_string(*header, &dc3_patient_name); # Get slice position and orientation (row and column vectors) local(@position) = &convert_coordinates(&acr_find_numeric(*header, &dc3_image_position)); if (scalar(@position) != 3) { warn "************** Error reading slice position ***************\n"; } local(@array) = &acr_find_numeric(*header, &dc3_image_orientation); if (scalar(@array) != 6) { warn "************* Error reading slice orientation *************\n"; } local(@column) = &convert_coordinates(@array[0..2]); local(@row) = &convert_coordinates(@array[3..5]); # Figure out normal and orientation local(@normal) = &vector_cross_product(*column, *row); local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local(@col_dircos, @row_dircos, @slc_dircos); ($file_info{'rowstep'}, $file_info{'colstep'}) = &acr_find_numeric(*header, &dc3_pixel_size); if (length($file_info{'rowstep'}) <= 0) { $file_info{'colstep'} = $file_info{'rowstep'} = 1; } $file_info{'colstep'} *= -1.0; $file_info{'rowstep'} *= -1.0; @col_dircos = &get_dircos(@column); @row_dircos = &get_dircos(@row); @slc_dircos = &get_dircos(@normal); $file_info{'slicepos'} = &vector_dot_product(*position, *slc_dircos); $file_info{'colstart'} = &vector_dot_product(*position, *col_dircos) + $file_info{'colstep'} / 2; $file_info{'rowstart'} = &vector_dot_product(*position, *row_dircos) + $file_info{'rowstep'} / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &acr_find_numeric(*header, &dc3_tr)/1000; $file_info{'te'} = &acr_find_numeric(*header, &dc3_te)/1000; $file_info{'ti'} = &acr_find_numeric(*header, &dc3_ti)/1000; $file_info{'mr_flip'} = &acr_find_numeric(*header, &dc3_flip); ($file_info{'patient_birthdate'} = &acr_find_string(*header, &dc3_patient_birthdate)) =~ s/\./-/g; ($file_info{'patient_age'} = &acr_find_string(*header, &dc3_patient_age)) =~ s/\D//g; local($sex_flag) = &acr_find_string(*header, &dc3_patient_sex); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &acr_find_string(*header, &dc3_patient_id); $file_info{'institution'} = &acr_find_string(*header, &dc3_institution); local($study_date, $study_time); ($study_date = &acr_find_string(*header, &dc3_study_date)) =~ s/\./-/g; $study_time = &acr_find_string(*header, &dc3_study_time); $file_info{'start_time'} = "$study_date $study_time"; # Get dicom element info - only even numbered groups local(@elements) = &acr_get_element_numbers(*header); local($element); foreach $element (@elements) { local($group,$element) = split($;, $element); if (hex($group) % 2 != 0) {next;} local($value) = &acr_find_string_with_string(*header, $group, $element); if (defined($value) && length($value) > 0) { $file_info{"dicom_$group:el_$element"} = $value; } } # Get specific file info local($compression_code) = &acr_find_string(*header, 0x28, 0x60); if (($compression_code ne "NONE") && ($compression_code ne "")) { warn "File is compressed\n"; return 1; } ($specific_file_info{'pixel_data_group'}, $specific_file_info{'pixel_data_element'}) = &dc3_pixel_data; $specific_file_info{'file_offset'} = $header{'file_offset'}; $specific_file_info{'pixel_size'} = $file_info{'pixel_size'}; return 0; } sub dicom3_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in dicom3_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract " . $specific_file_info{'file_offset'} . " -1 $cur_file" . " | extract_acr_nema -i " . $specific_file_info{'pixel_data_group'} . " " . $specific_file_info{'pixel_data_element'} . " "; if (!$Image_is_big_endian && $specific_file_info{'pixel_size'} > 1) { $cmd .= " | byte_swap "; } return $cmd; } # Routine to initialize tape drive sub dicom3_initialize_tape_drive { &cleanup_and_die("dicom_to_minc does not support tape reading.\n",1); } # MAIN PROGRAM *initialize_tape_drive = *dicom3_initialize_tape_drive; *read_file_info = *dicom3_read_file_info; *get_image_cmd = *dicom3_get_image_cmd; # Check for dicom-specific arguments $Image_is_big_endian = 0; @args_to_remove = (); foreach $i (0..$#ARGV) { $_ = $ARGV[$i]; if (/^-h(elp)?$/) { warn "Dicom-specific options: -big_endian_image:\tSpecify that the image is big endian "; } elsif (/^-big/ && (index("-big_endian_image",$_)==0)) { $Image_is_big_endian = 1; push(@args_to_remove, $i); } } foreach $i (reverse(@args_to_remove)) { splice(@ARGV, $i, 1); } &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/gedicom_to_minc.pl0000644000175000000620000002073012574624760023764 0ustar stevestaff# Subroutines for GE mri machines, version 6.x dicom # Routine to initialize tape drive sub gedicom_initialize_tape_drive { &cleanup_and_die("gedicom_to_minc does not support tape reading.\n",1); } # Routine to get a string from the header sub acr_find_string { if (scalar(@_) != 3) { die "Argument error in numaris3_find_string"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'string'}; } # Routine to get an array of values from the header sub acr_find_numeric { local(@values) = split(/\\/, &acr_find_string(@_)); foreach $value (@values) { $value += 0; } return (scalar(@values) > 1) ? @values : $values[0]; } # Routine to get an integer from the header sub acr_find_int { if (scalar(@_) != 3) { die "Argument error in numaris3_find_int"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'short'}; } # Routine to convert world coordinates sub convert_coordinates { local(@coords) = @_; $coords[0] *= -1; $coords[1] *= -1; return @coords; } # Routine to compute a dot product sub vector_dot_product { local(*vec1, *vec2) = @_; local($result, $i); $result = 0; for $i (0..2) { $result += $vec1[$i] * $vec2[$i]; } return $result; } # Routine to compute a vector cross product sub vector_cross_product { local(*vec1, *vec2) = @_; local(@result); $#result = 2; $result[0] = $vec1[1] * $vec2[2] - $vec1[2] * $vec2[1]; $result[1] = $vec1[2] * $vec2[0] - $vec1[0] * $vec2[2]; $result[2] = $vec1[0] * $vec2[1] - $vec1[1] * $vec2[0]; return @result; } # Subroutine to read the headers sub gedicom_read_headers { # Set constants for reading file local($header_maxid) = "0x0029"; local($expected_manufacturer) = "GE MEDICAL SYSTEMS"; # Check arguements if (scalar(@_) != 2) { &cleanup_and_die("Argument error in gedicom_read_headers",1); } local($filename, *header) = @_; # Dump the header local($group, $element, $data); open(DUMP, "dump_acr_nema $filename $header_maxid|"); while () { chop; if (/^\s*(0x[\da-f]{4,4})\s+(0x[\da-f]{4,4})\s+length = \d+ :(.*)$/) { $group = $1; $element = $2; $data = $3; if ($data =~ /string = "(.*)"$/) { $header{$group, $element, 'string'} = $1; } if ($data =~ /short = (\d+)/) { $header{$group, $element, 'short'} = $1; } } } close(DUMP); # Check the return status if ($? != 0) { warn "Error dumping header for file $filename"; return 1; } # Check for magic in header local($manufacturer) = &acr_find_string(*header, 0x8, 0x70); if ($manufacturer ne $expected_manufacturer) { warn "Unrecognized manufacturer \"$manufacturer\" - " . "expected \"$expected_manufacturer\"\n"; return 1; } return 0; } # Routine to get Numaris 3 file info sub gedicom_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local(%header); undef(%header); if (&gedicom_read_headers($filename, *header)) { return 1; } # Get interesting values $file_info{'numechos'} = &acr_find_numeric(*header, 0x21, 0x107e); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} ($file_info{'exam'} = &acr_find_string(*header, 0x20, 0x10)) =~ s/[^\w-\.]//g; $file_info{'series'} = &acr_find_numeric(*header, 0x20, 0x11); $file_info{'image'} = &acr_find_numeric(*header, 0x20, 0x13); $file_info{'echo'} = &acr_find_numeric(*header, 0x18, 0x86); $file_info{'width'} = &acr_find_int(*header, 0x28, 0x11); $file_info{'height'} = &acr_find_int(*header, 0x28, 0x10); local($bits_alloc) = &acr_find_int(*header, 0x28, 0x100); if ($bits_alloc != 16) { warn "Wrong number of bits allocated per image ($bits_alloc)\n"; return 1; } $file_info{'pixel_size'} = 2; $file_info{'patient_name'} = &acr_find_string(*header, 0x10, 0x10); # Get slice position and orientation (row and column vectors) local(@position) = &convert_coordinates(&acr_find_numeric(*header, 0x20, 0x32)); if (scalar(@position) != 3) { warn "************** Error reading slice position ***************\n"; } local(@array) = &acr_find_numeric(*header, 0x20, 0x37); if (scalar(@array) != 6) { warn "************* Error reading slice orientation *************\n"; } local(@column) = &convert_coordinates(@array[0..2]); local(@row) = &convert_coordinates(@array[3..5]); # Figure out normal and orientation local(@normal) = &vector_cross_product(*column, *row); local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local(@col_dircos, @row_dircos, @slc_dircos); ($file_info{'rowstep'}, $file_info{'colstep'}) = &acr_find_numeric(*header, 0x28, 0x30); if (length($file_info{'rowstep'}) <= 0) { $file_info{'colstep'} = $file_info{'rowstep'} = 1; } $file_info{'colstep'} *= -1.0; $file_info{'rowstep'} *= -1.0; @col_dircos = &get_dircos(@column); @row_dircos = &get_dircos(@row); @slc_dircos = &get_dircos(@normal); $file_info{'slicepos'} = &vector_dot_product(*position, *slc_dircos); $file_info{'colstart'} = &vector_dot_product(*position, *col_dircos) + $file_info{'colstep'} / 2; $file_info{'rowstart'} = &vector_dot_product(*position, *row_dircos) + $file_info{'rowstep'} / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &acr_find_numeric(*header, 0x18, 0x80)/1000; $file_info{'te'} = &acr_find_numeric(*header, 0x18, 0x81)/1000; $file_info{'ti'} = &acr_find_numeric(*header, 0x18, 0x82)/1000; $file_info{'mr_flip'} = &acr_find_numeric(*header, 0x18, 0x1314); ($file_info{'patient_age'} = &acr_find_string(*header, 0x10, 0x1010)) =~ s/\D//g; local($sex_flag) = &acr_find_string(*header, 0x10, 0x40); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &acr_find_string(*header, 0x10, 0x20); $file_info{'institution'} = &acr_find_string(*header, 0x8, 0x80); local($study_date, $study_time); ($study_date = &acr_find_string(*header, 0x8, 0x22)) =~ s/\./-/g; $study_time = &acr_find_string(*header, 0x8, 0x32); $file_info{'start_time'} = "$study_date $study_time"; # Get specific file info local($compression_code) = &acr_find_string(*header, 0x28, 0x60); if (($compression_code ne "NONE") && ($compression_code ne "")) { warn "File is compressed\n"; return 1; } $specific_file_info{'pixel_data_group'} = "0x7fe0"; $specific_file_info{'pixel_data_element'} = "0x10"; return 0; } # Routine to get return command that will extract image sub gedicom_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in gedicom_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract_acr_nema " . $cur_file . " " . $specific_file_info{'pixel_data_group'} . " " . $specific_file_info{'pixel_data_element'} . " " . " | byte_swap "; return $cmd; } # MAIN PROGRAM *initialize_tape_drive = *gedicom_initialize_tape_drive; *read_file_info = *gedicom_read_file_info; *get_image_cmd = *gedicom_get_image_cmd; &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/dicomfile_to_minc.pl0000644000175000000620000002120212574624760024303 0ustar stevestaff# Special version for files at Johns Hopkins # Dicom files from GE mri, probably via unknown intermediary # Based on subroutines for GE mri machines, version 6.x dicom # Routine to initialize tape drive sub dicomfile_initialize_tape_drive { &cleanup_and_die("dicomfile_to_minc does not support tape reading.\n",1); } # Routine to get a string from the header sub acr_find_string { if (scalar(@_) != 3) { die "Argument error in numaris3_find_string"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'string'}; } # Routine to get an array of values from the header sub acr_find_numeric { local(@values) = split(/\\/, &acr_find_string(@_)); foreach $value (@values) { $value += 0; } return (scalar(@values) > 1) ? @values : $values[0]; } # Routine to get an integer from the header sub acr_find_int { if (scalar(@_) != 3) { die "Argument error in numaris3_find_int"; } local(*header, $group, $element) = @_; local($grstr) = sprintf("0x%04x", $group); local($elstr) = sprintf("0x%04x", $element); return $header{$grstr, $elstr, 'short'}; } # Routine to convert world coordinates sub convert_coordinates { local(@coords) = @_; $coords[0] *= -1; $coords[1] *= -1; return @coords; } # Routine to compute a dot product sub vector_dot_product { local(*vec1, *vec2) = @_; local($result, $i); $result = 0; for $i (0..2) { $result += $vec1[$i] * $vec2[$i]; } return $result; } # Routine to compute a vector cross product sub vector_cross_product { local(*vec1, *vec2) = @_; local(@result); $#result = 2; $result[0] = $vec1[1] * $vec2[2] - $vec1[2] * $vec2[1]; $result[1] = $vec1[2] * $vec2[0] - $vec1[0] * $vec2[2]; $result[2] = $vec1[0] * $vec2[1] - $vec1[1] * $vec2[0]; return @result; } # Subroutine to read the headers sub dicomfile_read_headers { # Set constants for reading file local($header_maxid) = "0x0029"; local($expected_manufacturer) = "GE MEDICAL SYSTEMS"; # Check arguements if (scalar(@_) != 2) { &cleanup_and_die("Argument error in dicomfile_read_headers",1); } local($filename, *header) = @_; # Dump the header local($group, $element, $data); open(DUMP, "extract 132 -1 $filename | dump_acr_nema - $header_maxid|"); while () { chop; if (/^\s*(0x[\da-f]{4,4})\s+(0x[\da-f]{4,4})\s+length = \d+ :(.*)$/) { $group = $1; $element = $2; $data = $3; if ($data =~ /string = "(.*)"$/) { $header{$group, $element, 'string'} = $1; } if ($data =~ /short = (\d+)/) { $header{$group, $element, 'short'} = $1; } } } close(DUMP); # Check the return status if ($? != 0) { warn "Error dumping header for file $filename"; return 1; } # Check for magic in header local($manufacturer) = &acr_find_string(*header, 0x8, 0x70); if ($manufacturer ne $expected_manufacturer) { warn "Unrecognized manufacturer \"$manufacturer\" - " . "expected \"$expected_manufacturer\"\n"; return 1; } return 0; } # Routine to get Numaris 3 file info sub dicomfile_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local(%header); undef(%header); if (&dicomfile_read_headers($filename, *header)) { return 1; } # Get interesting values $file_info{'numechos'} = &acr_find_numeric(*header, 0x21, 0x107e); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} ($file_info{'exam'} = &acr_find_string(*header, 0x20, 0x10)) =~ s/[^\w-\.]//g; $file_info{'series'} = &acr_find_numeric(*header, 0x20, 0x11); $file_info{'image'} = &acr_find_numeric(*header, 0x20, 0x13); $file_info{'echo'} = &acr_find_numeric(*header, 0x18, 0x86); $file_info{'width'} = &acr_find_int(*header, 0x28, 0x11); $file_info{'height'} = &acr_find_int(*header, 0x28, 0x10); local($bits_alloc) = &acr_find_int(*header, 0x28, 0x100); if ($bits_alloc != 16) { warn "Wrong number of bits allocated per image ($bits_alloc)\n"; return 1; } $file_info{'pixel_size'} = 2; $file_info{'patient_name'} = &acr_find_string(*header, 0x10, 0x10); # Get slice position and orientation (row and column vectors) local(@position) = &convert_coordinates(&acr_find_numeric(*header, 0x20, 0x32)); if (scalar(@position) != 3) { warn "************** Error reading slice position ***************\n"; } local(@array) = &acr_find_numeric(*header, 0x20, 0x37); if (scalar(@array) != 6) { warn "************* Error reading slice orientation *************\n"; } local(@column) = &convert_coordinates(@array[0..2]); local(@row) = &convert_coordinates(@array[3..5]); # Figure out normal and orientation local(@normal) = &vector_cross_product(*column, *row); local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local(@col_dircos, @row_dircos, @slc_dircos); ($file_info{'rowstep'}, $file_info{'colstep'}) = &acr_find_numeric(*header, 0x28, 0x30); if (length($file_info{'rowstep'}) <= 0) { $file_info{'colstep'} = $file_info{'rowstep'} = 1; } $file_info{'colstep'} *= -1.0; $file_info{'rowstep'} *= -1.0; @col_dircos = &get_dircos(@column); @row_dircos = &get_dircos(@row); @slc_dircos = &get_dircos(@normal); $file_info{'slicepos'} = &vector_dot_product(*position, *slc_dircos); $file_info{'colstart'} = &vector_dot_product(*position, *col_dircos) + $file_info{'colstep'} / 2; $file_info{'rowstart'} = &vector_dot_product(*position, *row_dircos) + $file_info{'rowstep'} / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &acr_find_numeric(*header, 0x18, 0x80)/1000; $file_info{'te'} = &acr_find_numeric(*header, 0x18, 0x81)/1000; $file_info{'ti'} = &acr_find_numeric(*header, 0x18, 0x82)/1000; $file_info{'mr_flip'} = &acr_find_numeric(*header, 0x18, 0x1314); ($file_info{'patient_age'} = &acr_find_string(*header, 0x10, 0x1010)) =~ s/\D//g; local($sex_flag) = &acr_find_string(*header, 0x10, 0x40); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &acr_find_string(*header, 0x10, 0x20); $file_info{'institution'} = &acr_find_string(*header, 0x8, 0x80); local($study_date, $study_time); ($study_date = &acr_find_string(*header, 0x8, 0x22)) =~ s/\./-/g; $study_time = &acr_find_string(*header, 0x8, 0x32); $file_info{'start_time'} = "$study_date $study_time"; # Get specific file info local($compression_code) = &acr_find_string(*header, 0x28, 0x60); if (($compression_code ne "NONE") && ($compression_code ne "")) { warn "File is compressed\n"; return 1; } $specific_file_info{'pixel_data_group'} = "0x7fe0"; $specific_file_info{'pixel_data_element'} = "0x10"; return 0; } # Routine to get return command that will extract image sub dicomfile_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in dicomfile_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract 132 -1 $cur_file | " . "extract_acr_nema " . $specific_file_info{'pixel_data_group'} . " " . $specific_file_info{'pixel_data_element'} . " " . " | byte_swap "; return $cmd; } # MAIN PROGRAM *initialize_tape_drive = *dicomfile_initialize_tape_drive; *read_file_info = *dicomfile_read_file_info; *get_image_cmd = *dicomfile_get_image_cmd; &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/ge4_to_minc.pl0000644000175000000620000001432712574624760023041 0ustar stevestaff# Subroutines for GE mri machines, version 4.x # Routine to initialize tape drive sub ge4_initialize_tape_drive { &cleanup_and_die("ge4_to_minc does not support tape reading.\n",1); } # Routine to convert data general float to ieee sub convert_dg_float_to_ieee { local($packed_float, $ival, $exponent, $mantissa, @result, $int_result); @result = (); foreach $packed_float (@_) { $ival = unpack('L',$packed_float); if ($ival == 0.0) { push(@result, 0.0); } else { $exponent = (((($ival >> 24) & 0x7f) - 64) * 4) + 127; $mantissa = $ival & 0x00ffffff; while ($mantissa && (!( $mantissa & 0x00800000))) { $exponent--; $mantissa = $mantissa << 1; } $exponent--; $exponent = $exponent << 23; $mantissa &= 0x007fffff; $int_result = ($ival & 0x80000000) | $exponent | $mantissa; push(@result, unpack('f',pack('L', $int_result))); } } return (scalar(@result) > 1) ? @result : $result[0]; } # Subroutine to unpack a data general float from a string sub unpack_float { local(*string, $offset) = @_; if ($dump_unpack_value) { print "unpack_value: length, offset = ",length($string), " $offset\n"; } return &convert_dg_float_to_ieee(substr($string, $offset, 4)); } # Subroutine to read the ge 4.x file headers sub ge4_read_headers { # Set constants for reading file local($suite_hdr_len) = 3072; local($exam_hdr_len) = 1024; local($series_hdr_len) = 1024; local($image_hdr_len) = 1024; local($pixel_hdr_prefix_len) = 2048; # Check arguments if (scalar(@_) != 8) { &cleanup_and_die("Argument error in ge4_read_headers",1); } local($filename, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len) = @_; # Read in file headers if (!open(GEF, "<".$filename)) { warn "Can't open file $filename: $!"; return 1; } return 1 if (read(GEF, $suite_hdr, $suite_hdr_len) != $suite_hdr_len); return 1 if (read(GEF, $exam_hdr, $exam_hdr_len) != $exam_hdr_len); return 1 if (read(GEF, $series_hdr, $series_hdr_len) != $series_hdr_len); return 1 if (read(GEF, $image_hdr, $image_hdr_len) != $image_hdr_len); return 1 if (read(GEF, $pixel_hdr, $pixel_hdr_prefix_len) != $pixel_hdr_prefix_len); # Calculate the offset to pixel data $pixel_data_offset = 14336; # Use -1 for pixel data length to get to end of file $pixel_data_len = -1; # Close input file close(GEF); return 0; } # Routine to get file info sub ge4_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Get headers local($suite_hdr, $exam_hdr, $series_hdr, $image_hdr, $pixel_hdr, $pixel_data_offset, $pixel_data_len); if (&ge4_read_headers($filename, *suite_hdr, *exam_hdr, *series_hdr, *image_hdr, *pixel_hdr, *pixel_data_offset, *pixel_data_len)) { return 1; } # Get interesting values local($fovx, $fovy); $file_info{'numechos'} = &unpack_value(*image_hdr, 196, 's'); if ($file_info{'numechos'} <= 0) {$file_info{'numechos'} = 1;} $file_info{'exam'} = &unpack_value(*exam_hdr, 64, 'A6')+0; $file_info{'series'} = &unpack_value(*image_hdr, 92, 'A4')+0; $file_info{'image'} = &unpack_value(*image_hdr, 88, 'A4')+0; $file_info{'echo'} = &unpack_value(*image_hdr, 198, 's'); $file_info{'width'} = &unpack_value(*image_hdr, 274, 's'); $file_info{'height'} = &unpack_value(*image_hdr, 276, 's'); $file_info{'pixel_size'} = 2; $file_info{'slicepos'} = &unpack_float(*image_hdr, 146); $file_info{'patient_name'} = &unpack_value(*exam_hdr, 108, "A25"); local($orient_flag) = &unpack_value(*series_hdr, 276, 's'); if ($orient_flag == 1) { # Sagittal $file_info{'orientation'} = 'sagittal'; } elsif ($orient_flag == 2) { # Coronal $file_info{'orientation'} = 'coronal'; } else { # Transverse $file_info{'orientation'} = 'transverse'; } $fovx = &unpack_float(*series_hdr, 302); $fovy = $fovx; $file_info{'colstep'} = -$fovx / $file_info{'width'}; $file_info{'rowstep'} = -(($fovy != 0) ? $fovy : $fovx) / $file_info{'height'}; $file_info{'tr'} = &unpack_float(*image_hdr, 164)/1000000; $file_info{'te'} = &unpack_float(*image_hdr, 172)/1000000; $file_info{'ti'} = &unpack_float(*image_hdr, 176)/1000000; $file_info{'mr_flip'} = &unpack_value(*image_hdr, 350, 's'); $file_info{'scan_seq'} = &unpack_value(*series_hdr, 410, 'A12'); $file_info{'scan_seq'} =~ s/\0.*$//; $file_info{'scan_seq'} =~ s/\n//; $file_info{'ge_pseq'} = &unpack_value(*series_hdr, 298, 's'); $file_info{'ge_pseqmode'} = &unpack_value(*series_hdr, 408, 's'); # Get GE specific stuff $specific_file_info{'pixel_data_offset'} = $pixel_data_offset; $specific_file_info{'pixel_data_len'} = $pixel_data_len; local($compress) = &unpack_value(*image_hdr, 282, 's'); $specific_file_info{'compress'} = $compress; if ($compress != 0) { warn "Unusable compression scheme ($compress)."; return 1; } local($depth) = &unpack_value(*image_hdr, 284, 's'); if ($depth <= 0) {$depth = 16;} $specific_file_info{'depth'} = $depth; if ($depth != 16) { warn "Unusable pixel depth ($depth)."; return 1; } return 0; } sub ge4_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in ge4_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract " . $specific_file_info{'pixel_data_offset'} . " " . $specific_file_info{'pixel_data_len'} . " " . $cur_file; return $cmd; } # MAIN PROGRAM *initialize_tape_drive = *ge4_initialize_tape_drive; *read_file_info = *ge4_read_file_info; *get_image_cmd = *ge4_get_image_cmd; &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/siemens_magnetom_vision_to_minc.pl0000644000175000000620000001403512574624760027277 0ustar stevestaff# Subroutines for Siemens Magnetom Vision mri machines, internal format. # # Some things to note: I couldn't find any fields giving the image # dimensions, so I assume 256x256. Nor could I find anything about offset # to image data, so I assume that it runs to the end of the file (the header # usually is fixed length, but not always). # Routine to convert Siemens coordinates sub convert_coordinates { local(@coords) = @_; $coords[0] *= -1; $coords[2] *= -1; return @coords; } # Routine to initialize tape drive sub smv_initialize_tape_drive { &cleanup_and_die("Tape reading is not supported.\n",1); } sub smv_read_header { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in smv_read_header",1); } local($filename, *header) = @_; # Constants local($header_length) = 6144; # Open file if (!open(SMV, "<".$filename)) { warn "Can't open file $filename: $!\n"; return 1; } # Read in the header if (read(SMV, $header, $header_length) != $header_length) { return 1; } # Close the file close(SMV); return 0; } sub smv_read_file_info { if (scalar(@_) != 3) { &cleanup_and_die("Argument error in read_file_info",1); } local($filename, *file_info, *specific_file_info) = @_; # Constant local($imageoffset) = 6144; # Get header local($header); if (&smv_read_header($filename, *header)) { return 1; } # Get image size local($pixelsize) = 2; local($imagewidth) = int(sqrt(((-s $filename) - $imageoffset) / $pixelsize)); local($imagesize) = $imagewidth * $imagewidth * $pixelsize; # Get interesting values $file_info{'numechos'} = 100; # We cheat and use study date for exam, getting rid of weird characters $file_info{'exam'} = sprintf("%04d%02d%02d_%02d%02d%02d", &unpack_value(*header, 12, 'I3'), &unpack_value(*header, 52, 'I3')); $file_info{'series'} = &unpack_value(*header, 5943, 'A2')+0; $file_info{'image'} = &unpack_value(*header, 5546, 'A4')+0; $file_info{'echo'} = &unpack_value(*header, 5752, 'A1')+0; $file_info{'pixel_size'} = $pixelsize; $file_info{'width'} = $imagewidth; $file_info{'height'} = $file_info{'width'}; $file_info{'patient_name'} = &unpack_value(*header, 768, 'A25'); local(@normal) = &convert_coordinates(&unpack_value(*header, 3792, 'd3')); if (scalar(@normal) != 3) { @normal = (0,0,1); } local($norm_r) = &abs($normal[0]); local($norm_a) = &abs($normal[1]); local($norm_s) = &abs($normal[2]); local($plane) = 'transverse'; local($max) = $norm_s; if ($norm_r > $max) { $plane = 'sagittal'; $max = $norm_r; } if ($norm_a > $max) { $plane = 'coronal'; $max = $norm_a; } $file_info{'orientation'} = $plane; # Get coordinate information local(@col_dircos, @row_dircos, @slc_dircos); ($file_info{'rowstep'}, $file_info{'colstep'}) = &unpack_value(*header, 5000, 'd2'); if (length($file_info{'rowstep'}) <= 0) { $file_info{'colstep'} = $file_info{'rowstep'} = 1; } $file_info{'colstep'} *= -1.0; $file_info{'rowstep'} *= -1.0; @col_dircos = &get_dircos (&convert_coordinates(&unpack_value(*header, 3832, 'd3'))); @row_dircos = &get_dircos (&convert_coordinates(&unpack_value(*header, 3856, 'd3'))); @slc_dircos = &get_dircos(@normal); local($xcentre, $ycentre, $zcentre) = &convert_coordinates(&unpack_value(*header, 3768, 'd3')); $file_info{'slicepos'} = $xcentre * $slc_dircos[0] + $ycentre * $slc_dircos[1] + $zcentre * $slc_dircos[2]; $file_info{'colstart'} = ($xcentre * $col_dircos[0] + $ycentre * $col_dircos[1] + $zcentre * $col_dircos[2]) - $file_info{'colstep'} * ($file_info{'width'} - 1) / 2; $file_info{'rowstart'} = ($xcentre * $row_dircos[0] + $ycentre * $row_dircos[1] + $zcentre * $row_dircos[2]) - $file_info{'rowstep'} * ($file_info{'height'} - 1) / 2; $file_info{'col_dircos_x'} = $col_dircos[0]; $file_info{'col_dircos_y'} = $col_dircos[1]; $file_info{'col_dircos_z'} = $col_dircos[2]; $file_info{'row_dircos_x'} = $row_dircos[0]; $file_info{'row_dircos_y'} = $row_dircos[1]; $file_info{'row_dircos_z'} = $row_dircos[2]; $file_info{'slc_dircos_x'} = $slc_dircos[0]; $file_info{'slc_dircos_y'} = $slc_dircos[1]; $file_info{'slc_dircos_z'} = $slc_dircos[2]; # Get other info $file_info{'tr'} = &unpack_value(*header, 5734, 'A7') / 1000; $file_info{'te'} = &unpack_value(*header, 5746, 'A5') / 1000; $file_info{'ti'} = undef; $file_info{'mr_flip'} = &unpack_value(*header, 5714, 'A3'); $file_info{'patient_birthdate'} = sprintf("%04d-%02d-%02d", &unpack_value(*header, 808, 'I3')); local($sex_flag) = &unpack_value(*header, 5517, 'A1'); if ($sex_flag eq 'M ') { $file_info{'patient_sex'} = "male__"; } elsif ($sex_flag eq 'F ') { $file_info{'patient_sex'} = "female"; } $file_info{'patient_id'} = &unpack_value(*header, 795, 'A12'); $file_info{'institution'} = &unpack_value(*header, 105, 'A25'); $file_info{'start_time'} = sprintf("%04d-%02d-%02d %02d:%02d:%02d", &unpack_value(*header, 12, 'I3'), &unpack_value(*header, 52, 'I3')); # Get specific file info $specific_file_info{'pixel_data_offset'} = $imageoffset; $specific_file_info{'pixel_data_len'} = $file_info{'width'} * $file_info{'height'} * $file_info{'pixel_size'}; return 0; } sub smv_get_image_cmd { if (scalar(@_) != 2) { &cleanup_and_die("Argument error in smv_get_image_cmd",1); } local($cur_file, *specific_file_info) = @_; local($cmd) = "extract " . $specific_file_info{'pixel_data_offset'} . " " . $specific_file_info{'pixel_data_len'} . " " . $cur_file; return $cmd; } *initialize_tape_drive = *smv_initialize_tape_drive; *read_file_info = *smv_read_file_info; *get_image_cmd = *smv_get_image_cmd; &mri_to_minc(@ARGV); minc-tools-2.3.00+dfsg/conversion/mri_to_minc/dgtoieee.pl0000755000175000000620000000203612574624760022434 0ustar stevestaff#! /usr/local/bin/perl # Routine to convert data general float to ieee sub convert_dg_float_to_ieee { local($packed_float, $ival, $exponent, $mantissa, @result, $int_result); @result = (); foreach $packed_float (@_) { $ival = unpack('L',$packed_float); if ($ival == 0.0) { push(@result, 0.0); } else { $exponent = (((($ival >> 24) & 0x7f) - 64) * 4) + 127; $mantissa = $ival & 0x00ffffff; while ($mantissa && (!( $mantissa & 0x00800000))) { $exponent--; $mantissa = $mantissa << 1; } $exponent--; $exponent = $exponent << 23; $mantissa &= 0x007fffff; $int_result = ($ival & 0x80000000) | $exponent | $mantissa; push(@result, unpack('f',pack('L', $int_result))); } } return @result; } foreach $arg (@ARGV) { $arg = pack('f', $arg); } print join("\n",&convert_dg_float_to_ieee(@ARGV)),"\n"; minc-tools-2.3.00+dfsg/conversion/mri_to_minc/Makefile0000644000175000000620000000260012574624760021744 0ustar stevestaff# -------------------------------------------------------------------- # # Generic Makefile # PROG = ge_uncompress # executable name PROG_OBJ = ge_uncompress.o # files to compile OPT = -O # compiler options LDOPT = -L/usr/local/lib # ld options INCLUDES = -I/usr/local/include -I. # include directories PERL_PROGS = ge4_to_minc ge5_to_minc siemens_to_minc dicom_to_minc \ siemens_magnetom_vision_to_minc gedicom_to_minc dicomfile_to_minc PERL_INCLUDE = mri_to_minc.pl # -------------------------------------------------------------------- .SUFFIXES: .ln .pl CFLAGS = $(INCLUDES) $(OPT) # CFLAGS and LINTFLAGS should LINTFLAGS = $(INCLUDES) # be same, except for -g/-O default : $(PROG) $(PERL_PROGS) all : $(PROG) lint_$(PROG) $(PERL_PROGS) .c.ln: # defines the rule for creating .ln lint $(LINTFLAGS) -c $< -o $@ .c.o: # defines the rule for creating .o $(CC) $(CFLAGS) -c $< -o $@ .pl: cat $(PERL_INCLUDE) $@.pl > $@; chmod +x $@ LINT_LIST = $(PROG_OBJ:.o=.ln) # list of lint files in program $(PROG_OBJ) : Makefile # How to create executable $(PROG): $(PROG_OBJ) $(CC) $(PROG_OBJ) -o $@ $(LDOPT) # how to lint the executable source lint_$(PROG): $(LINT_LIST) lint -u $(LINTFLAGS) $(LINT_LIST) # How to create perl programs from multiple source files $(PERL_PROGS): $(PERL_INCLUDE) Makefile clean: \rm -f $(PROG) $(PROG_OBJ) $(LINT_LIST) $(PERL_PROGS) minc-tools-2.3.00+dfsg/conversion/dcm2mnc/0002755000175000000620000000000012574624760017334 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/dcm2mnc/dicom_read.c0000644000175000000620000025627612574624760021606 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_read.c @DESCRIPTION: Code to read siemens dicom files and get info from them. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_read.c,v $ * Revision 1.29 2010-11-23 23:30:50 claude * dcm2mnc: fixed seg fault bug (Claude) and added b-matrix (Ilana) * * Revision 1.28 2008-08-12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 1.27 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 1.26 2008/01/11 07:17:07 stever * Remove unused variables. * * Revision 1.25 2007/12/09 19:52:15 alex * Fixed error in echo_time extraction * * Revision 1.24 2007/06/08 20:28:57 ilana * added several fields to mincheader (dicom elements and found in ASCONV header) * * Revision 1.23 2007/05/30 15:17:34 ilana * fix so that diffusion images all written into 1 4d volume, gradient directions and bvalues are written to mincheader, some fixes for TIM diffusion images * * Revision 1.22 2006/04/09 15:38:02 bert * Add support for standard DTI fields * * Revision 1.21 2005/12/05 16:50:08 bert * Deal with weird XMedCon images * * Revision 1.20 2005/10/26 23:43:35 bert * Latest attempt at getting slice spacing consistent * * Revision 1.19 2005/09/16 22:13:33 bert * Fix slice spacing handling * * Revision 1.18 2005/08/26 21:25:54 bert * Latest changes ported from 1.0 branch * * Revision 1.16.2.5 2005/08/26 03:50:16 bert * Use ACR_Number_of_temporal_positions for number of time slices * * Revision 1.16.2.4 2005/08/18 16:38:43 bert * Minor updates for dealing w/older numaris data * * Revision 1.16.2.3 2005/06/20 22:03:01 bert * Add functions for traversing DICOM sequences and recursively hunting for needed fields. * * Revision 1.16.2.2 2005/06/02 17:04:26 bert * 1. Fix handling of signed values for pixel minimum and maximum, 2. Set found_dircos[] when we adopt the default direction cosines for files with null direction cosines * * Revision 1.16.2.1 2005/05/12 21:16:47 bert * Initial checkin * * Revision 1.16 2005/05/09 15:30:32 bert * Don't allow a rescale slope value of zero * * Revision 1.15 2005/04/28 17:17:57 bert * Set and update new width information fields in a manner analogous to the coordinate fields in the General_Info and File_Info structures * * Revision 1.14 2005/04/20 23:14:04 bert * Remove most SPI_ references * * Revision 1.13 2005/04/20 17:47:38 bert * Fairly major restructuring, added init_general_info() function * * Revision 1.12 2005/04/18 21:43:04 bert * Properly set default minimum and maximum values based on the pixel representation * * Revision 1.11 2005/04/18 21:01:51 bert * Set signed/unsigned flag correctly * * Revision 1.10 2005/04/18 20:43:25 bert * Added some additional debugging information for image position and orientation * * Revision 1.9 2005/04/18 16:22:13 bert * Don't allow non-slice MRI dimensions to grow arbitrarily * * Revision 1.8 2005/04/05 21:49:52 bert * Use rescale slope and intercept to determine the proper slice minimum and maximum * * Revision 1.7 2005/03/29 20:21:44 bert * Fix use of slice spacing; fully check for position information if possible, otherwise create a reasonable position from the slice index * * Revision 1.6 2005/03/14 23:29:35 bert * Support basic dynamic PET fields. Also allocate indices and coordinates arrays for all dimensions, even those we won't use. * * Revision 1.5 2005/03/13 19:37:42 bert * Try to use slice location for coordinate when all else fails, also added one debugging message and a check for PET modality * * Revision 1.4 2005/03/03 20:11:00 bert * Consider patient_id and patient_name when sorting into series. Fix handling of missing direction cosines * * Revision 1.3 2005/03/03 18:59:15 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.2 2005/03/02 20:18:09 bert * Latest fixes and tweaks * * Revision 1.1 2005/02/17 16:38:10 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.12 2002/05/01 21:29:34 rhoge * removed MrProt from minc header - encountered files with large strings, * causing seg faults * * Revision 1.11 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.10 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.9 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.8 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.7 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.6 2000/12/14 21:33:13 rhoge * code modifications to restore measurement loop support that was broken * by changes to support acqusition loop scanning * * Revision 1.5 2000/12/13 13:25:36 rhoge * removed dummy printf from convert_time_to_seconds - turns out that * buggy behaviour was an optimization problem * * Revision 1.4 2000/12/12 19:27:52 rhoge * changed atof(acr_find_string) back to acr_find_double after realizing * that these functions assume string representation * * Revision 1.3 2000/12/12 14:43:22 rhoge * fixed syntax error (dangling comment symbol) from removal of debug * printfs - compiles now * * Revision 1.2 2000/12/11 20:01:44 rhoge * fixed code for frame time computation - ACR vals are strings. Also * inserted dummy fprintf statement in convert_time_to_seconds as this * seems to salvage Linux problems * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * -now always use ACR_Series for run number (seemed to be * problems with test introduced in 6.1) * -added code to detect use of acquisition loop and also to handle * `corrected' siemens acq loop scans * -changed code to use registration time instead of scan time for * session id * -got rid of extraneous acquisition id digit * -added technical information about data acquisition * * Revision 6.2 1999/10/29 17:51:58 neelin * Fixed Log keyword * * Revision 6.1 1999/08/05 20:00:34 neelin * Get acquisition id from series or study element, depending on the * version of the Siemens software. * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/10 19:36:13 neelin * Small fix to set default direction cosines when they are absent from the * dicom data. * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.1 1997/06/13 12:51:21 neelin * Changed definition of time index and acquisition id to match change * in Siemens dicom software. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 "dcm2mnc.h" #define IMAGE_NDIMS 2 static double convert_time_to_seconds(double dicom_time); static void get_intensity_info(Acr_Group group_list, File_Info *file_info); static void get_coordinate_info(Acr_Group group_list, File_Info *file_info, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], const int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]); static void get_general_header_info(Acr_Group group_list, General_Info *general_info); //static void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]); static void get_identification_info(Acr_Group group_list, double *study_id, int *acq_id, int *rec_num, int *image_type); static int irnd(double x) { if (x > 0.0) { x += 0.5; } else { x -= 0.5; } return (int) floor(x); } int is_numaris3(Acr_Group group_list) { return (strstr(acr_find_string(group_list, ACR_Manufacturer, ""), "SIEMENS") != NULL && strstr(acr_find_string(group_list, ACR_Software_versions, ""), "VB33") != NULL); } int get_subimage_count(const Acr_Group group_list) { int result = acr_find_int(group_list, ACR_Number_of_frames, 0); if (result <= 0) { result = acr_find_int(group_list, EXT_Slices_in_file, 1); } return result; } /* ----------------------------- MNI Header ----------------------------------- @NAME : init_general_info @INPUT : fi_ptr - file-specific info group_list - input data volume_to_world - correspondence of volume to world dimensions spatial_sizes - 3D voxel counts dircos - direction cosines steps - width of each voxel for each spatial dimension starts - starting position for each spatial dimension coordinate - @OUTPUT : gi_ptr - general information about files in this series @RETURNS : (nothing) @DESCRIPTION: Initializes a "General_Info" structure based upon several bits of information, including a previously initialized File_Info structure (fi_ptr) and the DICOM group_list. Broken out from the get_file_info() function to help simplify that function. @METHOD : @GLOBALS : @CALLS : @CREATED : April 19, 2005 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ static void init_general_info(General_Info *gi_ptr, /* OUT */ const File_Info *fi_ptr, /* IN */ const Acr_Group group_list, /* IN */ const World_Index volume_to_world[VOL_NDIMS], /* IN */ const int spatial_sizes[VOL_NDIMS], /* IN */ double dircos[VOL_NDIMS][WORLD_NDIMS], /* IN */ const double steps[VOL_NDIMS], /* IN */ const double starts[VOL_NDIMS], /* IN */ double study_id, /* IN */ int acq_id, /* IN */ int rec_num) /* IN */ { int ivalue; /* For pixel representation value. */ World_Index iworld; /* World coordinate index (XCOORD, YCOORD...) */ World_Index jworld; /* World coordinate index */ Volume_Index ivolume; /* Voxel coordinate index (VROW, VCOLUMN...) */ // Get row and columns sizes gi_ptr->nrows = spatial_sizes[VROW]; gi_ptr->ncolumns = spatial_sizes[VCOLUMN]; // Save the study, acquisition, reconstruction and image type // identifiers gi_ptr->study_id = study_id; gi_ptr->acq_id = acq_id; gi_ptr->rec_num = rec_num; strcpy(gi_ptr->image_type_string, acr_find_string(group_list, ACR_Image_type, "")); /* Get spatial coordinate information */ gi_ptr->slice_world = volume_to_world[VSLICE]; gi_ptr->row_world = volume_to_world[VROW]; gi_ptr->column_world = volume_to_world[VCOLUMN]; for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; gi_ptr->step[iworld] = steps[ivolume]; gi_ptr->start[iworld] = starts[ivolume]; for (jworld = 0; jworld < WORLD_NDIMS; jworld++) { gi_ptr->dircos[iworld][jworld] = dircos[ivolume][jworld]; } if (G.Debug) { printf("%2d. %s axis length %d step %.3f, start %.3f, cosines %.3f,%.3f,%.3f\n", ivolume, World_Names[iworld], spatial_sizes[ivolume], gi_ptr->step[iworld], gi_ptr->start[iworld], gi_ptr->dircos[iworld][XCOORD], gi_ptr->dircos[iworld][YCOORD], gi_ptr->dircos[iworld][ZCOORD] ); } } /* Set data type and range */ if (fi_ptr->bits_alloc <= 8) { gi_ptr->datatype = NC_BYTE; } else { gi_ptr->datatype = NC_SHORT; } /* bert- modify code to correctly read the pixel * representation if available and use that to set the * signed/unsigned flag. */ ivalue = acr_find_int(group_list, ACR_Pixel_representation, -1); if (ivalue == ACR_PIXEL_REP_UNSIGNED) { gi_ptr->is_signed = 0; } else if (ivalue == ACR_PIXEL_REP_SIGNED) { gi_ptr->is_signed = 1; } else { if (ivalue != -1) { printf("WARNING: Unknown pixel representation value %d\n", ivalue); } gi_ptr->is_signed = ((gi_ptr->datatype == NC_SHORT) && (fi_ptr->bits_stored < 16)); } gi_ptr->pixel_min = fi_ptr->pixel_min; gi_ptr->pixel_max = fi_ptr->pixel_max; /* Save display window info */ gi_ptr->window_min = fi_ptr->window_min; gi_ptr->window_max = fi_ptr->window_max; /* Get the rest of the header information */ get_general_header_info(group_list, gi_ptr); /* Copy the group list */ gi_ptr->group_list = acr_copy_group_list(group_list); // note that number of slices will be wrong here for mosaics // we add some other mosaic-relevant fields... gi_ptr->num_mosaic_rows = acr_find_int(group_list, EXT_Mosaic_rows, 1); gi_ptr->num_mosaic_cols = acr_find_int(group_list, EXT_Mosaic_columns, 1); gi_ptr->num_slices_in_file = acr_find_int(group_list, EXT_Slices_in_file, 1); gi_ptr->sub_image_rows = (int)acr_find_short(group_list, EXT_Sub_image_rows, acr_find_short(group_list, ACR_Rows, 0)); gi_ptr->sub_image_columns = (int)acr_find_short(group_list, EXT_Sub_image_columns, acr_find_short(group_list, ACR_Rows, 0)); /* Set initialized flag */ gi_ptr->initialized = TRUE; if (G.Debug) { printf("Pixel minimum %.10f maximum %.10f\n", gi_ptr->pixel_min, gi_ptr->pixel_max); printf("Window minimum %.10f maximum %.10f\n", gi_ptr->window_min, gi_ptr->window_max); } } void get_axis_lengths(const Acr_Group group_list, General_Info *gi_ptr, const File_Info *fi_ptr) { Acr_Element_Id mri_total_list[MRI_NDIMS]; int imri; int index; // Initialize array for MRI dimension lengths // mri_total_list[SLICE] = ACR_Images_in_acquisition; mri_total_list[ECHO] = ACR_Echo_train_length; mri_total_list[TIME] = ACR_Acquisitions_in_series; mri_total_list[PHASE] = NULL; mri_total_list[CHEM_SHIFT] = NULL; /* Get dimension information */ for (imri = 0; imri < MRI_NDIMS; imri++) { /* Get sizes along "MRI" dimensions... */ gi_ptr->cur_size[imri] = 1; if (mri_total_list[imri] != NULL) { int def_val = 1; if (imri == SLICE) { /* Look for the standard slice count fields first. We * start with the (0054, 0081) first, and if that fails * we retry with (0020, 1002). */ def_val = acr_find_int(group_list, ACR_Number_of_slices, 0); /* If we have a multiframe image (MOSAIC), use the number of * frames. This may not be present in some MOSAIC files. */ if (def_val == 0) { def_val = acr_find_int(group_list, ACR_Number_of_frames, 0); } if (def_val == 0) { def_val = acr_find_int(group_list, mri_total_list[imri], 0); } gi_ptr->max_size[imri] = def_val; } else if (imri == TIME) { /* Look for the official time slice count field first. */ def_val = acr_find_int(group_list, ACR_Number_of_temporal_positions, -1); if (G.Debug > 1) printf("(0021,0105)=%d\n", def_val); /* Now look in the equally official but different field. */ if (def_val < 0) { def_val = acr_find_int(group_list, ACR_Number_of_time_slices, def_val); if (G.Debug > 1) printf("(0054,0101)=%d\n", def_val); } if (def_val < 0) { /* Sometimes need to look further for time count (ex:segmented FLASH) */ def_val = acr_find_int(group_list, ACR_Cardiac_number_of_images, def_val); if (G.Debug > 1) printf("(0018,1090)=%d\n", def_val); } if (def_val <= 0) { def_val = acr_find_int(group_list, mri_total_list[imri], def_val); } if (def_val < 0) { /* As our last resort, we try to determine this by seeing how many files we have, and dividing by the number of slices. */ if (gi_ptr->max_size[SLICE] > 1) { int num_per_file = get_subimage_count(group_list); int num_images = gi_ptr->num_files * num_per_file; printf("%d images (%d, %d)\n", num_images, gi_ptr->num_files, num_per_file); if ((num_images % gi_ptr->max_size[SLICE]) == 0) { def_val = num_images / gi_ptr->max_size[SLICE]; } } } gi_ptr->max_size[imri] = def_val; } else { gi_ptr->max_size[imri] = acr_find_int(group_list, mri_total_list[imri], def_val); } } else { gi_ptr->max_size[imri] = 0; } /* We alway specify a maximum size of one for an unknown dimension, * but we want to keep track of those for which no valid value * was actually discovered (bert). */ if (gi_ptr->max_size[imri] < 1) { gi_ptr->max_size[imri] = 1; gi_ptr->size_isset[imri] = 0; } else { gi_ptr->size_isset[imri] = 1; } /* Check for 3D partitions for slice dimensions */ if (imri == SLICE) { /* Get number of 3D partitions for working out number of * slices */ int number_of_3D_partitions = acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 1); if (number_of_3D_partitions < 1) { number_of_3D_partitions = 1; } /* Do this only if there is a plausible reason to think we need it. */ if (gi_ptr->size_isset[imri]) { gi_ptr->max_size[imri] *= number_of_3D_partitions; } } gi_ptr->image_index[imri] = -1; /* Allocate space for index and coordinate arrays. * Set the first values. */ gi_ptr->indices[imri] = malloc(gi_ptr->max_size[imri] * sizeof(int)); gi_ptr->coordinates[imri] = malloc(gi_ptr->max_size[imri] * sizeof(double)); gi_ptr->widths[imri] = malloc(gi_ptr->max_size[imri] * sizeof(double)); for (index = 0; index < gi_ptr->max_size[imri]; index++) { gi_ptr->indices[imri][index] = -1; gi_ptr->coordinates[imri][index] = 0; gi_ptr->widths[imri][index] = 0; /* default */ } gi_ptr->search_start[imri] = 0; if (G.Debug) { printf("%2d. %s axis length %d\n", imri, Mri_Names[imri], gi_ptr->max_size[imri]); } } /* Loop over dimensions */ } void get_file_indices(const Acr_Group group_list, const General_Info *gi_ptr, File_Info *fi_ptr) { Acr_Element_Id mri_index_list[MRI_NDIMS]; int imri; // Array of elements for mri dimensions mri_index_list[SLICE] = SPI_Current_slice_number; mri_index_list[ECHO] = ACR_Echo_number; // dicom time element may be different on old systems mri_index_list[TIME] = ACR_Temporal_position_identifier; mri_index_list[PHASE] = NULL; mri_index_list[CHEM_SHIFT] = NULL; /* Get indices for image in current file */ for (imri = 0; imri < MRI_NDIMS; imri++) { if (mri_index_list[imri] != NULL) { int tmp_index = acr_find_int(group_list, mri_index_list[imri], -1); if (imri == SLICE) { if (tmp_index < 0) { /* Non-MOSAIC only? */ tmp_index = acr_find_int(group_list, ACR_Image, -1); } if (tmp_index < 0) { tmp_index = acr_find_int(group_list, ACR_PET_Image_index, -1); } /* Perform this range check only if absolutely needed. */ if (tmp_index >= 1 && gi_ptr->max_size[SLICE] > 1 && gi_ptr->max_size[TIME] > 1) { tmp_index = ((tmp_index - 1) % gi_ptr->max_size[SLICE]) + 1; } } /* Use alternative index fields for time, if needed. */ if (imri == TIME && tmp_index < 0) { int range_check = 1; /* Only use the acquisition number if we've determined it is * not constant. */ if (G.min_acq_num != G.max_acq_num) { tmp_index = acr_find_int(group_list, ACR_Acquisition, -1); /* If the acquisition number corresponds to the time axis, * we DON'T want to perform the range check below. */ if (G.max_acq_num == gi_ptr->max_size[TIME]) { range_check = 0; } } if (tmp_index < 0) { /* Yet another alternative field from some PET scans. * Generally needs to be range-checked. */ tmp_index = acr_find_int(group_list, ACR_PET_Image_index, -1); } if (tmp_index < 0) { /* If all else fails, try this. Will almost certainly need * to be range-checked. */ tmp_index = acr_find_int(group_list, ACR_Image, -1); } /* Force the index to remain inside the established range of the * dimension, if known. Often these values will vary from 1-N * where N is the total number of slices in an acquisition. * We do NOT perform this check if we're dealing with * a mosaic or multiframe image. */ if (range_check && tmp_index >= 1 && gi_ptr->max_size[SLICE] > 1 && gi_ptr->max_size[TIME] > 1 && gi_ptr->subimage_type == SUBIMAGE_TYPE_NONE) { /* Keep the index inside the proper range, if known. */ tmp_index = ((tmp_index - 1) / gi_ptr->max_size[SLICE]) + 1; //printf("D: %d ", tmp_index); } } if (tmp_index < 0) { tmp_index = 1; } fi_ptr->index[imri] = tmp_index; } else { fi_ptr->index[imri] = 1; } //printf("%s: %d ", Mri_Names[imri], fi_ptr->index[imri]); } //printf("\n"); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_info @INPUT : group_list - input data @OUTPUT : file_inf_p - file-specific info general_info - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : Modified Feb. 2000 by Rick Hoge to handle Acquisition loop mode : if assume_acq_loop is FALSE the other added variables don't : matter ---------------------------------------------------------------------------- */ void get_file_info(Acr_Group group_list, File_Info *fi_ptr, General_Info *gi_ptr, const char *file_name) { Mri_Index imri; /* MRI index (SLICE, ECHO, TIME, PHASE...) */ int nrows; /* Row count in this file */ int ncolumns; /* Column count in this file */ int spatial_sizes[VOL_NDIMS]; /* Voxel coordinate extents */ double study_id; /* Study identifier */ int acq_id; /* Acquisition identifier */ int rec_num; /* ? Seems to be a dummy */ int cur_index; /* Index of slice(s) in current file */ int index; /* General index value */ Orientation orientation; /* TRANSVERSE, SAGITTAL, or CORONAL */ World_Index volume_to_world[VOL_NDIMS]; /* Maps voxel to world indices */ double coordinate[WORLD_NDIMS]; /* Slice coordinates */ double dircos[VOL_NDIMS][WORLD_NDIMS]; /* Direction cosines */ double steps[VOL_NDIMS]; /* Step (spacing) coordinates */ double starts[VOL_NDIMS]; /* Start (origin) coordinates */ Acr_Element element; /* Get image dimensions */ nrows = (int)acr_find_short(group_list, ACR_Rows, 0); ncolumns = (int)acr_find_short(group_list, ACR_Columns, 0); spatial_sizes[VROW] = nrows; spatial_sizes[VCOLUMN] = ncolumns; spatial_sizes[VSLICE] = acr_find_int(group_list, ACR_Images_in_acquisition, 1); /* Initialize fields in the File_Info structure. */ for (imri = 0; imri < MRI_NDIMS; imri++) { fi_ptr->index[imri] = 0; fi_ptr->width[imri] = 0.0; fi_ptr->coordinate[imri] = 0.0; } /* Get intensity information */ get_intensity_info(group_list, fi_ptr); /* Check for necessary values not found */ if ((nrows <= 0) || (ncolumns <= 0) || (fi_ptr->bits_stored <= 0) || (fi_ptr->bits_alloc <= 0)) { if (G.Debug) { printf("ERROR: Needed values missing, marking '%s' invalid\n", file_name); } fi_ptr->valid = FALSE; return; } /* Get study, acq, rec, image type id's */ get_identification_info(group_list, &study_id, &acq_id, &rec_num, NULL); if (!gi_ptr->initialized) { get_axis_lengths(group_list, gi_ptr, fi_ptr); spatial_sizes[VSLICE] = gi_ptr->max_size[SLICE]; } get_file_indices(group_list, gi_ptr, fi_ptr); if (!gi_ptr->initialized) { for (imri = 0; imri < MRI_NDIMS; imri++) { gi_ptr->default_index[imri] = fi_ptr->index[imri]; } } /* Get coordinate information. This needs to be done before we call * init_general_info(). */ get_coordinate_info(group_list, fi_ptr, &orientation, volume_to_world, spatial_sizes, dircos, steps, starts, coordinate); /* * Use the coordinate information rather than the slice or time * position derived above. This seems to be much more reliable with * IMA files, but not for real DICOM. */ if (G.file_type == IMA) { fi_ptr->index[SLICE] = irnd(fi_ptr->coordinate[SLICE] * 100.0); } /* Set up general info on first pass */ if (!gi_ptr->initialized) { init_general_info(gi_ptr, fi_ptr, group_list, volume_to_world, spatial_sizes, dircos, steps, starts, study_id, acq_id, rec_num); for (imri = 0; imri < MRI_NDIMS; imri++) { gi_ptr->indices[imri][0] = fi_ptr->index[imri]; gi_ptr->coordinates[imri][0] = fi_ptr->coordinate[imri]; gi_ptr->widths[imri][0] = fi_ptr->width[imri]; printf("First %s at %d,%f,%f\n", Mri_Names[imri], fi_ptr->index[imri], fi_ptr->coordinate[imri], fi_ptr->width[imri]); } } /* Update general info and validate file on later passes */ else { /* Check for consistent pixel minimum and maximum. */ if ((gi_ptr->pixel_max != fi_ptr->pixel_max) || (gi_ptr->pixel_min != fi_ptr->pixel_min)) { printf("WARNING: Inconsistent pixel minimum and maximum\n"); printf(" %f %f, %f %f\n", gi_ptr->pixel_min, gi_ptr->pixel_max, fi_ptr->pixel_min, fi_ptr->pixel_max); #if 0 if (gi_ptr->pixel_max < fi_ptr->pixel_max) { gi_ptr->pixel_max = fi_ptr->pixel_max; } if (gi_ptr->pixel_min > fi_ptr->pixel_min) { gi_ptr->pixel_min = fi_ptr->pixel_min; } #endif } /* Check for consistent data type */ if (((gi_ptr->datatype == NC_BYTE) && (fi_ptr->bits_alloc > 8)) || ((gi_ptr->datatype == NC_SHORT) && (fi_ptr->bits_alloc <= 8))) { printf("Inconsistent datatype, marking invalid\n"); fi_ptr->valid = FALSE; return; } /* Check row and columns sizes */ if ((nrows != gi_ptr->nrows) && (ncolumns != gi_ptr->ncolumns)) { printf("Mismatched rows/columns, marking invalid\n"); fi_ptr->valid = FALSE; return; } /* Check study and acquisition id's */ if ((gi_ptr->study_id != study_id) || (gi_ptr->acq_id != acq_id)) { printf("Mismatched acquisition/study, marking invalid\n"); fi_ptr->valid = FALSE; return; } // Update display window info if (gi_ptr->window_min > fi_ptr->window_min) gi_ptr->window_min = fi_ptr->window_min; if (gi_ptr->window_max < fi_ptr->window_max) gi_ptr->window_max = fi_ptr->window_max; /* Look to see if indices have changed */ for (imri = 0; imri < MRI_NDIMS; imri++) { /* If a dimension is known to have a maximum size of one * or less, we do NOT allow it to grow in any way. An * exception is made for the slice and time dimensions, * however, since it appears that it is common for them to * be unspecified and can be guessed only by the number of * distinct locations discovered. */ if (imri != SLICE && gi_ptr->max_size[imri] <= 1) { if (/* G.Debug && */ fi_ptr->index[imri] > 1) { printf("Warning: merging extra indices on %s axis: ", Mri_Names[imri]); printf(" %d %d\n", gi_ptr->max_size[imri], fi_ptr->index[imri]); } continue; } /* Get current index */ cur_index = fi_ptr->index[imri]; /* Check whether this index is in the list. */ if (gi_ptr->cur_size[imri] == 1) { index = ((cur_index == gi_ptr->default_index[imri]) ? 0 : -1); } else { /* Search list of indices for 'cur_index'. Search is started at search_start[] and has maximum length of cur_size[imri]. */ index = search_list(cur_index, gi_ptr->indices[imri], gi_ptr->cur_size[imri], gi_ptr->search_start[imri]); } /* If it is not, then add it */ if (index < 0) { if (G.Debug >= HI_LOGGING) { printf("Need to add index %d to %s list, %d/%d\n", cur_index, Mri_Names[imri], gi_ptr->cur_size[imri], gi_ptr->max_size[imri]); } /* Check whether we can add a new index */ if (gi_ptr->cur_size[imri] >= gi_ptr->max_size[imri]) { gi_ptr->max_size[imri]++; gi_ptr->indices[imri] = realloc(gi_ptr->indices[imri], gi_ptr->max_size[imri] * sizeof(int)); gi_ptr->coordinates[imri] = realloc(gi_ptr->coordinates[imri], gi_ptr->max_size[imri] * sizeof(double)); gi_ptr->widths[imri] = realloc(gi_ptr->widths[imri], gi_ptr->max_size[imri] * sizeof(double)); } /* Add the index and coordinate to the lists */ index = gi_ptr->cur_size[imri]; gi_ptr->search_start[imri] = index; gi_ptr->indices[imri][index] = cur_index; gi_ptr->coordinates[imri][index] = fi_ptr->coordinate[imri]; gi_ptr->widths[imri][index] = fi_ptr->width[imri]; gi_ptr->cur_size[imri]++; if (G.Debug) { printf("Added %s coordinate %f at %d %f %f %f\n", Mri_Names[imri], gi_ptr->coordinates[imri][index], cur_index, coordinate[0], coordinate[1], coordinate[2]); } } else if (G.Debug) { /* printf("%s: %s index %d is already on the list.\n", file_name, Mri_Names[imri], cur_index); printf("Existing coordinate: %f, file coordinate: %f\n", gi_ptr->coordinates[imri][index], fi_ptr->coordinate[imri]); */ } } /* Loop over Mri_Index */ //ilana debug //int test = fi_ptr->coordinate[TIME]; //print ("*******Time coordinate IS: %i\n\n",test); } /* Get DTI information if available. */ fi_ptr->b_value = (double)acr_find_double(group_list, ACR_Diffusion_b_value, -1); element = acr_find_group_element(group_list, ACR_Diffusion_gradient_orientation); Acr_Double grad_direction[WORLD_NDIMS]; if (element == NULL || acr_get_element_double_array(element, WORLD_NDIMS, grad_direction) != WORLD_NDIMS) { grad_direction[XCOORD] = grad_direction[YCOORD] = grad_direction[ZCOORD] = 0; /*this should be 0 for the b=0 images*/ } fi_ptr->grad_direction[XCOORD] = (double)grad_direction[XCOORD]; fi_ptr->grad_direction[YCOORD] = (double)grad_direction[YCOORD]; fi_ptr->grad_direction[ZCOORD] = (double)grad_direction[ZCOORD]; /* Want to add B-matrix if it exists, the problem is that the b=0 image does not even have the field while the other directions might (AND b=0 is ofen the first image!). Initialize to all 0s and decide later whether to include in header? */ if (gi_ptr->acq.dti) { element = acr_find_group_element(group_list, SPI_B_matrix); if (element == NULL || acr_get_element_double_array(element, B_MATRIX_COUNT, fi_ptr->b_matrix) != B_MATRIX_COUNT) { int i; for (i = 0; i < B_MATRIX_COUNT; i++) { fi_ptr->b_matrix[i] = 0; /* bmatrix for b=0 should be 0*/ } } } // If we get to here, then we have a valid file fi_ptr->valid = TRUE; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_identification_info @INPUT : group_list - input data @OUTPUT : study_id acq_id rec_num image_type @RETURNS : (nothing) @DESCRIPTION: Routine to get image identification information. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_identification_info(Acr_Group group_list, double *study_id, int *acq_id, int *rec_num, int *image_type) { int number_of_frames; int number_of_averages; if (study_id != NULL) { // generate a study ID number from date & time: yyyymmdd.hhmmss // (should be unique enough for our application) *study_id = (((float)acr_find_int(group_list, ACR_Study_date, 0)) + ((float)acr_find_int(group_list, ACR_Study_time, 0))/1e6); } if (acq_id != NULL) { *acq_id = acr_find_int(group_list, ACR_Series, 0); number_of_frames = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); number_of_averages = acr_find_int(group_list, ACR_Nr_of_averages, 1); /* Determine if measurement loop was used (rhoge) - if so, we replace the different series numbers with ACR_Study_time, which is the same for all frames in a run. This will aid in grouping the files later on. Criteria used for identification of meast loop: 1) more than one dynamic scan 2) number of dynamic scans NOT equal to nominal number of signal averages (if they're equal, we assume acquisition loop scan) WARNING: it is possible that someone might use the measurement loop do serial scans which each have multiple signal averages. If NSA = the number of measts. in this case, then the scan will not be recognized as a Meast loop scan and the different frames will be placed in different series. To fix this, we'd really need to look at the series numbers of future and past files. It's also unlikely to happen but I'm sure it will... This also means we should NOT correct the number of signal averages on the sending side */ /* if ((number_of_frames > 1) || (*acq_id == 0)) { (orig test) */ if ( (G.file_type == N3DCM) && (number_of_frames > 1) && (number_of_frames != number_of_averages)) { *acq_id = acr_find_int(group_list, ACR_Study_time, 0); } } if (rec_num != NULL) *rec_num = 0; if (image_type != NULL) *image_type = 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_intensity_info @INPUT : group_list - input data @OUTPUT : fi_ptr - file-specific info @RETURNS : (nothing) @DESCRIPTION: Routine to get intensity information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_intensity_info(Acr_Group group_list, File_Info *fi_ptr) { double window_centre, window_width; double rescale_intercept, rescale_slope; int ivalue; /* 0000 for unsigned, 0001 for signed */ int imin, imax; /* Default minimum and maximum values */ /* Get pixel storage information */ fi_ptr->bits_alloc = (int)acr_find_short(group_list, ACR_Bits_allocated, 0); fi_ptr->bits_stored = (int)acr_find_short(group_list, ACR_Bits_stored, 0); /* bert- properly set the minimum and maximum pixel values depending * on whether or not this file specifies signed pixel values. */ ivalue = acr_find_int(group_list, ACR_Pixel_representation, -1); if (ivalue == ACR_PIXEL_REP_SIGNED) { imin = -(1 << (fi_ptr->bits_stored - 1)); imax = (1 << (fi_ptr->bits_stored - 1)) - 1; } else { imin = 0; imax = (1 << fi_ptr->bits_stored) - 1; } if (G.useMinMax) { int pmin; int pmax; /* Get pixel value information * I think this might wrongly assume that the ACR values min/max * apply to the whole volume - it actually appears that they apply * to the current slice. */ pmin = (int)acr_find_short(group_list, ACR_Smallest_pixel_value, imin); pmax = (int)acr_find_short(group_list, ACR_Largest_pixel_value, imax); /* Hack to convert to signed representation if indicated - if * bit 15 is set, we have to "promote" to 2's complement by * sign-extending the result before converting it to a double. * Perhaps this sort of thing needs to be pushed down into the * ACR-NEMA library somehow, so that the representations of * short values are automatically converted properly? */ if (ivalue == ACR_PIXEL_REP_SIGNED) { if (pmin & 0x8000) { pmin -= 0x10000; } if (pmax & 0x8000) { pmax -= 0x10000; } } fi_ptr->pixel_min = (double) pmin; fi_ptr->pixel_max = (double) pmax; } else { /* for now, use bits_stored to determine dynamic range * DICOM info on largest pixel applies to first slice, * not whole volume - this caused problems (roundoff?) * in Siemens Numaris 4 scans */ fi_ptr->pixel_min = imin; fi_ptr->pixel_max = imax; } /* Get the rescale intercept and slope. If they are not present, * we use the default values of 0.0 for the intercept and 1.0 for * the slope. */ rescale_intercept = (double)acr_find_double(group_list, ACR_Rescale_intercept, 0); rescale_slope = (double)acr_find_double(group_list, ACR_Rescale_slope, 1); /* If the rescale slope is set to zero, force the default value of * one and issue a warning. */ if (rescale_slope == 0.0) { printf("WARNING: File contains a rescale slope value of zero.\n"); rescale_slope = 1.0; } fi_ptr->slice_min = fi_ptr->pixel_min * rescale_slope + rescale_intercept; fi_ptr->slice_max = fi_ptr->pixel_max * rescale_slope + rescale_intercept; /* Get window min and max */ window_centre = (fi_ptr->slice_max + fi_ptr->slice_min) / 2.0; window_width = fi_ptr->slice_max - fi_ptr->slice_min; window_centre = (double)acr_find_double(group_list, ACR_Window_centre, window_centre); window_width = (double)acr_find_double(group_list, ACR_Window_width, window_width); fi_ptr->window_min = window_centre - window_width / 2.0; fi_ptr->window_max = window_centre + window_width / 2.0; } /* Function to recursively search an element list for a specific * element, skipping a specified number of occurrences before * returning. This is only called by acr_recurse_for_element(). */ static Acr_Element acr_recursive_search(Acr_Element el_lst, int *nskip, Acr_Element_Id srch_id) { Acr_Element el_ret = NULL; Acr_Element el_tmp; for (el_tmp = el_lst; el_tmp != NULL; el_tmp = acr_get_element_next(el_tmp)) { /* If we find what we're looking for, return it. */ if (acr_get_element_group(el_tmp) == srch_id->group_id && acr_get_element_element(el_tmp) == srch_id->element_id) { if (*nskip <= 0) { el_ret = el_tmp; break; } else { --(*nskip); } } /* See if we need to recurse. */ if (acr_element_is_sequence(el_tmp)) { el_lst = (Acr_Element) acr_get_element_data(el_tmp); el_ret = acr_recursive_search(el_lst, nskip, srch_id); if (el_ret != NULL) { break; } } } return (el_ret); } /* acr_recurse_for_element() * * Function to search a group list for a particular element. Unlike other * functions along these lines, this function will recursively descend into * compound datatypes (DICOM sequences) to hunt for instances of a particular * element. * * The search proceeds in two stages: The first is to search for * a particular sequence object. This must be found or else the search is * called off. Once the expected sequences is found, the function will * recursively search all of the substructure of that sequence for the * requested subelement. The "nskip" parameter tells the function to ignore * the first "nskip" matches that it locates. */ Acr_Element acr_recurse_for_element(Acr_Group group_list, int nskip, Acr_Element_Id seq_id, Acr_Element_Id srch_id) { Acr_Element el_seq; /* Hunt for the necessary sequence object. */ el_seq = acr_find_group_element(group_list, seq_id); if (el_seq == NULL || !acr_element_is_sequence(el_seq)) { /* If not found, or not a sequence, abort the search. */ return (NULL); } /* Otherwise proceed to "stage 2" and hunt for the requested subelement. */ return acr_recursive_search((Acr_Element) acr_get_element_data(el_seq), &nskip, srch_id); } int dicom_read_position(Acr_Group group_list, int n, double coordinate[3]) { Acr_Element element; int result; /* Try to read a unique element from the sequences. If this * succeeds, we need to flag this fact so that the higher-level * processing can adapt accordingly. */ element = acr_recurse_for_element(group_list, n, ACR_Perframe_func_groups_seq, ACR_Image_position_patient); if (element != NULL) { result = DICOM_POSITION_LOCAL; /* Found a slice-specific position */ } else { result = DICOM_POSITION_GLOBAL; /* Found a global position */ /* bert-look for field in weird XMedCon location */ element = acr_recurse_for_element(group_list, 0, ACR_Detector_information_seq, ACR_Image_position_patient); if (element == NULL) { element = acr_find_group_element(group_list, ACR_Image_position_patient); } if (element == NULL) { element = acr_find_group_element(group_list, ACR_Image_position_patient_old); } } if (element == NULL) { printf("WARNING: Failed to find image position\n"); } else { if (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) == WORLD_NDIMS) { return (result); } if (G.Debug) { printf("WARNING: Failed to read image position ('%s')\n", (char *)acr_get_element_string(element)); } } return DICOM_POSITION_NONE; } int dicom_read_orientation(Acr_Group group_list, double orientation[6]) { Acr_Element element; int result; /* read in row/col vectors: */ /* Look for single global header with slices all in same file. */ element = acr_recurse_for_element(group_list, 0, ACR_Shared_func_groups_seq, ACR_Image_orientation_patient); /* Look for single local header with slices all in same file, with one header per slice (Philips Intera). */ if (element == NULL) { element = acr_recurse_for_element(group_list, 0, ACR_Perframe_func_groups_seq, ACR_Image_orientation_patient); } /* bert - deal with weird XMedCon images... */ if (element == NULL) { element = acr_recurse_for_element(group_list, 0, ACR_Detector_information_seq, ACR_Image_orientation_patient); } /* Look for header with slices in separate files. */ if (element == NULL) { element = acr_find_group_element(group_list, ACR_Image_orientation_patient); } if (element == NULL) { /* If we failed to find the newer, better patient orientation * information, try to use the obsolete information if present. */ element = acr_find_group_element(group_list, ACR_Image_orientation_patient_old); } if (element == NULL) { printf("WARNING: Failed to find image orientation!\n"); return (0); } else if ((result = acr_get_element_numeric_array(element, 6, orientation)) != 6) { printf("WARNING: Failed to read image orientation! (%d, '%s')\n", result, (char *)acr_get_element_string(element)); return (0); } return (1); } /* * Read the pixel size, an array of 2 floating point numbers, from the * DICOM group list. */ int dicom_read_pixel_size(Acr_Group group_list, double pixel_size[IMAGE_NDIMS]) { Acr_Element element; int result = 0; int i; for (i = 0; i < IMAGE_NDIMS; i++) { pixel_size[i] = -DBL_MAX; } /* Look for single global header with slices all in same file. */ element = acr_recurse_for_element(group_list, 0, ACR_Shared_func_groups_seq, ACR_Pixel_size); /* Look for single local header with slices all in same file, with one header per slice (Philips Intera). */ if (element == NULL) { element = acr_recurse_for_element(group_list, 0, ACR_Perframe_func_groups_seq, ACR_Pixel_size); } /* Look for header with slices in separate files. */ if (element == NULL) { element = acr_find_group_element(group_list, ACR_Pixel_size); } if (element == NULL) { printf("WARNING: Can't find pixel size element\n"); } else { /* Extract sizes from header string if found. */ if (acr_get_element_numeric_array(element, IMAGE_NDIMS, pixel_size) != IMAGE_NDIMS) { printf("WARNING: Can't read pixel size element\n"); } else { result = 1; } } /* If the values are still uninitialized, set them to some reasonable * defaults. */ if (pixel_size[0] == -DBL_MAX) { pixel_size[0] = 1.0; /* Assume 1mm spacing */ } if (pixel_size[1] == -DBL_MAX) { pixel_size[1] = pixel_size[0]; /* Assume uniform sample grid. */ } return (result); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_coordinate_info @INPUT : group_list - input data sizes - size of each spatial dimension @OUTPUT : fi_ptr - file-specific info volume_to_world - volume index to world coordinate index mapping dircos - direction cosines for spatial dimensions steps - step sizes for spatial dimensions starts - start positions for spatial dimensions (for a slice) coordinate - coordinate of centre of slice @RETURNS : (nothing) @DESCRIPTION: Routine to get coordinate information for a slice from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_coordinate_info(Acr_Group group_list, File_Info *fi_ptr, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], const int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]) { Volume_Index ivolume; World_Index iworld; int found_dircos[VOL_NDIMS]; int found_coordinate; double frame_time; double start_time; double magnitude; double largest; double psize[IMAGE_NDIMS]; double slice_thickness, slice_spacing; double RowColVec[6]; /* row/column unit vectors in dicom element */ const Orientation orientation_list[WORLD_NDIMS] = { SAGITTAL, CORONAL, TRANSVERSE }; if (G.Debug >= HI_LOGGING) { printf("get_coordinate_info\n"); } /* Initialize a few things... */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { found_dircos[ivolume] = FALSE; } found_coordinate = FALSE; #if 0 /* TODO: For now this appears to be necessary. In cases I don't fully * understand, the Siemens Numaris 3 DICOM image orientation does not * give the correct direction cosines, so we use the nonstandard Siemens * fields instead. Someday I should figure out the relation (if any) * between the standard fields and these fields, and try to normalize * this mess. * * We only attempt this for files that are clearly marked as SIEMENS * files, with a version string that looks like VB33 (VB33D, VB33G, etc.) * Later versions do not seem to use these fields. */ if (is_numaris3(group_list)) { Acr_Element_Id dircos_elid[VOL_NDIMS]; /* Set direction cosine element ids. Note that the reversal of * rows and columns is intentional - their idea of the meaning * of theses labels is different from ours. (Their row vector * points along the row and not along the row dimension.) */ dircos_elid[VSLICE] = SPI_Image_normal; dircos_elid[VROW] = SPI_Image_column; dircos_elid[VCOLUMN] = SPI_Image_row; /* Get direction cosines */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { element = acr_find_group_element(group_list, dircos_elid[ivolume]); if (element == NULL) { continue; } if (acr_get_element_numeric_array(element, WORLD_NDIMS, dircos[ivolume]) != WORLD_NDIMS) { continue; } /* negate the X and Z coordinates */ convert_numa3_coordinate(dircos[ivolume]); found_dircos[ivolume] = TRUE; } } #endif /* If we did not find the Siemens proprietary image vectors, try * the DICOM standard image position. */ if (!found_dircos[VCOLUMN] || !found_dircos[VROW] || !found_dircos[VSLICE]) { if (dicom_read_orientation(group_list, RowColVec)) { dircos[VCOLUMN][XCOORD] = RowColVec[0]; dircos[VCOLUMN][YCOORD] = RowColVec[1]; dircos[VCOLUMN][ZCOORD] = RowColVec[2]; dircos[VROW][XCOORD] = RowColVec[3]; dircos[VROW][YCOORD] = RowColVec[4]; dircos[VROW][ZCOORD] = RowColVec[5]; found_dircos[VCOLUMN] = TRUE; found_dircos[VROW] = TRUE; convert_dicom_coordinate(dircos[VROW]); convert_dicom_coordinate(dircos[VCOLUMN]); /* slice direction unit vector is cross product of row, col vectors: */ dircos[VSLICE][XCOORD] = dircos[VCOLUMN][YCOORD] * dircos[VROW][ZCOORD] - dircos[VCOLUMN][ZCOORD] * dircos[VROW][YCOORD]; dircos[VSLICE][YCOORD] = dircos[VCOLUMN][ZCOORD] * dircos[VROW][XCOORD] - dircos[VCOLUMN][XCOORD] * dircos[VROW][ZCOORD]; dircos[VSLICE][ZCOORD] = dircos[VCOLUMN][XCOORD] * dircos[VROW][YCOORD] - dircos[VCOLUMN][YCOORD] * dircos[VROW][XCOORD]; found_dircos[VSLICE] = TRUE; } } if (G.Debug >= HI_LOGGING) { printf("dircos %f %f %f %f %f %f %f %f %f\n", dircos[VSLICE][XCOORD], dircos[VSLICE][YCOORD], dircos[VSLICE][ZCOORD], dircos[VROW][XCOORD], dircos[VROW][YCOORD], dircos[VROW][ZCOORD], dircos[VCOLUMN][XCOORD], dircos[VCOLUMN][YCOORD], dircos[VCOLUMN][ZCOORD]); } /* Normalize the direction cosines */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { magnitude = 0.0; for (iworld=0; iworld < WORLD_NDIMS; iworld++) { magnitude += dircos[ivolume][iworld] * dircos[ivolume][iworld]; } if (magnitude <= 0) { found_dircos[ivolume] = FALSE; continue; } magnitude = sqrt(magnitude); for (iworld=0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] /= magnitude; } } /* If we don't find direction cosines, then assume transverse volume */ if (!found_dircos[VSLICE] || !found_dircos[VROW] || !found_dircos[VCOLUMN]) { if (G.Debug) { printf("Using default direction cosines\n"); } for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] = ((ivolume == (WORLD_NDIMS-iworld-1)) ? -1.0 : 0.0); found_dircos[iworld] = TRUE; } } } /* Figure out volume index to world index mapping and sign of direction * cosines - the code below figures out the primary direction in x,y,z * of each volume coordinate (row,col,slice) */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { largest = -1.0; for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { magnitude = dircos[ivolume][iworld]; if (magnitude < 0.0) magnitude = -magnitude; if (magnitude > largest) { largest = magnitude; volume_to_world[ivolume] = iworld; } } } if (G.Debug >= HI_LOGGING) { printf(" Volume_to_world slice=%s row=%s column=%s\n", World_Names[volume_to_world[VSLICE]], World_Names[volume_to_world[VROW]], World_Names[volume_to_world[VCOLUMN]]); } /* Get orientation (depends on primary direction of slice normal) */ *orientation = orientation_list[volume_to_world[VSLICE]]; if (G.Debug >= HI_LOGGING) { printf(" Orientation is %s\n", (*orientation == SAGITTAL) ? "SAGITTAL" : (*orientation == CORONAL) ? "CORONAL" : "TRANSVERSE"); } /* Get step information */ dicom_read_pixel_size(group_list, psize); steps[VCOLUMN] = psize[0]; steps[VROW] = psize[1]; /* anisotropic resolution? */ /* Figure out the slice thickness. It could be from either one of * two possible places in the file. * * This code has changed several times, and there may be no single * correct way of deriving the true slice spacing from the official * DICOM slice thickness and slice spacing fields. My best guess is * to look for both fields, and to adopt the */ slice_thickness = (double)acr_find_double(group_list, ACR_Slice_thickness, 0); slice_spacing = (double)acr_find_double(group_list, ACR_Spacing_between_slices, 0); if (slice_thickness == 0.0) { /* No slice thickness value found. */ if (slice_spacing == 0.0) { if (G.Debug >= HI_LOGGING) { printf("Using default slice thickness of 1.0\n"); } steps[VSLICE] = 1.0; } else { if (G.Debug >= HI_LOGGING) { printf("Using (0018,0088) for slice thickness\n"); } steps[VSLICE] = slice_spacing; } } else if (slice_spacing == 0.0) { /* No slice spacing value found. */ if (G.Debug >= HI_LOGGING) { printf("Using (0018,0050) for slice thickness\n"); } steps[VSLICE] = slice_thickness; } else { /* Both fields are set. I choose the slice spacing rather * than the slice thickness in this case. However, I believe * there is some evidence that this can cause problems in rare * cases. */ if (G.Debug && !NEARLY_EQUAL(slice_thickness, slice_spacing)) { printf("WARNING: slice thickness conflict: "); printf("old = %.10f, new = %.10f\n", slice_thickness, slice_spacing); } steps[VSLICE] = slice_spacing; } /* Make sure that direction cosines point the right way (dot * product of direction cosine and axis is positive) and that step * has proper sign. */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; if (dircos[ivolume][iworld] < 0.0) { if (G.Debug >= HI_LOGGING) { printf("Swapping direction of %s %s\n", Volume_Names[ivolume], World_Names[iworld]); } steps[ivolume] *= -1.0; for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] *= -1.0; } } } /* Find 3D coordinate of slice - ACR_Image_position_patient gives * the *corner* of the slice! * * Start by assuming that we didn't find it. */ found_coordinate = FALSE; for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { coordinate[iworld] = 0.0; } if (G.opts & OPTS_NO_LOCATION) { /* If the coordinates are untrustworthy, just generate something * reasonable for the slice coordinate. Ignore the rest. */ coordinate[volume_to_world[VSLICE]] = (steps[VSLICE] * fi_ptr->index[SLICE]); found_coordinate = TRUE; } else { found_coordinate = dicom_read_position(group_list, fi_ptr->index[SLICE] - 1, coordinate); if (!found_coordinate) { /* Last gasp - try to interpret the slice location as our slice * position. It might work. */ if (!found_coordinate) { coordinate[volume_to_world[VSLICE]] = (double)acr_find_double(group_list, ACR_Slice_location, 1.0); } found_coordinate = TRUE; } } convert_dicom_coordinate(coordinate); /* Work out start positions in volume coordinates */ for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && found_dircos[VCOLUMN]) { starts[ivolume] = coordinate[XCOORD] * dircos[ivolume][XCOORD] + coordinate[YCOORD] * dircos[ivolume][YCOORD] + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; } else { starts[ivolume] = 0.0; } } if (G.Debug >= HI_LOGGING) { printf(" coordinate %f %f %f, start %f %f %f\n", coordinate[XCOORD], coordinate[YCOORD], coordinate[ZCOORD], starts[VROW], starts[VCOLUMN], starts[VSLICE]); } /* Find position along each dimension */ fi_ptr->coordinate[SLICE] = starts[VSLICE]; fi_ptr->coordinate[ECHO] = (double)acr_find_double(group_list, ACR_Echo_time, 0.0) / MS_PER_SECOND; /* Get the dimension width for time, if available. The units are in * milliseconds in DICOM, whereas we use seconds in MINC. */ fi_ptr->width[TIME] = (double)acr_find_double(group_list, ACR_Actual_frame_duration, 0.0) / MS_PER_SECOND; /* PET scan times (bert) */ frame_time = acr_find_double(group_list, ACR_Trigger_time, -1.0); if (frame_time >= 0.0) { frame_time /= 1000.0; start_time = 0; } else { start_time = (double)acr_find_double(group_list, ACR_Frame_reference_time, -1.0); frame_time = (double)acr_find_double(group_list, ACR_Actual_frame_duration, -1.0); if (start_time > 0.0 && frame_time > 0.0) { frame_time = start_time / 1000.0; /* Convert msec to seconds. */ } else { /* Try to use the repetition time if it is legit. For now we * make this test very restrictive. */ double tr = acr_find_double(group_list, ACR_Repetition_time, -1.0); char *str_seq = acr_find_string(group_list, ACR_Scanning_sequence, ""); char *str_var = acr_find_string(group_list, ACR_Sequence_variant, ""); if (tr > 0.0 && !strcmp(str_seq, "EP") && !strcmp(str_var, "SK")) { frame_time = (fi_ptr->index[TIME] - 1) * tr / 1000.0; } else { /* time section (rhoge) * now assume that time has been fixed when file was read */ start_time = (double)acr_find_double(group_list, ACR_Series_time, 0.0); frame_time = (double)acr_find_double(group_list, ACR_Acquisition_time, 0.0); if (frame_time == 0.0) { /* See if the acquisition datetime contains anything useful. If so * we will use it. */ char *dt_str = acr_find_string(group_list, ACR_Acquisition_datetime, ""); if (dt_str[0] != 0) { /* If we got the string, it is of the format: * YYYYMMDDHHMMSS.FFFFFF * We skip the first eight characters because we don't * care about the date. */ frame_time = atof(dt_str + 8); /* TODO: We would like a better way of getting the start time, * but there don't seem to be other fields that are 100% reliable. */ start_time = 0; } } start_time = convert_time_to_seconds(start_time); frame_time = convert_time_to_seconds(frame_time) - start_time; /* check for case where scan starts right before midnight, * but frame is after midnight */ if (start_time < 600.0 && frame_time < 0.0) { frame_time += SECONDS_PER_DAY; } } } } fi_ptr->coordinate[TIME] = frame_time; /* end of time section */ fi_ptr->coordinate[PHASE] = 0.0; fi_ptr->coordinate[CHEM_SHIFT] = 0.0; } #if 0 /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_numa3_coordinate @INPUT : coordinate @OUTPUT : coordinate @RETURNS : (nothing) @DESCRIPTION: Routine to convert a coordinate to the correct orientation @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : made version specific to Numaris 3 SPI data (rhoge) ---------------------------------------------------------------------------- */ static void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]) { coordinate[XCOORD] = -coordinate[XCOORD]; coordinate[ZCOORD] = -coordinate[ZCOORD]; } #endif /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_dicom_coordinate @INPUT : coordinate @OUTPUT : coordinate @RETURNS : (nothing) @DESCRIPTION: Routine to convert a coordinate to the correct orientation @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : made new dicom version (rhoge) ---------------------------------------------------------------------------- */ void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]) { /* Allow the user to override this, if only for debugging purposes... */ if (G.opts & OPTS_KEEP_COORD) { return; } coordinate[XCOORD] = -coordinate[XCOORD]; coordinate[YCOORD] = -coordinate[YCOORD]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_general_header_info @INPUT : group_list - input data @OUTPUT : gi_ptr - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract general header information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void get_string_field(char *out_str, Acr_Group group_list, Acr_Element_Id element_id) { strncpy(out_str, acr_find_string(group_list, element_id, ""), STRING_T_LEN); } void get_general_header_info(Acr_Group group_list, General_Info *gi_ptr) { int length; char *string; if (G.Debug) { printf("SOP Class UID: %s\n", acr_find_string(group_list, ACR_SOP_Class_UID, "")); printf("Images in acquisition: %d\n", acr_find_int(group_list, ACR_Images_in_acquisition, -1)); printf("Acquisitions in series: %d\n", acr_find_int(group_list, ACR_Acquisitions_in_series, -1)); printf("3D raw partitions: %d\n", acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, -1)); } /* Get intensity units */ strncpy(gi_ptr->units, "", STRING_T_LEN); /* Get patient info */ get_string_field(gi_ptr->patient.name, group_list, ACR_Patient_name); get_string_field(gi_ptr->patient.identification, group_list, ACR_Patient_identification); get_string_field(gi_ptr->patient.birth_date, group_list, ACR_Patient_birth_date); get_string_field(gi_ptr->patient.age, group_list, ACR_Patient_age); string = (char*)acr_find_string(group_list, ACR_Patient_sex, ""); if (*string == 'M') strncpy(gi_ptr->patient.sex, MI_MALE, STRING_T_LEN); else if (*string == 'F') strncpy(gi_ptr->patient.sex, MI_FEMALE, STRING_T_LEN); else if (*string == 'O') strncpy(gi_ptr->patient.sex, MI_OTHER, STRING_T_LEN); else strncpy(gi_ptr->patient.sex, "", STRING_T_LEN); gi_ptr->patient.weight = (double)acr_find_double(group_list, ACR_Patient_weight, -DBL_MAX); /* added by rhoge - registration timing info */ get_string_field(gi_ptr->patient.reg_date, group_list, ACR_Study_date); get_string_field(gi_ptr->patient.reg_time, group_list, ACR_Study_time); get_string_field(gi_ptr->patient.position, /*position of patient added by ilana*/ group_list, ACR_Patient_position); /* Get study info */ /*some more timing info added by ilana*/ get_string_field(gi_ptr->acq.series_time, group_list, ACR_Series_time); get_string_field(gi_ptr->study.start_time, group_list, ACR_Study_date); length = strlen(gi_ptr->study.start_time); gi_ptr->study.start_time[length] = ' '; length++; strncpy(&gi_ptr->study.start_time[length], acr_find_string(group_list, ACR_Study_time, ""), STRING_T_LEN - length); string = (char*)acr_find_string(group_list, ACR_Modality, ""); if (strcmp(string, ACR_MODALITY_MR) == 0) strncpy(gi_ptr->study.modality, MI_MRI, STRING_T_LEN); else if (strcmp(string, ACR_MODALITY_PT) == 0) strncpy(gi_ptr->study.modality, MI_PET, STRING_T_LEN); get_string_field(gi_ptr->study.manufacturer, group_list, ACR_Manufacturer); get_string_field(gi_ptr->study.model, group_list, ACR_Manufacturer_model); gi_ptr->study.field_value = (double)acr_find_double(group_list, ACR_Magnetic_field_strength, -DBL_MAX); get_string_field(gi_ptr->study.software_version, group_list, ACR_Software_versions); get_string_field(gi_ptr->study.serial_no, group_list, ACR_Device_serial_number); get_string_field(gi_ptr->study.calibration_date, group_list, ACR_Calibration_date); get_string_field(gi_ptr->study.calibration_time, /*add time as well ilana*/ group_list, ACR_Calibration_time); get_string_field(gi_ptr->study.institution, group_list, ACR_Institution_id); get_string_field(gi_ptr->study.station_id, group_list, ACR_Station_id); get_string_field(gi_ptr->study.referring_physician, group_list, ACR_Referring_physician); get_string_field(gi_ptr->study.performing_physician, group_list, ACR_Performing_physician); get_string_field(gi_ptr->study.operator, group_list, ACR_Operators_name); get_string_field(gi_ptr->study.procedure, group_list, ACR_Procedure_description); sprintf(gi_ptr->study.study_id, "%.6f",gi_ptr->study_id); /* Acquisition id modified by rhoge to get rid of first digit that is not required for identification of run */ /* sprintf(gi_ptr->study.acquisition_id, "%d_%d", acr_find_int(group_list, ACR_Series, 0), gi_ptr->acq_id); */ sprintf(gi_ptr->study.acquisition_id, "%d", gi_ptr->acq_id); /* Get acquisition information */ get_string_field(gi_ptr->acq.acquisition_time, group_list, ACR_Acquisition_time); /*add acquisition start time ilana*/ get_string_field(gi_ptr->acq.image_time, group_list, ACR_Image_time); get_string_field(gi_ptr->acq.scan_seq, group_list, ACR_Sequence_name); get_string_field(gi_ptr->acq.protocol_name, group_list, ACR_Protocol_name); get_string_field(gi_ptr->acq.series_description, /*add series description ilana*/ group_list, ACR_Series_description); get_string_field(gi_ptr->acq.receive_coil, group_list, ACR_Receive_coil_name); get_string_field(gi_ptr->acq.transmit_coil, group_list, ACR_Transmit_coil_name); get_string_field(gi_ptr->acq.slice_order, group_list, EXT_Slice_order); /*0x1 means ASCENDING 0x2 means DESCENDING 0x4 means INTERLEAVED*/ if(!strcmp(gi_ptr->acq.slice_order,"0x1 ")) strncpy(gi_ptr->acq.slice_order, "ascending", STRING_T_LEN); else if(!strcmp(gi_ptr->acq.slice_order,"0x2 ")) strncpy(gi_ptr->acq.slice_order, "descending", STRING_T_LEN); else if(!strcmp(gi_ptr->acq.slice_order,"0x4 ")) strncpy(gi_ptr->acq.slice_order, "interleaved", STRING_T_LEN); gi_ptr->acq.rep_time = (double)acr_find_double(group_list, ACR_Repetition_time, -DBL_MAX); if (gi_ptr->acq.rep_time != -DBL_MAX) gi_ptr->acq.rep_time /= 1000.0; gi_ptr->acq.echo_time = (double)acr_find_double(group_list, ACR_Echo_time, -DBL_MAX); if (gi_ptr->acq.echo_time != -DBL_MAX) gi_ptr->acq.echo_time /= 1000.0; gi_ptr->acq.echo_train_length = (double)acr_find_double(group_list, ACR_Echo_train_length, -DBL_MAX); /*added echo train length ilana*/ gi_ptr->acq.echo_number = (double)acr_find_double(group_list, ACR_Echo_number, -DBL_MAX); gi_ptr->acq.inv_time = (double)acr_find_double(group_list, ACR_Inversion_time, -DBL_MAX); if (gi_ptr->acq.inv_time != -DBL_MAX) gi_ptr->acq.inv_time /= 1000.0; gi_ptr->acq.delay_in_TR = (double)acr_find_double(group_list, EXT_Delay_in_TR, -DBL_MAX); /*added delay in TR ilana*/ if (gi_ptr->acq.delay_in_TR != -DBL_MAX) gi_ptr->acq.delay_in_TR /= 1000000.0; /*write in seconds*/ gi_ptr->acq.b_value = (double)acr_find_double(group_list, EXT_Diffusion_b_value, -DBL_MAX); gi_ptr->acq.flip_angle = (double)acr_find_double(group_list, ACR_Flip_angle, -DBL_MAX); gi_ptr->acq.slice_thickness = (double)acr_find_double(group_list, ACR_Slice_thickness, -DBL_MAX); gi_ptr->acq.num_slices = (double)acr_find_double(group_list, ACR_Images_in_acquisition, -DBL_MAX); gi_ptr->acq.num_dyn_scans = (double)acr_find_double(group_list, ACR_Acquisitions_in_series, -DBL_MAX); gi_ptr->acq.num_avg = (double)acr_find_double(group_list, ACR_Nr_of_averages, -DBL_MAX); gi_ptr->acq.imaging_freq = (double)acr_find_double(group_list, ACR_Imaging_frequency, -DBL_MAX); if (gi_ptr->acq.imaging_freq != -DBL_MAX) gi_ptr->acq.imaging_freq *= 1e6; get_string_field(gi_ptr->acq.imaged_nucl, group_list, ACR_Imaged_nucleus); gi_ptr->acq.win_center = (double)acr_find_double(group_list, ACR_Window_centre, -DBL_MAX); gi_ptr->acq.win_width = (double)acr_find_double(group_list, ACR_Window_width, -DBL_MAX); gi_ptr->acq.num_phase_enc_steps = (double)acr_find_double(group_list, ACR_Number_of_phase_encoding_steps, -DBL_MAX); gi_ptr->acq.percent_sampling = (double)acr_find_double(group_list, ACR_Percent_sampling, -DBL_MAX); /*don't need to multiply by 100 ilana*/ gi_ptr->acq.percent_phase_fov = (double)acr_find_double(group_list, ACR_Percent_phase_field_of_view, -DBL_MAX); /*don't need to multiply by 100 ilana*/ gi_ptr->acq.pixel_bandwidth = (double)acr_find_double(group_list, ACR_Pixel_bandwidth, -DBL_MAX); gi_ptr->acq.sar = (double)acr_find_double(group_list, ACR_SAR, -DBL_MAX); get_string_field(gi_ptr->acq.mr_acq_type, group_list, ACR_MR_acquisition_type); get_string_field(gi_ptr->acq.image_type, group_list, ACR_Image_type); if (G.Debug) { if (strstr(gi_ptr->acq.image_type, "MOSAIC") != NULL) { printf("This appears to be a Mosaic image\n"); } } get_string_field(gi_ptr->acq.phase_enc_dir, group_list, ACR_Phase_encoding_direction); /*Add image comments*/ /*strncpy(gi_ptr->acq.comments, "", STRING_T_LEN);*/ get_string_field(gi_ptr->acq.comments, group_list, ACR_Image_comments); /* Siemens Numaris 4 specific! */ #if 0 gi_ptr->acq.MrProt = strdup(acr_find_string(group_list, EXT_MrProt_dump, "")); #else gi_ptr->acq.MrProt = strdup(""); #endif string = (char*)acr_find_string(group_list, ACR_Acquisition_contrast, ""); gi_ptr->acq.dti = (strstr(string, "DIFFUSION") != NULL); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_time_to_seconds @INPUT : dicom_time @OUTPUT : (none) @RETURNS : real time in seconds from beginning of day @DESCRIPTION: Routine to convert dicom seconds (decimal hhmmss.xxxxx) to real seconds since the start of day. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static double convert_time_to_seconds(double dicom_time) { /* Constants */ #define DICOM_SECONDS_PER_HOUR 10000.0 #define DICOM_SECONDS_PER_MINUTE 100.0 /* Variables */ double hh, mm, ss; /* Get the components of the time */ hh = (int) (dicom_time / DICOM_SECONDS_PER_HOUR); dicom_time -= hh * DICOM_SECONDS_PER_HOUR; mm = (int) (dicom_time / DICOM_SECONDS_PER_MINUTE); dicom_time -= mm * DICOM_SECONDS_PER_MINUTE; ss = dicom_time; /* Work out the number of seconds */ return (hh * 3600.0) + (mm * 60.0) + ss; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dicom_image_data @INPUT : group_list - input data @OUTPUT : image - image data structure (user must free data) @RETURNS : (nothing) @DESCRIPTION: Routine to get an image from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void get_dicom_image_data(Acr_Group group_list, Image_Data *image) { /* Variables */ Acr_Element element; int nrows, ncolumns; int bits_alloc; int image_group; void *data = NULL; long imagepix, ipix; struct Acr_Element_Id elid; nc_type datatype; /* Get the image information */ bits_alloc = (int)acr_find_short(group_list, ACR_Bits_allocated, 0); nrows = (int)acr_find_short(group_list, ACR_Rows, 0); ncolumns = (int)acr_find_short(group_list, ACR_Columns, 0); image_group = (int)acr_find_short(group_list, ACR_Image_location, ACR_IMAGE_GID); /* Figure out type */ if (bits_alloc > CHAR_BIT) datatype = NC_SHORT; else datatype = NC_BYTE; /* Set image info */ imagepix = nrows * ncolumns; image->data = (unsigned short *) malloc(imagepix * sizeof(short)); CHKMEM(image->data); /* Get image pointer */ elid.group_id = image_group; elid.element_id = ACR_IMAGE_EID; element = acr_find_group_element(group_list, &elid); if (element == NULL) { memset(image->data, 0, imagepix * sizeof(short)); return; } data = acr_get_element_data(element); /* Convert the data according to type */ /* Look for byte data */ if (datatype == NC_BYTE) { for (ipix=0; ipix < imagepix; ipix++) { image->data[ipix] = *((unsigned char *) data + ipix); } } else { /* Look for unpacked short data */ if (bits_alloc == nctypelen(datatype) * CHAR_BIT) { acr_get_short(acr_get_element_byte_order(element), nrows*ncolumns, data, image->data); } /* Fill with zeros in any other case */ else { memset(image->data, 0, imagepix * sizeof(short)); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : parse_dicom_groups @INPUT : group_list - list of acr-nema groups that make up object @OUTPUT : data_info - information about data object @RETURNS : (nothing) @DESCRIPTION: Routine to parse dicom object @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ #define IDEFAULT (-1) void parse_dicom_groups(Acr_Group group_list, Data_Object_Info *di_ptr) { Acr_Element element; Acr_Short AcqMat[4]; Acr_Short freq_rows; Acr_Short freq_cols; double slice_coord[WORLD_NDIMS]; /* Get info to construct unique identifiers for study, series/acq * for file processing */ get_identification_info(group_list, &(di_ptr->study_id), &(di_ptr->acq_id), &(di_ptr->rec_num), &(di_ptr->image_type)); /* Get number of echos, echo number, number of dynamic scans and * dynamic_scan_number */ di_ptr->num_echoes = acr_find_int(group_list, ACR_Echo_train_length, IDEFAULT); di_ptr->echo_number = acr_find_int(group_list, ACR_Echo_number, IDEFAULT); di_ptr->num_dyn_scans = acr_find_int(group_list, ACR_Acquisitions_in_series, IDEFAULT); di_ptr->dyn_scan_number = acr_find_int(group_list, ACR_Acquisition, IDEFAULT); di_ptr->global_image_number = acr_find_int(group_list, ACR_Image, IDEFAULT); /* rhoge: new info added to di_ptr by rhoge: nominal number of slices; this is used in detection of a stream of files with the same acquisition ID number in which there are more files than slices. If the number of signal averages is greater than one, we will assume that this means the acquisition loop was used for dynamic scanning. WARNINGS: the same thing may need to be done with `number of partitions' for it to work with 3D scans */ di_ptr->num_slices_nominal = acr_find_int(group_list, ACR_Images_in_acquisition, IDEFAULT); di_ptr->slice_number = acr_find_int(group_list, SPI_Current_slice_number, IDEFAULT); di_ptr->slice_location = (double)acr_find_double(group_list, ACR_Slice_location, 0.0); di_ptr->coord_found = dicom_read_position(group_list, 0, slice_coord); /* identification info needed to generate unique session id * for file names */ di_ptr->study_date = acr_find_int(group_list, ACR_Study_date, IDEFAULT); di_ptr->study_time = acr_find_int(group_list, ACR_Study_time, IDEFAULT); di_ptr->scanner_serialno = acr_find_int(group_list, ACR_Device_serial_number, IDEFAULT); /* identification info needed to determine if mosaics used */ element = acr_find_group_element(group_list, ACR_Acquisition_matrix); if (element != NULL) { /* The acquisition matrix contains four elements: AcqMat[0] -> number of rows in frequency space. AcqMat[1] -> number of columns in frequency space. AcqMat[2] -> number of rows in phase space. AcqMat[3] -> number of columns in phase space. */ acr_get_element_short_array(element, 4, AcqMat); freq_rows = AcqMat[0]; freq_cols = AcqMat[1]; /* rows in acq matrix is larger of freq rows and freq columns: */ di_ptr->acq_rows = ( freq_rows > freq_cols ? freq_rows : freq_cols ); /* all images are square, at this time */ di_ptr->acq_cols = di_ptr->acq_rows; } else { di_ptr->acq_rows = IDEFAULT; di_ptr->acq_cols = IDEFAULT; } di_ptr->rec_rows = acr_find_int(group_list, ACR_Rows, IDEFAULT); di_ptr->rec_cols = acr_find_int(group_list, ACR_Columns, IDEFAULT); di_ptr->num_mosaic_rows = acr_find_int(group_list, EXT_Mosaic_rows, IDEFAULT); di_ptr->num_mosaic_cols = acr_find_int(group_list, EXT_Mosaic_columns, IDEFAULT); di_ptr->num_slices_in_file = acr_find_int(group_list, EXT_Slices_in_file, IDEFAULT); /* sequence, protocol names (useful for debugging): */ get_string_field(di_ptr->sequence_name, group_list, ACR_Sequence_name); get_string_field(di_ptr->protocol_name, group_list, ACR_Protocol_name); get_string_field(di_ptr->patient_name, group_list, ACR_Patient_name); get_string_field(di_ptr->patient_id, group_list, ACR_Patient_identification); } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/string_to_filename.c0000644000175000000620000002027612574624760023355 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename.c @DESCRIPTION: Code to convert a string to something that can be used in a file name. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1997 (Peter Neelin) @MODIFIED : * $Log: string_to_filename.c,v $ * Revision 1.4 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.3 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.2 2005/03/03 18:59:16 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.1 2005/02/17 16:38:11 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.2 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 "dcm2mnc.h" #include #define SEPARATOR '_' /* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename @INPUT : string - string to convert maxlen - maximum length of output string (including terminating '\0') @OUTPUT : filename - output string @RETURNS : (nothing) @DESCRIPTION: Routine to convert a string to something that can be used in a filename @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void string_to_filename(const char *string, char *filename, int maxlen) { int length, isrc, idst; int ch; int found_first, need_separator; /* Get string length */ length = strlen(string); if (length > maxlen-1) length = maxlen - 1; /* Loop through characters */ idst = 0; found_first = FALSE; need_separator = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalnum(ch)) { found_first = TRUE; if (need_separator) { filename[idst++] = SEPARATOR; need_separator = FALSE; } filename[idst++] = tolower(ch); } else if (found_first) { need_separator = TRUE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; return; } void string_to_initials(char *string, char *filename, int maxlen) { /* function added by R. Hoge to convert name-like strings to initials in environment where confidentiality policy prohibits use of names in file-names */ int length, isrc, idst; int ch; int first_found, sep_found, multi_word, comma_found, comma_found2, in_word; /* Get string length */ length = strlen(string); if (length > maxlen-1) length = maxlen - 1; /* do first pass to look for multi-words, commas */ first_found = FALSE; sep_found = FALSE; multi_word = FALSE; comma_found = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; /* if we hit a separator after finding a first alphanum, treat as multi words */ /* alphanumeric expressions (including underscores, hyphens) are treated as discrete words - note that we won't print hyphens */ /* examples of single words: test5 test-5 test_5 examples of multi words: test 5 snr_test 5 test,5 snr-test 5 */ if (sep_found && isalnum(ch)) { multi_word = TRUE; } if (first_found && !(isalnum(ch)||ch=='-'||ch=='_')) { sep_found = TRUE; } if (isalnum(ch)) { first_found = TRUE; } // Numaris 4 used caret (^) to separate names (Last^first) if (ch == ',' || ch == '^') { comma_found = TRUE; } } /* if Patient name is only a single word, then just strip out non-alphanumeric characters examples: snrtest1 -> snrtest1 snrtest-1 -> snrtest1 snr_test-2 -> snr_test2 note that hyphens are omitted, because these are used as delimiters in filename */ if (!multi_word) { idst = 0; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalnum(ch) || ch=='_') { filename[idst++] = tolower(ch); } } /* Add terminating '\0' */ filename[idst++] = '\0'; } else { /* multiple words */ if (!comma_found) { /* examples of multi-word no comma: john doe -> jd john edward doe -> jed john doe-smith -> jds my snr_test -> mst john doe 12 -> jd12 john12 smith -> j12s 12john smith -> 12js john doe test2b -> jst2b note that underscores are treated as separators here, contiguous digits are all printed, and digits are treated as printable separators */ /* Loop through characters */ idst = 0; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; } else { /* multiple words with comma separation */ /* examples of multi-word with comma: doe, john -> jd doe,john -> jd doe-smith, john -> jds note that we treat stuff before the comma as the LAST name */ /* we do two passes: all the stuff after the comma THEN all the stuff before the comma */ idst = 0; /* Loop through characters, writing those after comma*/ comma_found2 = FALSE; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; if (comma_found2) filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; if (comma_found2) filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } // numaris 4 uses Last^First if (ch == ',' || ch == '^') { comma_found2 = TRUE; } } /* now write characters/initials before the comma*/ comma_found2 = FALSE; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; if (!comma_found2) filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; if (!comma_found2) filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } // Numaris 4 uses Last^First if (ch == ',' || ch == '^') { comma_found2 = TRUE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; } } return; } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dicom_to_minc.h0000644000175000000620000002335712574624760022320 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_to_minc.h @DESCRIPTION: Header file for dicom_to_minc.h @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_to_minc.h,v $ * Revision 1.10 2010-11-23 23:30:50 claude * dcm2mnc: fixed seg fault bug (Claude) and added b-matrix (Ilana) * * Revision 1.9 2007-06-08 20:28:57 ilana * added several fields to mincheader (dicom elements and found in ASCONV header) * * Revision 1.8 2006/04/09 15:33:10 bert * Add flags and fields for DTI * * Revision 1.7 2005/11/04 22:25:34 bert * Change COORDINATE_EPSILON again, now set to 0.005 * * Revision 1.6 2005/10/26 23:43:55 bert * Fix COORDINATE_EPSILON at 0.0001 * * Revision 1.5 2005/07/14 19:00:30 bert * Changes ported from 1.X branch * * Revision 1.4.2.2 2005/06/20 22:00:26 bert * Simplify Image_Data structure, add is_siemens_mosaic() function declaration * * Revision 1.4.2.1 2005/05/12 21:16:47 bert * Initial checkin * * Revision 1.4 2005/04/28 17:10:22 bert * Added width information to General_Info and File_Info structures * * Revision 1.3 2005/04/20 23:14:32 bert * Remove unnecessary fields, add copy_spi_to_acr() function definition * * Revision 1.2 2005/03/02 18:25:13 bert * Add Mri_Names and Volume_Names * * Revision 1.1 2005/02/17 16:38:10 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.6 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.5 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.4 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.3 2002/03/19 13:13:57 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.2 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* General constants */ #define SECONDS_PER_MINUTE 60 #define MINUTES_PER_HOUR 60 #define SECONDS_PER_HOUR (MINUTES_PER_HOUR*SECONDS_PER_MINUTE) #define HOURS_PER_DAY 24 #define SECONDS_PER_DAY (HOURS_PER_DAY*SECONDS_PER_HOUR) #define MS_PER_SECOND 1000 #define COORDINATE_EPSILON (0.005) /* bert- relaxed from 1.0e-5 */ /* Default value for ncopts */ #define NCOPTS_DEFAULT NC_VERBOSE /* MINC variable for dicom elements */ #define DICOM_ROOT_VAR "dicom_groups" /* Possible MRI dimensions */ typedef enum { SLICE = 0, ECHO, TIME, PHASE, CHEM_SHIFT, MRI_NDIMS } Mri_Index; extern const char *Mri_Names[MRI_NDIMS]; /* World dimensions */ typedef enum { XCOORD = 0, YCOORD, ZCOORD, WORLD_NDIMS } World_Index; extern const char *World_Names[WORLD_NDIMS]; /* Volume dimensions */ typedef enum { VSLICE = 0, VROW, VCOLUMN, VOL_NDIMS } Volume_Index; extern const char *Volume_Names[VOL_NDIMS]; /* Orientations */ typedef enum {TRANSVERSE = 0, SAGITTAL, CORONAL, NUM_ORIENTATIONS} Orientation; /* For dicom_to_minc(), these codes specify whether we are dealing * with a "normal" DICOM sequence, a Siemens mosaic sequence, or a * DICOM multiframe sequence. */ #define SUBIMAGE_TYPE_NONE 0 #define SUBIMAGE_TYPE_MOSAIC 1 #define SUBIMAGE_TYPE_MULTIFRAME 2 /* Structure for general info about files */ typedef struct { int initialized; int num_files; int subimage_type; double study_id; int acq_id; /* Time of scan */ int rec_num; string_t image_type_string; int nrows; int ncolumns; int default_index[MRI_NDIMS]; /* Index for dimensions with size == 1 */ int cur_size[MRI_NDIMS]; /* Size of dimension across these files */ int max_size[MRI_NDIMS]; /* Size of dimension across acquisition */ int size_isset[MRI_NDIMS]; /* If non-zero, found a max_size value. */ int *indices[MRI_NDIMS]; /* List of indices found for each dimension. Only allocated when size > 1 */ /* */ int search_start[MRI_NDIMS]; /* Indices into lists for starting searches */ double *coordinates[MRI_NDIMS]; /* Array indicating coordinate of each index in indices array */ double *widths[MRI_NDIMS]; /* Array indicating width of each index in indices array */ int image_index[MRI_NDIMS]; /* Mapping from MRI dim to output image dim */ World_Index slice_world; World_Index row_world; World_Index column_world; double step[WORLD_NDIMS]; double start[WORLD_NDIMS]; double dircos[WORLD_NDIMS][WORLD_NDIMS]; nc_type datatype; /* netCDF (and therefore MINC) datatype */ int is_signed; /* TRUE of 2's compliment data */ double pixel_min; double pixel_max; string_t units; double window_min; double window_max; int num_mosaic_rows; int num_mosaic_cols; int num_slices_in_file; int sub_image_rows; int sub_image_columns; struct { string_t name; string_t identification; string_t birth_date; string_t age; string_t sex; string_t reg_date; string_t reg_time; double weight; string_t position; /*added ilana*/ } patient; struct { string_t start_time; string_t modality; string_t manufacturer; string_t model; double field_value; string_t software_version; string_t serial_no; string_t calibration_date; string_t calibration_time; string_t institution; string_t station_id; string_t referring_physician; string_t performing_physician; string_t operator; string_t procedure; string_t study_id; string_t acquisition_id; } study; struct { string_t scan_seq; string_t acquisition_time; /*added by ilana*/ string_t image_time; /*added by ilana*/ string_t series_time; /*added by ilana*/ string_t protocol_name; string_t series_description; /*added by ilana*/ string_t receive_coil; string_t transmit_coil; double rep_time; double slice_thickness; double num_slices; string_t slice_order; double echo_time; double echo_train_length; /*added by ilana*/ double echo_number; double inv_time; double delay_in_TR; /*added by ilana*/ double b_value; double flip_angle; double num_avg; double num_dyn_scans; double imaging_freq; string_t imaged_nucl; double win_center; double win_width; double num_phase_enc_steps; double percent_sampling; double percent_phase_fov; double pixel_bandwidth; string_t phase_enc_dir; string_t mr_acq_type; string_t image_type; double sar; string_t comments; char *MrProt; // Siemens Numaris 4 specific short dti; /* TRUE if we think this is DTI */ } acq; Acr_Group group_list; } General_Info; #define B_MATRIX_COUNT 6 /* Structure for file-specific info */ typedef struct { int valid; const char *name; Acr_Group group_list; int bits_alloc; int bits_stored; int index[MRI_NDIMS]; double pixel_max; double pixel_min; double slice_max; double slice_min; double window_max; double window_min; double coordinate[MRI_NDIMS]; double width[MRI_NDIMS]; /* Sample width along each MRI dimension */ double b_value; /* DTI b-value, if present */ double grad_direction[WORLD_NDIMS]; /* DTI gradient direction */ double b_matrix[B_MATRIX_COUNT]; } File_Info; /* Structure for storing the actual image data */ typedef struct { unsigned short *data; } Image_Data; /* function definitions */ extern int dicom_to_minc(int num_files, const char **file_list, const char *minc_file, int clobber, const char *file_prefix, const char **output_file_name); extern Acr_Group read_std_dicom(const char *filename, int max_group); extern Acr_Group read_numa4_dicom(const char *filename, int max_group); extern int search_list(int value, const int *list_ptr, int list_length, int start_index); extern Acr_Group copy_spi_to_acr(Acr_Group group_list); extern int is_siemens_mosaic(Acr_Group group_list); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/numaris.txt0000644000175000000620000002116512574624760021556 0ustar stevestaffSome Siemens-specific field definitions I found floating around the net. // Identifying group GROUP_LENGTH 0x0009 0x0000 UL PRIVATE_CREATOR 0x0009 0x0010 LO PRIVATE_CREATOR 0x0009 0x0012 LO PRIVATE_CREATOR 0x0009 0x0013 LO COMMENTS 0x0009 0x1010 LO UNIQUE_IDENTIFIER 0x0009 0x1015 LO DATA_OBJECT_TYPE 0x0009 0x1040 US DATA_OBJECT_SUBTYPE 0x0009 0x1041 SH STORAGE_MODE 0x0009 0x1210 CS EVALUATION_MASK_IMAGE 0x0009 0x1212 UL TABLE_ZERO_DATE 0x0009 0x1226 DA TABLE_ZERO_TIME 0x0009 0x1227 TM CPU_IDENTIFICATION_LABEL 0x0009 0x1316 LO HEADER_VERSION 0x0009 0x1320 SH // Patient group GROUP_LENGTH 0x0011 0x0000 UL PRIVATE_CREATOR 0x0011 0x0010 LO PRIVATE_CREATOR 0x0011 0x0011 LO ORGAN 0x0011 0x1010 LO REGISTRATION_DATE 0x0011 0x1110 DA REGISTRATION_TIME 0x0011 0x1111 TM USED_PATIENT_WEIGHT 0x0011 0x1123 DS // Acquisition group GROUP_LENGTH 0x0019 0x0000 UL PRIVATE_CREATOR 0x0019 0x0010 LO PRIVATE_CREATOR 0x0019 0x0012 LO PRIVATE_CREATOR 0x0019 0x0014 LO PRIVATE_CREATOR 0x0019 0x0015 LO NET_FREQUENCY 0x0019 0x1010 IS MEASUREMENT_MODE 0x0019 0x1020 CS CALCULATION_MODE 0x0019 0x1030 CS NOISE_LEVEL 0x0019 0x1050 IS NUMBER_OF_DATABYTES 0x0019 0x1060 IS AC_SCALE_VECTOR 0x0019 0x1070 DS AC_ELEMENT_CONNECTED 0x0019 0x1080 LO TOTAL_MEASUREMENT_TIME_NOM 0x0019 0x1210 DS TOTAL_MEASUREMENT_TIME_CUR 0x0019 0x1211 DS START_DELAY_TIME 0x0019 0x1212 DS DWELL_TIME 0x0019 0x1213 DS NUMBER_OF_PHASES 0x0019 0x1214 IS SEQUENCE_CONTROL_MASK 0x0019 0x1216 UL MEASUREMENT_STATUS_MASK 0x0019 0x1218 UL FOURIER_LINES_NOM 0x0019 0x1220 IS FOURIER_LINES_CUR 0x0019 0x1221 IS FOURIER_LINES_AFTER_ZERO 0x0019 0x1226 IS FIRST_FOURIER_LINE 0x0019 0x1228 IS ACQUISITION_COLUMNS 0x0019 0x1230 IS RECONSTRUCTION_COLUMNS 0x0019 0x1231 IS AC_ELEMENT_NUMBER 0x0019 0x1240 IS AC_ELEMENT_SELECT_MASK 0x0019 0x1241 UL AC_ELEMENT_DATA_MASK 0x0019 0x1242 UL AC_ELEMENT_TO_ADC_CONNECT 0x0019 0x1243 IS AC_ADC_PAIR_NUMBER 0x0019 0x1245 IS AC_COMBINATION_MASK 0x0019 0x1246 UL NUMBER_OF_AVERAGES 0x0019 0x1250 IS FLIP_ANGLE 0x0019 0x1260 DS NUMBER_OF_PRESCANS 0x0019 0x1270 IS RAW_DATA_FILTER_TYPE 0x0019 0x1281 CS IMAGE_DATA_FILTER_TYPE 0x0019 0x1283 CS PHASE_CORRECTION_FILTER_TYPE 0X0019 0x1285 CS NORMALIZATION_FILTER_TYPE 0x0019 0x1287 CS SATURATION_REGIONS 0x0019 0x1290 IS IMAGE_ROTATION_ANGLE 0x0019 0x1294 DS COIL_ID_MASK 0x0019 0x1296 UL COIL_CLASS_MASK 0x0019 0x1297 UL COIL_POSITION 0x0019 0x1298 DS MAGNETIC_FIELD_STRENGTH 0x0019 0x1412 DS ADC_VOLTAGE 0x0019 0x1414 DS ADC_OFFSET 0x0019 0x1416 DS TRANSMITTER_AMPLITUDE 0x0019 0x1420 DS NUMBER_OF_TRANS_AMPS 0x0019 0x1421 IS TRANSMITTER_CALIBRATION 0x0019 0x1424 DS RECEIVER_AMPLIFIER_GAIN 0x0019 0x1451 DS RECEIVER_PREAMP_GAIN 0x0019 0x1452 DS PHASE_GRADIENT_AMPLITUDE 0x0019 0x1470 DS READOUT_GRADIENT_AMPLITUDE 0x0019 0x1471 DS SELECTION_GRADIENT_AMPLITUDE 0x0019 0x1472 DS GRADIENT_DELAY_TIME 0x0019 0x1480 DS TOTAL_GRADIENT_DELAY_TIME 0x0019 0x1482 DS SENSITIVITY_CORRECTION_LABEL 0x0019 0x1490 LO RF_WATCHDOG_MASK 0x0019 0x14a0 IS RF_POWER_ERROR_INDICATOR 0x0019 0x14a2 DS SPECIFIC_ABSORPTION_RATE 0x0019 0x14a5 DS SPECIFIC_ENERGY_DOSE 0x0019 0x14a6 DS ADJUSTMENT_STATUS_MASK 0x0019 0x14b0 UL FLOW_SENSITIVITY 0x0019 0x14d1 DS CALCULATION_SUBMODE 0x0019 0x14d2 CS FIELD_OF_VIEW_RATIO 0x0019 0x14d3 DS BASE_RAW_MATRIX_SIZE 0x0019 0x14d4 IS 2D_PHASE_OVERSAMPLING_LINES 0x0019 0x14d5 IS 2D_PHASE_OVERSAMPLING_PART 0x0019 0x14d6 IS ECHO_LINE_POSITION 0x0019 0x14d7 IS ECHO_COLUMN_POSITION 0x0019 0x14d8 IS LINES_PER_SEGMENT 0x0019 0x14d9 IS PHASE_CODING_DIRECTION 0x0019 0x14da CS PARAMETER_FILE_NAME 0x0019 0x1510 LO SEQUENCE_FILE_NAME 0x0019 0x1511 LO SEQUENCE_FILE_OWNER 0x0019 0x1512 LO SEQUENCE_DESCRIPTION 0x0019 0x1513 LO // Relationship group GROUP_LENGTH 0x0021 0x0000 UL PRIVATE_CREATOR 0x0021 0x0010 LO PRIVATE_CREATOR 0x0021 0x0011 LO PRIVATE_CREATOR 0x0021 0x0013 LO PRIVATE_CREATOR 0x0021 0x0023 LO ZOOM 0x0021 0x1010 DS TARGET 0x0021 0x1011 DS ROI_MASK 0x0021 0x1020 US FIELD_OF_VIEW 0x0021 0x1120 DS IMAGE_MAGNIFICATION_FACTOR 0x0021 0x1122 DS IMAGE_SCROLL_OFFSET 0x0021 0x1124 DS IMAGE_PIXEL_OFFSET 0x0021 0x1126 IS VIEW_DIRECTION 0x0021 0x1130 CS REST_DIRECTION 0x0021 0x1132 CS IMAGE_POSITION 0x0021 0x1160 DS IMAGE_NORMAL 0x0021 0x1161 DS IMAGE_DISTANCE 0x0021 0x1163 DS IMAGE_POSITIONING_HISTORY_MASK 0x0021 0x1165 US IMAGE_ROW 0x0021 0x116a DS IMAGE_COLUMN 0x0021 0x116b DS PATIENT_ORIENTATION_SET_1 0x0021 0x1170 CS PATIENT_ORIENTATION_SET_2 0x0021 0x1171 CS STUDY_TYPE 0x0021 0x1182 SH PHASE_COR_ROW_SEQ 0x0021 0x1320 IS PHASE_COR_COL_SEQ 0x0021 0x1321 IS PHASE_CORRECTION_ROWS 0x0021 0x1322 IS PHASE_CORRECTION_COLUMNS 0x0021 0x1324 IS 3D_RAW_PARTITIONS_NOMINAL 0x0021 0x1330 IS 3D_RAW_PARTITIONS_CURRENT 0x0021 0x1331 IS IMAGE_PARTITIONS 0x0021 0x1334 IS PARTITION_NUMBER 0x0021 0x1336 IS SLAB_THICKNESS 0x0021 0x1339 DS NUMBER_OF_SLICES_NOMINAL 0x0021 0x1340 IS NUMBER_OF_SLICES_CURRENT 0x0021 0x1341 IS CURRENT_SLICE_NUMBER 0x0021 0x1342 IS CURRENT_GROUP_NUMBER 0x0021 0x1343 IS CURRENT_SLICE_DISTANCE_FACTOR 0x0021 0x1344 DS ORDER_OF_SLICES 0x0021 0x134f ST SIGNAL_MASK 0x0021 0x1350 IS EFFECTIVE_REPETITION_TIME 0x0021 0x1356 DS NUMBER_OF_ECHOES 0x0021 0x1370 IS SEQUENCE_TYPE 0x0021 0x2300 CS VECTOR_SIZE_ORIGINAL 0x0021 0x2301 IS VECTOR_SIZE_EXTENDED 0x0021 0x2302 IS ACQUIRED_SPECTRAL_RANGE 0x0021 0x2303 DS VOI_POSITION 0x0021 0x2304 DS VOI_SIZE 0x0021 0x2305 DS CSI_MATRIX_SIZE_ORIGINAL 0x0021 0x2306 IS CSI_MATRIX_SIZE_EXTENDED 0x0021 0x2307 IS SPATIAL_GRID_SHIFT 0x0021 0x2308 DS SIGNAL_LIMITS_MINIMUM 0x0021 0x2309 DS SIGNAL_LIMITS_MAXIMUM 0x0021 0x2310 DS SPECTROSCOPY_INFO_MASK 0x0021 0x2311 IS AC_ADC_OFFSET 0x0021 0x2330 DS AC_PREAMPLIFIER_GAIN 0x0021 0x2331 DS // Image presentation group GROUP_LENGTH 0x0029 0x0000 UL PRIVATE_CREATOR 0x0029 0x0010 LO PRIVATE_CREATOR 0x0029 0x0011 LO PRIVATE_CREATOR 0x0029 0x0012 LO WINDOW_STYLE 0x0029 0x1110 CS PIXEL_QUALITY_CODE 0x0029 0x1120 CS SORT_CODE 0x0029 0x1152 IS PMTF_INFORMATION_1 0x0029 0x1131 LO PMTF_INFORMATION_2 0x0029 0x1132 UL PMTF_INFORMATION_3 0x0029 0x1133 UL PMTF_INFORMATION_4 0x0029 0x1134 CS // Device group GROUP_LENGTH 0x0051 0x0000 UL PRIVATE_CREATOR 0x0051 0x0010 LO IMAGE_TEXT 0x0051 0x1010 LO minc-tools-2.3.00+dfsg/conversion/dcm2mnc/gems_element_defs.h0000644000175000000620000000470512574624760023156 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : gems_element_defs.h @DESCRIPTION: Element definitions for GE Medical Systems @METHOD : @GLOBALS : @CALLS : @CREATED : March 14, 2005 @MODIFIED : @COPYRIGHT : Copyright (C) 2005 Robert D. 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. ---------------------------------------------------------------------------- */ /* This information was derived from the publically available "Advance(TM) * 4.05 Conformance Statement for DICOM V3.0" from GE Medical Systems, * copyright 2000 by GE Medical Systems. */ GLOBAL_ELEMENT(GEMS_Acqu_private_creator_id, 0x0019, 0x0010, SH); GLOBAL_ELEMENT(GEMS_Frame_acq_start , 0x0019, 0x106c, DT); GLOBAL_ELEMENT(GEMS_Frame_acq_duration , 0x0019, 0x106d, SL); GLOBAL_ELEMENT(GEMS_Image_slice_number , 0x0019, 0x10a6, SL); GLOBAL_ELEMENT(GEMS_Fast_phases , 0x0019, 0x10f2, SS); GLOBAL_ELEMENT(GEMS_Sers_private_creator_id, 0x0025, 0x0010, SH); GLOBAL_ELEMENT(GEMS_Images_in_series , 0x0025, 0x1007, SL); /* From GEHC-DICOM-Conformance_DV231_DOC1143469_Rev1.pdf */ GLOBAL_ELEMENT(GEMS_Rela_private_creator_id, 0x0021, 0x0010, SH); GLOBAL_ELEMENT(GEMS_Locations_in_acquisition, 0x0021, 0x104f, SS); /* not yet fully defined */ /* from http://www.gehealthcare.com/usen/interoperability/dicom/docs/5162373r1.pdf */ #if 0 /* If release 10.0 or above (0019,10e0) gives # diffusion directions, * otherwise (0019,10df) and/or (0019,10d9) are used. */ GLOBAL_ELEMENT(GEMS_DTI_diffusion_directions, 0x0019, 0x10e0, DS); GLOBAL_ELEMENT(GEMS_DTI_diffusion_directions, 0x0019, 0x10df, DS); GLOBAL_ELEMENT(GEMS_Concatenated_SAT, 0x0019, 0x10d9, DS); GLOBAL_ELEMENT(GEMS_Diffusion_direction, 0x0021, 0x105a, SL); /* this field apparently consists of 4 integers - the first gives the * b-value in DTI?? */ GLOBAL_ELEMENT(GEMS_Slop_int_69, 0x0049, 0x1039, IS); #endif minc-tools-2.3.00+dfsg/conversion/dcm2mnc/doc/0002755000175000000620000000000012574624760020101 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/dcm2mnc/doc/ge_advance_4.05.pdf0000644000175000000620000254533212574624760023332 0ustar stevestaff%PDF-1.2 % 11 0 obj << /Length 12 0 R /Filter /LZWDecode >> stream Ѹ@6EA@T"FLgEhd.!C4J@1qI@(*Lfqa6 a樐Z20hy)  ҩy@b6M'CIs'1hi QDgBIфj1%vE[/"NS*XAY GN,։ ba;<7R"шaఓGb,2G lTÉXP.a)" |=>|!6Pa.L&!!2((ؾL\4CLҧD]׶\:Ȉ7p: 8*)b* endstream endobj 12 0 obj 544 endobj 4 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F3 9 0 R >> /ProcSet 2 0 R >> /Contents 11 0 R >> endobj 17 0 obj << /Length 18 0 R /Filter /LZWDecode >> stream Ѹ@2 E!@T"FLgEaf6 h\0LqI @(3a :- EH< D0p9%ˆuVWVj"b4`ԨgV`!-5p2 褞!29  p$K'G2d9l)c03e6X1N..CNFQ endstream endobj 18 0 obj 215 endobj 13 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F4 14 0 R /F5 16 0 R >> /ProcSet 2 0 R >> /Contents 17 0 R >> endobj 21 0 obj << /Length 22 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1> /ProcSet 2 0 R >> /Contents 21 0 R >> endobj 24 0 obj << /Length 25 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1> /ProcSet 2 0 R >> /Contents 24 0 R >> endobj 27 0 obj << /Length 28 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb10C$)`h?i n ;! # ˤ芋8 D:Cǩ b`n!p@$B" $N:JPbX=͋- CMCPД]%IьEGAk#JSU,QNЭ,O|&S`\&pnjFjX>bQ̹5ͨ+$J@/MԖKOGnܖGq\+9p/ttӷ5w^9WV5xeXl%Q,Pc:a6CYvjֶ- ț&M|(KJ'b8B* Jj}!%w޴vÏj1+WV-4nPͩฉ""9g4^wT[M)u%^u g?Ewa JuP*6,z܊ ~րIz( K< ;ŁnonvmY{u݆ҽLW=>MeUa"Sj$z~) r]lqDnϽM5w}xuhKKM$Z)e+) #RS[T?84i;(I]*,ދ(@ #OKOA6+fD\N )A'_aKBϚ:߄%O;w3j7ᛮ㼓#O*-IȒbBO M5Y"v $J?q2(V3:ŗz땡$5N(bC64j`h&Urk 'PoB[7fߔ:p*P@ʜ>2HJe*MTI5U$h(OYCDBK+xX Ù4ޜS+t<D9 #&@SE }<&ȞBaw=+i8hü}OM(p\C .dD@-b_Aƪc^DֹU$N*ek h0BAN4[kzv;Jpg+M"Z9!%' 3|ك Av4o3A("dRK'١N)yJR!:V ɛmI(*ctWNI;dG];k އYbpłU>yg'ThV;,PJMa JldܨT&BXC)s7^K{yPw%&ie4lMZ&GIT-@A2(nP_u ¹ܰ:XPhk}2g%҅ <J0I) "ppxmeylgեuI n_oSX5h S7'x&b@ݭ3v#*IfêH endstream endobj 28 0 obj 2063 endobj 26 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R >> /ProcSet 2 0 R >> /Contents 27 0 R >> endobj 30 0 obj << /Length 31 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>p $`!`P) ) (-""tD&j-,3L3AD9ΓD=s<ϓKO[*iDa\AB%`H/0'(Bb+(BKv޷t "b@$:.t'4[…Pe12h8q8G)dpاA4j}tSwi=^:V '+ٌB8L?/t5ObxP*M،ㇳ05tEF\Z0?i/dKqj]aV5kXv-:N&7ݡ[c8Cx%cy΍-*^Wr\jM^m*ZoɾXVUAd"Bf b@(n"0f5Za3MEt3KsKma[ߚ5b4q"ރJ萂!X8qg*0,կd~kZ;@٫Upum eنݙ0&^B_SA!}ֵ}Én^(D[8] )yh0^)/yA\#dYt6T{vOtR3II1'$pңK i.\s0F 1,߇ )\/ RYL#4T$ ` 5٥,@z !T"^I@o?lP'@ctU.it;F"1D)z$HNP\Q.2$h/TV_IdZtfY<[ib+]f urEה^pH7Y([@PJ3Ұҹ`%L -E.$9B\D7s]8 ͘H!#5vtgA'`(5 i(аuJ#%"g(:I-4$ΉXkFdFV S ","\/^vQT2\)WU(Ϲ$!vLRa3@q!rR+-,8a4uV{4T ".9z~p#~qNsu7&yy!UCUfjxBo:29͊%Phzpr2SlQԕ:rjIB)($V )m' $:Q),uDUYTkְyOKy j$ڻ0Q]Ę*dd(1+ T>TlfVիtmKՊ`e4^b/4EsA:!;KD&Ͱq岋7 FS-qw(MPj -|^!}7پ~*ZRi*%F"+ES D\/w턗*:_c2JY6eֈPSе(W")5KeIt3[[W&}d@ AE=S-̜=x>@NUJ4, ڽWepaI5 ;+T'j}\ oqkd urS{u>.`ZιK4Y;ɚ,BlR^}FǾsFl/;SK^w ,MlSK, 6;GfbF{_dLjMrrn|r3>1a\'.Γieu)[z鞯_Kz]PZoVnx p@ErJY1cϸf9`'I㏢~"5)Nz<*7` ճ_fˊ`Fn{ÎV"u@i endstream endobj 31 0 obj 2207 endobj 29 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R >> /ProcSet 2 0 R >> /Contents 30 0 R >> endobj 34 0 obj << /Length 35 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1> /ProcSet 2 0 R >> /Contents 34 0 R >> endobj 38 0 obj << /Length 39 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1Q<]!z$|7̗347.Ptњajb̵br(.$J^%o,00z !դ^ ZyfXe Buh@h«by@5>?6^r@FQZO,bi9trKϐOg?45u+'Uzjew5BR“.Jf 1I=t3d*T@QCOl?k9TIfmUi}^?+V׫l8[L =vV A_+ Q`u@XȎl|([{-n]n=K`n`'` 0Ӗ*Z~hM@fo!Kn{. D"WV`٘H md/#bcI!Q( 'h'٠/yWİ4})mH}bUPf6oE~ia9Hp oLu@i endstream endobj 39 0 obj 1800 endobj 36 0 obj << /Type /Page /Parent 33 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 38 0 R >> endobj 41 0 obj << /Length 42 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1i Ⰺ)-kb܉rݷ."芁'bFA %f) *M*0Qtzґa!$,(i0x:(: #7h9:3f,` x6#pu25[#%Y#=F0bYEMh ~7c@8g\'Σvl[VpWC4w;#Mg*MnPYc\l6IPIQ] Ơz,$d"쓕$Ra !;-x*IM%snZaͷe3 3NF'9SX|;=O$Xꀴe M7Qv{_搃}& 40 $:'SA(C|Ps'nT RnʆO(|Ae/8C!T)իBs -8#7mod}^F \|L&:{Mn`(In)lJm~aqg^cXGݒ\ktqޚV*)wW[4B-/֖!Vm>j|BM-?<mgZ~!۝/ϐ .za6#;]kuB" d!βp=#(PkEe:l £8yP"L1 (8jZi$ɨ b 䬂F :8pxİINnQ$Fn~Gp":#ENkL~:=`n*p/XiĮ Mբ.LpxdžıN#d*ϑ }i*5 'Jni* fi ˾^o(ϣ>WN-^jȅ v- * ;Q*lU_QK._LѦ}EF0j.ikkLyryg ͔jKhp\ nծvy 떨! (OlRq -n仱&**k0{+Pbb>{qf)lFr;Ґ1f㶩$*RxK#()l+rQ>+Rݭ%-2+lҮq"q..|2lifjvBzw"lRxZs.-3BzG!D`ܯzM5 lr6'6HOw/zRH?͈My|6 #C8ϖq$BM $ѰB |]Xؐ`,{z*> >RӒK?̠р>QfP˫A+B3ةlCNQ D;KI> 󭎠Z̢F%=;s9=f:G 1ԊI'iyJϵ4 K6ЭJOLko/JTb-;LF.}>fBߏ)m#hlS8j${֠B0<5Ə"H0(@]S54Q4"B &GPNZnBr.Lؐp|O(Wn\nne RqUOVɿRõ5Snr lbP[$U:0 $yjPm(7Zlrո@j8dn\^x]2B^ {ogUxg_8l:9\X6nb3vAAaibnv5 Yk{"^u4wQD!2/H_ihGghvrj̑rB6!.چij$gSj7AkkC6wk>Oy^<9;m,/g'ޏ~vem耀U$gc"Ne'TKVtd@4]J!RHn Űi Z]<<o*PT=jvwIwBw lZjWe*'7~UT=s5ztxt7{i|^ynn7^O5j\Aqu1RN &r jX^*Ȧ* kx>>Mz*yL7)syNk\3`;`X;N،7aKEs觅 NKOs~ol/4u9͈H7<+ڣK#(QTQbpIpq@AqIqv*"ĕrrs76l[@&3K<+$@**K1633ā(3լ'wn~璯/vvbp95vH @,~90ƒ7@ny{Ylxˏdk~q@|AC=|}ym;#=NvDYy9gYzEE{{.vy]ďh A9mFj#m1VJmr% j [e9qȩW$ +w/8F5t/bSP5C{ct6 c4^{v9;g[t{ E|4}%{ڃ;TK7eؐŻ'q^D+ХX_qkg:cbr{75sb* vCs3ŎUQBFwo[Xn "5?zd?Ogmc*jgOw92gW/܃@<sRp)܋'u|_/[X>zcļC[Jeo͚PFlF4C8ϓxȄ+ȗ쓆:GA#7HAIݯJ 7NE!Vd":fVN˽ t A^"sSW]aW zLD6(E|A5&5gv,%&] TR|t&QirO=ď2Al"pƟ܇? VB#joϻۛjؾ-*&%-[s!JQ נ܎k!j5(ȲTd۪Tc\s?3G^!u%3`>VV>n9i;_^D ƒGSέmJo600boH6q枾G$`TʄHpAJ[($R1/7<{ Gcd쫨s0u><F .=AR[ZVZfI8$dmp:> Ad?N@ 87@@P - "K(!"[pN^@M-"` @ $7ߑ@@@ b,H2EK2A )B) lT1!h7GhC##qpl 6Ln2 =hra"Iʀ41S%2A_1 u~"SƃiXOH 3mQWm"=.u@1Pi9 ѐIu  8DәXO7Ȃ8c F@("HdhT%"$n;Ȭc:^`syとWE<w%psru;fE?f\N,Ap# Rr&s2 $)p+-,Po [mdڣP(Cx:c6ml(X;-r$2(: #7`38Z3Ds |~ rH2 #(JG'H5sK2cH4rT'J  endstream endobj 42 0 obj 5857 endobj 40 0 obj << /Type /Page /Parent 33 0 R /Resources << /Font 46 0 R /ProcSet 2 0 R >> /Contents 41 0 R >> endobj 46 0 obj << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F8 43 0 R /F9 44 0 R >> endobj 49 0 obj << /Length 50 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i,% 7 o*(0!po$*EE /)Tns7Cm7N8O5xL|4-FmЈ4;PhpQB5 >hV+85W }F-=a`ʠ (Z*[CڨA4,%Iu3#uw^(a$'20vBeMPA822b$p>cv+c"%-p1#b-Km8ˡx@4(7CLLO*ȹ#p:ҊS/R!3 4<:U}$1ܜ0v0S~ѭi:V)9g%Z0hV;|2L4Z!dl `JUHGLΨRJt$#mN<:3Xv߸C^RP$nRY$qCºul`L H Z:ȼH`j!="R i"߁b 80a 6`|H03bmC`o =ΕZI a 7f1e:sp,CcBx1~䈭p6 Hv .-s /wT ccѤ@ Jӳ9Qru], 4ܬeNMq5Lj  $9_dQ3(ٰ5&xwk&4(m գ4“s-,W:#Kp\BC $D ٯ9X$݋Fqb Y3$K4Ƀ7<<2Х~k0~0sXA2C'f,#`FQbm$L8FX^VSr6Yn 2[։PCGdɘK(Ad[f1~C n @@4~!'jP]5*yƱ $ vV#e?2UX3%!w(r(|Q6&jյ5op<դ2AyCY)k[߇9oK*,%+KA@p Ա:ФН8 m2&%ss4C,jTUXmV*<T&UoMr:+oCN> /Contents 49 0 R >> endobj 51 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> endobj 53 0 obj << /Length 54 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1 !L՘@!Ō6#)m?P;usJᒉ{AE>@0vMB9 1(dN 6R38^' c sL @"cxD Mj*0bY=ZXp\C7\վ.2!6 "P4[8ۖ*RPAd a|<WȌ>rLȡ-A}_uզJ f چrTȦ܊ ~H&*x"&-. F!f}%9l]v}aeͷ`%ҕOTWe5J0LS$&_}?R|6'a^:Q&2Bvx(80mn+ X edaxj\!qUκZ#Um0>ڨ8lfpCPZ u<ؒҏGŭ T20 9T"LjW,8UJ @@r `Ok -j8fa50<0ЮC8eO92#0L&dL6IP \ !3(yVW v"+(w٩T%r# NY*d<Κ$hN TL;Cʘغ*c[H ((2ixҬZ~̉9PfyO j "XdD#,i7MĵC* !BtX<z' R1XWV%"*Yj&)N˜f $bk׫i*W I!%v.Y@C Z=|GZ4ˆ9 xRVw)Hh&yj_10FMDW, \K8̼,T7%*#p^6$a92zI]6kV d "X@afU:% KJ3̤4kj,w@l$A@ :#^ULa_ C$X\I$W6DrX[BTCX=!dsPCHp^*(!O9  a@o9qb^۫OoW'[aURY :(dDdho30:UJH-trK =BZ9x*3r>Ǵf0 Vrao7=ôA$bTpĤEJ T,ҸG]iepiT s)K)vp_@z!8EN!L::@ٟPNN6PpR[wk+Aw[Fo@w$@\n4jF>OGGr96pÎ*څ2~g̃J 4E"r%PT Z"J4PBW$xW)F,%Hh"礯*mXBh[l[n Z T%`g".Z@JFJeV"Lj}J+lJ+8K0&H׫UDdLDD ZJ"d!.N' O (c`ܐ@K |Q(pc|f?kdqSq B.dIaTJQGZkPP?x9tģ)A N T\ ƅlt@am̱b> /Contents 53 0 R >> endobj 57 0 obj << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R /F11 55 0 R >> endobj 59 0 obj << /Length 60 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1N6 AKU$ɑJY.O9A0 u6o'0IB $B$K!s 8P`7EDȃ$Z At 20-Kqug >LqH4\v1l͐5 LAwB܃gL0jLt4( Te:A ^gj1SJ JiA}ꄣ"{X񔆠d^S$75Dypr: Bo@TU5Y1%ג`t (|aZ \(m uPŷ" )"̀Np l1:Й9yNŦ! `qPRrqaNJ/5DBu@8:_>`uX&}g?,) VE&L< :斩}`7)1斨zre2~MQM騞"Ycuh5Zp ꎾfy~>̅ j +HIRj u6BPTaݸnS؛t| gV^$"EP_J]JT$?IABU%*Z2F"V9xHӺzik˒ʁruͩ \d_tG'NgM{Gd eFIJWUgl>ֈXB ̻b8sD" *iN)9;idu0t'mz`=r;5XDpKt 5ߏNmAk\PȞUvJ I76cG{"N\AJ8` 4^ %2s%iY,_w)PXPL8pCh(L;xǬ6?`PFRP9{uIV?TG.Ii7.rc*L6̉ uX-TķMoH˭&"*MO8h|0$O#\0m0 ZĚŬ lrND*gJMb0^LJ@$ ^6BZҏoNp%`yDVF @ q I AobO *Va*X$K P]*< ~K` ^ > 겕I:))xԝnFP~Vn\`MN6pm2wZvBr豔͢.@ 7OOOHm0/'iGIOvkiEo|J$ItQw%m4Fly6  扐bJpZ0ajr@k$tX; ""Oje Q !Kop0+F0+`rk0&#ql)'i+-\DvY b~*@@@ #`-;pfEbBD@S.٠p^P2 CP  WK R#m0 'R&hG#iLGu}1OD.=Q1& hbͭF'R0V; RR!](0e(*oI+ Rgxs>r : endstream endobj 60 0 obj 3138 endobj 58 0 obj << /Type /Page /Parent 33 0 R /Resources << /Font 61 0 R /ProcSet 2 0 R >> /Contents 59 0 R >> endobj 61 0 obj << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R /F11 55 0 R >> endobj 64 0 obj << /Length 65 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1qdod)PwvX=lV}iڶ:CM6/06 x~e/2 28[KnuX0pv] 9tA ws#( 6\3 ރwWvxna #&T1Tc0va^qEAG97hI<!&Gʑj*]׃6PA TPI02A`I$"&K-c:*_(Nء퀀 Cxv .\{]Α.pybar(a:nF@-HbMaZDZoAPڳa 7pb;xa@꼰@k +^!Rr#dyEzըUy9 5Ea; A6=r*pKs,闛ՈT0 ã0@Y i 춖ֺ(br3=Cȼ+PT 2.P .O(X"aA Cji٩:2_؁8.URA&ؠbSl!Ia"ck--taIiP8BNPХb[ ̈́=95޴s.nNg3G2T&BR\/Zy:]%ǤB`*V/eʚp[ej =\b֕8ӈjllq q-@ VKe|0 صf+@B4tr^dH.eG]њC:Iͦ$▔_08Ȝ:DXt9к|ѥ,\{;LIyUTlQQ&xtE&KX6G^x:KMP5M`ϪΩCPhFj-i"7%%maNjՊ֍e$׉ђ^h| Mp404%AŌ".0I!Phk@c1ƭׂzǑuRHm`xo,ꥌm>)kw-؇wcM6dH2ύ14nTXSpepݻ p6[hFUR+·J+Z6]vV2>D 9 P48'@Oz.@FNmMè8n"햞MͮFx5˪j–XǍPF *_E o ŜI|̨(&MhHf,6kFp `PvΩ*@x` ppz`jZ^ ¯L[4@DMLP"F K kzP l行hll AgrKB] PM/@kD!p1BG [OgJ䀪^g(`: endstream endobj 65 0 obj 2751 endobj 62 0 obj << /Type /Page /Parent 63 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 64 0 R >> endobj 67 0 obj << /Length 68 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NtV 8CkV(r@+8#G $n $:.$A'(.$*r#1% `"thÈ#`b^&A܉2c(;<!z b 8:4p6 #W4<# 0#+J*C'!}1MSrXusW brAKI3gZ+JN\CxA6w9TZ&EX6 87-a{}w66&' lV"ď+8ʓ6G *{{ں6gK daxu P fؤ7$>%l7:Z/@t>İX$L4V@82@X 97+q &J=p( sf치5qQ|Ƹ!b ) 坆Vz  BVZWY. !9n:֠t>!1$| WB HT$#R2.$bS$#0k`bFB3'!4Cj2w(@'\bI bk!潙n]6ř4rn2<ӔʛSr%HAW ]^O$ZPHc;pV$q!H@Է;U):B1;1ңM?͘ݠA%+ ͑$M&>ECP '$T:EQYDH#LȂ/@#R Tf-^-t)R $"8z5REy@.qJ>p]l&Q SJ FdSNd/ ! QL-A^C auрȴ}d*A$g*c0A7-+bTm!qYPAj'bi:AQj8o5=f5J-K$":wg(NA$% BJCJ!“"wC"o8I+5t+|ăWhj W(x@ @MDy?cb|l|"N0i" gIr G(s! O W}Bq"\#O9%$ǐp@ikmb09٬!\BYI%JCt#tĈ"2nLvr<}aD%i;.lWJ V )aU՘S^5/$Y!ꭶ=fxCmxkdJk(e\ui=9>|^AY ?m y^v>lmb]'k!hǿ{伛/M+I0ZQeưɚNDt,Ew;|oDkC$5r􎽬:TQ:v%#Gu5ڱZwj\ڃQX9^r7jnְA:im5ށ {p+:Lh2 U'ό7Gį6.ʪM^!4E% bʑ~0 ssϚ5qlM&o#vg&R3c/_> 2~y]48QC/W 6yyN|+Aa )>e_AϦFHK ן+KhD(D ~h*voNVf^Wؼ ˔o̔Ld]\ϾjsZO+pYT\&\\ H 9$r0 d]bm*ܚjnoeog G ivjO^IX40#ڷ#jZ b` endstream endobj 68 0 obj 2499 endobj 66 0 obj << /Type /Page /Parent 63 0 R /Resources << /Font 71 0 R /ProcSet 2 0 R >> /Contents 67 0 R >> endobj 71 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F9 44 0 R /F12 69 0 R >> endobj 73 0 obj << /Length 74 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1`BHBl0l90H7H4̳$7:FI* H,5+1(#rrPCj:%HA@6#4u!IQH%1GNRm,R4ANkjU}'J.!;V0D@4rMX5LԔcH[TkcrMRAUPЖNVjXUQ֥]R6d9δ 8.;J[q'>ÖkN\FeKSxMvFR E6um^T D:mϺՊ۾ dX@5 Q{ygCjK`ogrnk=w;ˆQA1ת\cshE&‡v]t_ DTjZ *OIjdy2JJJxd/zT.dݬ3(5sm:JYY_O+FV!0C(V?/N=G=Z5q5] QGCQV3b6dm C1U4OZt 8T~i"-/h)rs š:y3tDϳU11+0-A IMDt䧜,:r b"d!ԛ2f !60п/B I'NJ=e:'! m$΍:ؗt)͹W2YER7ά#cܤUN^p"|-ɹu"!՛,|zh2\RTjZU8C(tiY5 ̉3_,v FwPsWMdВȵ!/!r,zrqs0R 1̆CmiZ9g$'/] 9W˿<86?4]\+dB-+IVE;2g x, X(A Varu?>ޗI$̎X,Hj+ٖ]5C7m|I6}BQdȓ>E-~Xbtˑ$H:Jf> lÀDs.^e-+xa ^K=t%-SfgKRıh44GyXqDWtƀtWn,m5Wg^v#;H5K%;4MM\ew@[ZL}uӽ o*''(€˳ q)LÄSeL ;-hn.i`zKjܖǬYs4ca?޼ Ecz-D"zXhMP P`a|Y#lWܬ W.80JjG}|r?hO* LDc¯ 4&KƜdҝ&Ȣ,VŮf(ΜgJ(+an l3k)T. nF<)4PSɤmEPtp~ض^O\$QZHE"zxE n_ *% l!. pĀ":^ERKw\n\o л  Pnnfף=e S hΓHH'C%V"Q12z+Q ۮgMBC-]q5PSF' $aw +F|";+Qh wqw EgQ1zP/? oFʧ Ǝ1 &ejpqqg Rc yR()n)o .!2C?ET̴۫۲E=K270ӤgBF=.L*E(tIj-$҂ 'dk'm)F*)du$Bc+̖ MI, vJ+M'Rmk)H-\-hm J)%}FFU0JPm)1Pw"0i"o.( 6t8k7Bhߎ4$3E")o9 &i,Ӯpc<:jɳ(ĚV'P"҄ƪ2Ɲ9\))H]0-¡-ܡ pd2k6\Ʃ2&Љ4G3@BwoDk ֹtB'*G0tt&I,2Dm)3HGeH v9taL*[(T|s?0(2w"K4w&)U 7> @Ō<0*/G(?Gʤ@sr|'ZPbqUVґ+ pT&?JxsDKKfmZ@CL2~v5BӥG-SbFEmpnh`È4E8*I(Xp:U (gm,jNAvq}RNJl)7* "13z䩄I;ҁ]bHuMM_+p@(?ÿNPT),Ds+O'\ 7P*!b \Yp5v7s< es@twnixi?z챏ene9#{.8zQz$^ }M:F1PrGUIpmZD轴օ*ȓPT-iU@Jp݆/o2FU8{-B5~^!z5~Upe6FCo Yer)_A5A`@MUdS0w!0agtF˦T endstream endobj 74 0 obj 4069 endobj 72 0 obj << /Type /Page /Parent 63 0 R /Resources << /Font 77 0 R /ProcSet 2 0 R >> /Contents 73 0 R >> endobj 77 0 obj << /F1 7 0 R /F6 20 0 R /F7 37 0 R /F9 44 0 R /F12 69 0 R /F13 75 0 R >> endobj 79 0 obj << /Length 80 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*N , Pb>ñզJ f @GplI h[L:4p6 3 #p7!3qe,ዼIB0o/>Dot̄:4<4cx C(2#)\9P*:9@:S06 x<47Np4Z(rIre"+-EnҨ|R%(RD۱<I+GRS+d“+Tu2Wà:"x #7cH>Y0l76#8˄c9O F!=j;V x((78}aYV5ABYxşhQzQQ+eq4ru0]%ǗT!9Ӟ yhoNan5ʋ)L7Æh%in7OÐɤA2^Ia`@1FQ#C(77DjrHp\4 #V8WvcpU/2!\ 1 /c(. Ac\*%~Xw!?ʖCv?~pDaŵX0@$vᑿ ZPp*!PvbENoUT Ql$ηׂ\fq܁se]m7raҹv".r@VOpЋi -VÒIĭ整Ҽi_:84؋b>Zգ|+8 9~ u/cMU=yT Nф0ZBH )5'EQRL-5%kq*hnxF#،ٸQ:n|ypJLJf snuϴD ܉u1Hw$$ 39tr':28/Ӝd1EFQI`P|.x'Aqiߛ/ǃOYd  R:r80Q|A`+L3d۩䱀4 9ʫ*98~H63vA:!ht.Lg\Xfa4PO *jƩ FHqiY׀ejޭRƔWp)mPM~-J8gMAE\/UzL[C/%Su Q[&vU+GZү{{׹ߪvϪ'rx,2=Ψ@@Q0Gv+am̨f(xQ"g @˲4P#a5Mihť;> /Contents 79 0 R >> endobj 81 0 obj << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R /F11 55 0 R >> endobj 83 0 obj << /Length 84 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1T3ͅ@/3Cl84 #b8eq]Wp@"<7H2˜0 9 ^x6: 4<6 7#P&4 Rv+dp'6 #vN[o4dhA(GCM(8lFX1Qbx50hTȭB%eA0, Õ>bilBk H aƺS<'µ!r\G+%.pܮG!P#<^ ҆vp#]z^ݧ+w'w<RFp;gBIKu P"pfg\,˺A8K*\l*$!݉q yy&T_a̘*BC $yBHn_+n?ܾ oOބT PC9f1Nsm" TB|^!*. .sau*OGo&$)v)(I(axqNLDX_%d6K"ň 캑RDS,̻xbH2 p14xBfU7G侗[9G c4 ;*9$>\(]%K_)(nUCB5 4Jbgׂ'}FHlr D B$2z*#I(A3eJ9m#‘zKȣTΎYeE)"5S3Én^s(.**H5BJQ,W`R q9<'"mVįOX`,e\Ygt֪[/nW\u޼W^~: `KKb,M&0Ɠn۷N%oLf\I0d5t§څ࡟S% &4Ɲ4j3kj*c{#fm &ےq|[ oZJB)#ˌRd =M8QZz)q/Jdb u_^&~ ӵ&bb&eFSiIA(t?vA5ĵ,7aӌk 2k"O"Q Ƣ|ARS'-) Tnl"ZcBn8QNx͸GA$ftRhzIEGO"/;MEB Bbm(0"l2oïJLJW c>T琫 P2/:jI *CO ʤ\K1trp:N-VZr̩%,P0&5O>c)nw-s1 %2 H@8p-I5oQ.M",L-2X冐'ޱ!U @ߍk'ϰGIfp(S #3+ "P*ӿ5 l3O`Cp (v' *( iS4d+q"~J[\/.뮾 `쩼[Ϡ ܝB+pk(n(2e)Iq?fTxt上cNB FP2!/36X-?-x?q%*DscTDWtoA4&t/7C9G9K1$.S"O9-;=,=4IR5/I>A-jEHQT/#G4yT͐5JJ)SF>G%'P/"FusL(mV} ?،:g"T*1O-]PPQO1Y:ƂaJui*]h=uX$KG{53=/5攬S!uw`gW0*'y-Lt5KO굠 %B,C48U[DQ:`eR U4WaG5yg_ h3ETJvo;uJA]>wiXfQjob-ޔ4kbEJ*dLop_/.uu:h+R%LVs_wn0>=h^UQpR,5 V-p0!,w w%r6uhW$'QsR3%)s>ituQb endstream endobj 84 0 obj 3760 endobj 82 0 obj << /Type /Page /Parent 63 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 83 0 R >> endobj 86 0 obj << /Length 87 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1BCKG& ĔJnt,6 <G+ k2C)asI.%#D|>`b!] Ț#EN!mVsҤ} -K)~QReL-փTF&/FLTt>ӝHM%1ҪY.AZZ]WePjjB"2409d}D`'M0BLIHfH4}v& i3V ~<ЍTT.jJdu.iL`-ҽ\Ք2Ի&{.p0 <^dרM /9k"{)$ +f0ì C!nN6*bM ,k!~}DY%\̥trك2RLp;gNt $oF$ƍ |a!iE a(v6HJ\U$ĘNTF2 2 >Nһ8h1@ pq9([#8IG"L*9'I쇌)㧭r0fscR?-i]HJKJB 'OSXONU<7Mց7L`3`6SޜtQ?(ܭ; q9#9]#DU_e6~6;Dpx 9V(fp`CqJYmG-a(P KRPĔSl y6laB(lw18&aEq b˪+')KŸniz{hvV\%& {hܹ$aIRA8/Ruͤ}bHg7Uˢ4{ۭTtl;D[꾮[8.ָ"ܫ oQl(i7'}=boW =rk `6EF-)$"̺377C')!P]c]*Dks SÅ8fߵN@#x,`6guO%|l?EĞ -A 'oix P$X gCJ\F'@ERLfxŪ*/ʜIҋԝP- ԡʽ*5BA W 04/jT>Px9ƔKCg` gʃHnmjCdWbTv$"39JApF L3A%f**ڬҭdJޮ($P¡ZLb a :>0jk U P lq($ Xe  endstream endobj 87 0 obj 2623 endobj 85 0 obj << /Type /Page /Parent 63 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> /ProcSet 2 0 R >> /Contents 86 0 R >> endobj 90 0 obj << /Length 91 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1`drl 84< 1h2c c !0H9 7"h@0ϳAУ8 sJ*-;$29C:U(:c`z5T Mga3O9(t]UpAT x\ #@9:p: XҊU6MQ-f9Ecc2]d|:T@PT%^;#` (9 :HAg7 o%4h[ݿpvVR }?hE 77C<&tCHzguU ۉ6ASjv(6{{M^^6_;7tXX: Y`dwpe6 8&=/})=V @!⏢hڗCAO- sOuB}UshqE{mV`C^*z0 6W[ (Uh;B WWcN޳k U˝~6_-% Xꝗ:_:8LPqh;{a,?xW觙<'מSz\(=6V\bhg */>I 2ZHGDY+-f&̸3i|FyC?͔Hb:g@̨S0 )% \SA! Ě zc#3JP\6/`PZ;GRU)Tҵ1%Ȩç@rPO" -Ff^gjbm1%ȭ{Nv,IPC  NSOh?pX\ sfmE(2Д>SʔEh]KIdLIBSɚrZIx̉u*&egDTlM>@$6rnL!ԙ<[pI N%S#l'OTf`jT $@`AbzsH< BA)4K)+q*&ةL) ԑoGhTu&V &Y F.D'ljCh1: &rRmB᮳A- s);kCź 5 Pe̹ p7*WT]sv^}/|.=(S]g~[ pE7<qpOǜRݮ"~ΩX~!Ed2e\l%cgK֊V -2xHmq^ِkme Zw+(vrǻ3*3 ).fb6%8RsS)lެpz!@뿚/%YBMCl~/c6g5&Қ;$ xNA*b([`)])vNjAUxeYSaG.ʑO>C)99;sG^L\ya}g(YEj|ԃ m(bn+DE`4%n>LLҦoT4͜lCA J 䀠YHDm| <ɴf&x< ܦٌN-d@ `dikVo$`&A+z g@BBH3 D $n ,`݂2ӰЌ>䞱Db3.J9:.pBЭ=`XjG % i&(&> /Contents 90 0 R >> endobj 92 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 94 0 obj << /Length 95 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1ጇ5ت* (8 ;h92,@4tn<ށ3%0x@1J0HO#07& (YM ~M `AO77ixhNF (;[J*T獔@}{O  #u9 2Ȉ䗍`@;< |8w%65L8ej CHlt7Pmt-o{ƢX2x((7"m 9sX;f);#`٥s׋g<d~0ӱF #C(8A(Li&>ym9f@ăaƕ"bfd$6fatr+94Cyn?p`i i6(&_yUCljK2=ջ1]蕜xQ Ň.X+R)g5` h7Fuhm=uZcZ%&ZY:hx8Cs 15Gy[ r1@ B \9R Kܓ`Ny+'0RX#~ DBp(4pyH h2s% I ^O<ݻ@w. G"t2YR,n|1@ Xf_i4"\@ޝ3o8W\[ÍS8Lp| j'@d o-G&$2~ AgG39+ч2n' A h72| Ւ1f(_K 8 sl-Lfe$TH&NdŻPd2HLD}r֔ >TC.ZvF$آ#d Me `ޭUJr\$}}) JlR{VxdH4ʻ|ʢU$܅,׳w2WWRTKEnA+Ppe|^Ab)T\[g \ ?w"38Q0*P#>`uo&| `^V=]L/z|ti2 -rx[ aY_Kq~6cϑ tuwqzʡ""{=Q( .s!U\]˅'/[~.‡3<Ӎs^g,sDOb`%XGBĸ0*}=Hj._]-o y2[.V72+9*" !Z@'n)Ԩ9}vF٘Wa( ֘[>r :I ~q.*e=9T<i}G_ &#)d~smp_롑2"dHқ[w\m¹ 9w.׭Vl_qJu6 `Jp` ~p` poЈJiH<nRK <fPLϤ `$|?*\ @{I2Q Y${0*&6Oo//Nd/ XO`/\@e t%fF&)d > /Contents 94 0 R >> endobj 96 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 98 0 obj << /Length 99 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1HզO@+8U/3\H[)ʲB0c( x9pO4-xce9R aQC'51M !6 h7]01QKappGl(/rkp !+fF+QZv !ѯ0f8dbЖArqْ*LqNN ῶ4^BȀIEx l7j 8 :5Rb<ŘH^PQ5"RwtgzMn4G( m>X#m<2HLi)rA 6IǙOB/QhMÄCa<+(^e 70USn[[:ὨL).TF#ybNp(2C8uc$X2oɹRAE4c )-OI g/PON3>T1R1C<0Bor*AJ?=٦RznHӅ qG$d/P;Nhq@S"9f rϤuCOv}RNG/CqS>1b ܱ: ͸u a_*iON/z@ӸkSn1JR Dscu*rIt3NDwYzM+'PΩKƊGӿ$텲"2-ZJ-Ґ5'U}[sN.] ߒ"FmӧA\ =9BP"Jh" qe:SN^BI D/$6J\ PuRԌd2^ZM0ьr{vyDrk5](R"UHgxIct:V Ӧ~TGTo}{'I|uΫ׻U9ĸWMG Gce`CvxX 9lO!Jn.]t^t=0(:C \ͫki5ֿn͚gtg]n@CjM}IInCZait]>Rjl˪uZ?j<ƹu㶧wi t~0*>M3."RN ,"RpCȿZGnpP0dBBlJX -7 Д0L4|us NvNpBsv"ʶ#  jN4   p|2 d] ~tLJj$<",x \/vBA,(-0ޗ?oD zэBmϊ0MQ@qrv->lOa ~o`J Y<ȴpOho o2oMQsp͹L&Pܣ8P0". P "9Y#{0$EP *RQ p)r:M i$p'\_rʑ%1(%(p/$f0%n|*32oo@R2Ƹʱ,*Rں! Z_-RX.ꂇRG03'2| -|tF )2P3S(! eS 3rJ!SP褽0m4U n$ ( f7 jƇoL'2g9iZBBvDtu X2Ju*@@q"UW@fPn ER ƴ-4XրElj&o e\zB" z1bVeF#PӨQKCs@>iJKk@Ɯc4n,h͂ Hboh@AF4Fx}`aTSEhTtLn4J ^`CHR`APj+CRJ6JƷC|~ 'I/[µId!}JOCJyJK4X5FL3L5LNR8X NRpNhO/Q$$QP4Dx5SDlgQu05'V Vo0 0yJWYXLՎU5y MX5Ndh< -MaEk[L5ll%ɯZ C?THlLZbV"<Y@ Te>ǦRa  @S9SMP1@Jnd@kXg6f ƼdY>&J> /Contents 98 0 R >> endobj 100 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 102 0 obj << /Length 103 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`A,!txn1 7(0H78x:a r4㬶1 ,_#7cL/L6At"C@4˴ ң z>V6ۅ0H4 RMS?K42#O\RWc(; $7XTQT5P0SWQm(CbEf[ =gOΔ]H2=P2 0:2x\YVJDc,bau!J& Zn N|)ʰu,ul1L45Mt9 3bf9BHEQx0Ia r !zu-Tvv5|J6\V b8AO-ՙmmR%U:ZVᖼw7Az-lK&#撤"H/' @G +$ǔبymGWC0AS&I%b4; 7FABJE5wSlʤjI!sm9[Mh:A.dc1 2ʐ2^u{Y*OLwu<C;gMkDlS4w^|7y´Pa3m !.+47y/EebK"[aZ$t: .+<4P³],ֺHc ~'BXKOZz9OP)I@` {@&DP,]׆@0a )r8 09Ha0o 8":޺`hKڙ YO1`zOMI+|$tpH0Lj#PBJk4h1p 8~SjnPfAAfm:kTz/&0k 7+t5$oFhGBZ$–@T=(l'S.JSJw(2 J@&R|d VsLE16z@Ӥf6peVҡչa,=u?Ez `#A C&SiC3֦ j0 tԈZtTZ;Rwr9koqR7%\[sHaJߠ55I5׼QKr%2]@ lW~ܝšﵷD;{lʒ< RiE2܃\ "ZeCJ {;5݋avAʼ޻M#&va9KF8H}nO@4A_)!&|>tQe-3^zV/ ݽQ:F44pOnPuW.84I,3uFSW23 "P +ÐyI&Cy@MRW^-cM 0`3N;趲Ly%w(rw&G@RP9WA dV%0#9 &zsei|=.4؂sxR.ށa-?p+uiJR93^,qĐIW\0—:btxwaOy%,*3ɗ[#0+7;Y ))O_ 0.}a@NLǼ.Y&M젒<ݓ{߻ՍpY[~xGL"$ҧ7xh9#  7[rHmȵp\`nkwԍصާE9=wIݾҵ\5~2ѓ,9#æ2vcN]G0T'OM~C9'̙NM\L5l-؊&[i.'MrEU `@ڢ8 k$ Cɮ#DsOsXҜ8݇+`-0P LO&.8:.q nJP#J.Zbq K扇 o.  l; Gp%.!P 㺽9P.[cP/ picTq ^"&N%.N+11QH<N=o(/.C @;>µE0WmhZr# b5nqG8>a3Pm e@ 0Ѱi ᆳBlIDÞG^)0TD}0^qJ$ `Q& 1 5 , S0`pb--Ro -+ Jp.g--&q/ұ1 !:!21Qc,3}7:FKL!SI qV6.4/):gPdbeHGql,sS}/pd%XOHW%І(T ~s jpzG+teŬ!'H2xHK`e@ ޔlht#fN@BCq/|oDb9ˆ!6~S_VMh;aG⍨P(@)GRaHHgI&IJEw JmJJK*4'bwKQ(QmARn'T5 Mc-)NrZW:+kr#,Luqu3,q l RR)QS5Srw5tU)UWEUr쎼CFkXOx̵xxdoXsO.ubZ07z3uc,|zEv&ư!dhΦtJC3^rfx(_EH+s7 ZtIisy'j4!2@VCPIo6l΍$QeB^"C>lyMJdoQEx6w~4xVJGt? P%P#m\ʐ/,;Rqn$/'L6(Uσaofo3zR7`sq!vx-Q1 9swYsASuWETY'T nu7i182 *.8CxHs7y Ay%SMW5YYwlYR{*$|lH\l}}Q]3 C&^^IMbK_}%iFVTm,J(*g a>h#~<ɡ@3@oA8њcO?#%XOe%T/f x\y9^r4Dت4!*S^$TkWiru)=*5֯6HHk lRQt'm-TًщWnpYoT]2p9pNkG J9XrW"MP9Q"㾽5)SWZt:>[C5E)5?YC,{Xjd=z"3wV.m.wгW~G osXUz׍.}Ynqh2uE75U[hZQ7W{D[~ endstream endobj 103 0 obj 4594 endobj 101 0 obj << /Type /Page /Parent 89 0 R /Resources << /Font 104 0 R /ProcSet 2 0 R >> /Contents 102 0 R >> endobj 104 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> endobj 106 0 obj << /Length 107 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1H`E@0һty*/Ч-˲8Ʋ+`2 R2!2N "@fPP#4IA2#p7 ;և?t5@*08 l4p@! @& @+s' 2e0J27o9BVC*/R'B6K(M2Z-3cPJXJ*xB7ØqHá!c|ޯ@^Nên$<#B/*R'¡Y.6;-H40DE刀iD9&Gb6odJigj$5L$vbM顐bdd8 kh8nkZ@jmp:Jx GjlۤHXAz)uP]\9LHWc*]glpww΁ڢJOz?BH OȂ3j/;F:fU6)b@1qn0c$M)*)Hnf^(¨.8ь%CFz !!"i*Ӆ (*)@Ss !(t47t[}9(}70U*Zl%J.0 .X q/x^5؈oV@apEK9P 1[HȰha`o#T:R UH"AX "QA2e*(35Ta)| HҒmLCyo Xsb.qw٤ϸ d)) J]* 3[ʘ6&YKʚS4v!!6;qw3nTmJ\[&< m x7D ࠮QYTqֶG^ vuOF(U#x :R娣H= POy/29GCJuܥ>9t)ʉlSPCR:rT2A:)P3[ l-<}C8jz8PDp q# PL`U ΫoX,4sVi3g2x)TTTmZ@A d&EC |(J,d@rO@g%MPIK*tK#--%:Kr KKuy3VSbiHe ~_p@˝ -Fd9H3䒩ɪjۤiWѼ6/m7yEgEAapNsL?K]/xˊ##%c6~|}ͨL2.t,4$+(LIhE5d6zM!lԴy5A7e& o'Mj pOM+\dDzPUK eX BO1V]o|Ihcdb@ :_L 5BoZ0 p3 $ ˏnpc7$1 bP -r  `- \N~0\$ZAO֏y񴰱VѾH#dc ] b (9~S挒Mr&Rf ^D[c"o"ncJISVZ C&k&VPpXb2J0"[(M)Oni0A) eڢJH,<HХ0>R Pk+ K ?G rwL/K2Hw,!/.oS3P4`5 PpNOJwM4BΪ! ,S (BXJ t 8 q@ڳʆ$]1OT$XosĮo<}K S4YeV o8 W`Wγȼ KE@Adhk^hBP$D 81*a]bp84PtT'O|SdE'c(Ndհ44fvrclLd;*jHI3)fIRJqKԞYK ghuЫ3V{LnQVJq/Η0,jvS iZf0Qsknu-+lv.ҙ4LntRQSCF"TďU6HSt @\Ē8긅?9]Ӧ7WπJحU6U@Q5uu\2C<\7`ev|s+@eMQ7?{? 猀׎yy myrw*0'U{B{|ˀ2O|RUaBTaME)Vږ7PNG~cmGV0if0hbg6F3yJ"M+xhO,$ۚfK039gσOMP wn1Ll[RI7vw03}n.X,ŠBp<5Dū ` e]*Tf +8i6.&R/}uY@gͼ "~hzUr_2 m7T'KP޷2$h],#R\d ^t"XstBt [&8p|e/ 8|4;`q[(QD>ֳצEo Zz(Ab(&> /Contents 106 0 R >> endobj 108 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> endobj 110 0 obj << /Length 111 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1!,0K$D2gCE<7 #i#oS .6c9^0磫ʌ>=6/=Ӹ#6Ɏwb2 #06AvEnկ2R cJR|ZITۿNWQID,DUo Jhqc}~q=.p7f!va N%e&)X 0 ePZxo~/7 %Pp+Xp!jD]Q>ACG({ C0bGD&:rEHAЙJ&f"tى̢Bح1Y0rHؐꠝD99ds {C9}].W1v4 7! q/ FQDq6(U#E*%HgʸT|QUϑ4uA@N !2tc>QPBHD4NB y Sxc.!3!]UJRjZ84o+DdٰTU.Bpe Uy#JC9\VoɱO(,O[Ԕ Ɔخ|M"H@X&KJ cES2$t%@l . ,Tq*xbwnDQ^fq'f? 䏇qEsA Fb^3BX@$gDW&oZ~ < v%Gh#vwonL q'bi`0j&p9E 0 `l'HMV0 Aƥq:j@gioxrP rPƆF/F#dx@(m ~pq)k&sN h,gw Gb  qȣ\CCBd >Gbimhx0+$(.qsb#tFbfbftg|ThGoVgj $&f‰P0w&wjk1o, )0Zv1 uN@ f Q(k/(+QĈ\ei'GzZnr-19g; 1&0g-|pu!& xO EF| @w%"A3$`2`6fig 2lB`lg1`P 0 4 ^fov {% iz8 N5΅&]-r!"+%0S?W c.a #_l@`!ŧDyA5ceB[f\tO%8;XD R p|r8C,S+2/>0,@H yt\bTi952ѣmDvVqM<_ofgDu* j~gEXR2|(2`ucM#(bM){MҤjr2Jri%RRҪ(S 3G=l߄wn&Fԫ|ӃF'{KGA@ gT)#dFDP@F$z?:3' /B[h9'9c%v4FY@ƪDn QrRoVhq\g/@_a!!l0S@m5p > 3;Ʃb&sqm> /ProcSet 2 0 R >> /Contents 110 0 R >> endobj 114 0 obj << /Length 115 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1d9P@b[' :  @2##9!8Lr7`@:#1#`2͏(9cx4S`@4r: #B ($kJ*`!plG8< c(:Mct7E Aв7 #0@0 #H9ң17J?5U^1xGVp[p;T,(L34Vm` h7 5Dj #7T;4[Jx^ ;Fen`d&<)Cm{w\`B6[=3T-`3àK,4C.(ǸX;nJ3 ;#撤Jnzt cRX҆D%\@ejB`ގ E8,j 0Z(G(n 8 MkNy&fe_3`} 547yu,WKK )Wʼn|*嫓|P(_-&Spa|iׯ _r ].S+unu7PhiAt.lDvG=4BA`WA8E@HT.L2BAp7!@BPKD;2̙hJ[ RP:e2I͙9&ӝ3PY8|sdMw)zubM?4" hΨOB2/Sd̕- ɳm?2ġhcJL<`D9h}8xKT]3HII<ƂI Qb bjcRT )8Xw"Zd̴8بUaBlB%kWw+ջIk >p.bp3GCؙ*-`8a]%m5`ڰn:$ȉm_46!|=nDxrkPnw<5\6cyn0=mg϶> /ProcSet 2 0 R >> /Contents 114 0 R >> endobj 117 0 obj << /Length 118 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`a!ts@(1H 9 #7:9@03 #8. Cc9r* #lOr$4 3TAS0J* ,Q;,0s12Q!GδMB2Mcuh9PTAPu5QULdQ ($3 #:4U?c(Z7@; #`թqLLC,[au'N#撤"H/' @G ԀI1۱h@ b`@75 0կ :/%i\7M5RQ#k3X[8a5+>/$IA99O͓p8NP@2K0o(2^Bt \eU]F璘bcvL4M}kQNqV ԙ6#t=c6F-qܷ=u'w9J_6֡"e& οmYe@v88fYXULbգ7KK2_VL93Mq/faO# VK6W6CJvYn௵9 )#8wRQJ~tprl%C +ڃe[QLI2sv*4&C9=d=ׂ PaL!2¦z2[e$34fUՑTw=XôPGv*$Psh\Bol )U*[f:\;uq4ցM]e͝^ HGӑ( ЖïV#\[r u0iV9O #$L\@=ڹ98$KTrm"ps<0$ u<46x: +F f^PAjhk'+e( |V2ZF?gXfW03b(rgj4&>@R9bs E&d|HغK`q70g$5R.6)Q:wNi¥+!p)0 i ԊinIL2ùI5NUMM_Eu]vz o-dՕ,Ò6?Tlflyr_غۮR9VønUli܄-iܶZyPtmǶ _ U{KWpgt8^ً{!5V^y3ξQ`CMwrIAo7Su2xpÄ Jpk7Z ]J't-eV+x08p[-Ɍ%ђkC=ĕsH0)F ЇNjR,*•e-@1L4,G3 r0c8wAp3Z3>4-Rhz'xlMK9CI3]? 4G Ӻ{j-Hgx 9j=IsZ6/TwO+O}/$n, ޹ol*B`Ro/;B08໰$` X9ؠAf"M"t9NDo092Pr0Z} m >0`P` ɪF" q THg*>dL&ƋǪ DPLX`ɮFR7 HlVǪQmM@x p =b + MT֎tG,/FdJXb@kb`d.ho&P'b/]M0P ЯHOBϮe(OOEѾ)/mxPPo ׅ4/"R!, c q| !K!""@R/" pVPSP^MwRT0y$bЇ%#!op!L%r} K 2ZN@ClI&^!ojCJV = hlFBj 9)"àE|tRwso,$IP )J.`q؉K0:&gFĐ*7 K`T)10e3 X[_&a%rhu@Q2M 4e,L򢴩6 dKM@ %Zs8dWm@|r:O(3/2ȉB ;l;2w1)/E rg3O8gQ3x1! @ȡE{e9s4Rj22F!KTĸԻLIML)Lp?2qB ` jA+"t 1zCBfxj@#lf<>b`L:L_3`ބVl T$H#ahԴIkZd)gd=HNvl?EL!MEhꈪq!qjTe tu 6O* Gk"a6$tm6D젣MN4;IQv֗nJLpLo4NԋMoW q+9op DqcQgҤ+cY PB P,",nn2RV0*JX̧RfUDM&2d<bJ5^LVEkVsbX/"T^I] @ `xKtIEI0`LЇlDPN \ 4],0(WѨA)h? 0ryVWX5nQY+UjZUs3#[ [sY[\k6dxp]SlXq7U^d8ӐP3SE4óQ$X3L>#=sdIw'S1~%@TA6XN_OX}Un" )mI47 mhw $ԇiPqoixYF6G0pxVFr$>P$}!MlY-MrIxmtqn'*rIbxWJ*"qvyoFYiYx9rYW Ch=ABM=ot3QȍwV9[uuxfKw9wQ.I&Yz\R .~tdƣɆRT72cO׹ew|Oa8d]z0w}L h EcJVJHq#SgbNN\v)4 ˍ-#[fN*) nh`QژVGw07 vAwښֵzE?yKpOYS yX-N!C:2: 깚6!z+ׄ- KFZSKi{#f2Do-H Rc+S[7;4۴:!{c9WvyЖ;Ź;J endstream endobj 118 0 obj 4578 endobj 116 0 obj << /Type /Page /Parent 113 0 R /Resources << /Font 119 0 R /ProcSet 2 0 R >> /Contents 117 0 R >> endobj 119 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> endobj 121 0 obj << /Length 122 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`a`$n 1H܎ x # 7H4pI"!|<<봅Ͱh*0l!s8D@jkEnu.AxU5^W5_W0cY` rb5y:]obXaCnyfۚٚptEz#JȊ*m @<M(f^@5+i \_#o((稵˒La? -ӀBh9;"!?j3S_!(ABZIiY/%z_" VpPx4Iw2zA45&غro_=Q|2e]VI]-p }& ҖS㉜qᆪ =c H2?)h: l ϭI Cɏe F0cRݕ4Mwro!Wޗ}Dz(Ӽ^g4x+@ޝϒ( 1,YQ? fKK0-BP{JcHv"ڊF MSю uh(iux7>~OC((քĐG=ܸrf f0ǰcDI -t D9_=KTagX9 xT&Xs 3{4+p3',e݁6#Jz!hpTn$ Q>bm?k]x_q¾G#@^A,#|r'n8Q ˧s52)@JK`Z"NOH~DxTx-P o`^9u ob^$>p(bGːnq Ib0-LO`vi.|kP/ d/k8(ɰ$pr1 p"5$dBu ?JVk@Ĭpа*lpp>tP-Ёk5  P5S1bI`DoO `Kz ;`$0$ q<0A0P{#SC8PPp/$ 0zkۀ`Epe G ]bIIq|QrNG{V5Q[; %'QȇL+ hbҴFDg_ ʑ CQ *0QY%u #Ш:> /Contents 121 0 R >> endobj 123 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 125 0 obj << /Length 126 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1Ac`+ EnҨ{@#-Zd @cT BI b͐Dp@# 4 #9 j42xc: #xFaLǁpnGC)K#?p%j̒ʀl3K2n8ľ3+VSe/@i`Va܉=8 Q CMF'J5UtoPEuQɐMOqJSs6(Ca>ea؀hZS2c9xlx͚06cA &L3eW}eL*HoYݕ-~Uˠuf s`݊)& ú@;<.6#8˲7{彩;)L`,-g\Vyxpwru~iy$7Ml)P5EQΣ ;۹e%xȉ%9^rҦq$XVe8\皉xh0C;PA%;'hC  :,ƃ(n<oN;wN<Tx +ה\Cgk𫗣^%u^IDNpABto & u 9Py`viB(I D*C(i N7;3~WDJy1LGAs/h"2jO~9Fl<2BJTJu>lE[# ݔAr8<\2ȈQЈW@\˜hq>TU$DN8y#=6DNs Ha6(zNHR} rvعo@@B+ !lʤdʓ*(1k9gal&X-?΍I0p -Bn[kUKC)vC\j 0?-G*`m x*O_?3d]u(P9MCc[~SF' (29;1?iޫ#Nhsm}!lO>҆A Dh*tp!!(TEW"D 2Q^w<20UhVSb(ĴVh}&@ip;zph5J}JPXaoB+66N]" ~8?ܹi+1"F}^/q.I~؜ !v#^'ƾW |hSq?8w=>6~OAL(dY?!Ƕ0ꕠ@ԦL *@Lu̪bFD>%t 'J6v ޠt r^0(G,btDCBJA"!r+K,(W )kC f, NbVⒼ֐` Ȇ"C*\颣 p  PAXK@ *K题m 𘄰@# +j9fN0Nwd)eID' h!m!J U1["Ew8g碾) ` JMFI|l Ш׏bU69"f\,981*T0*Qr%/<ODq*L!1 @Dn$qORʨL"O#"nozGۮ$RoGFrO!A$]% %RΏ%&qr=(Dr&'/{'hM(Ur)rH/P9RVQ)O*"'L"++/"+’1'tCҤm-,2(—*S,1%R- $,#3220 nrn`#dhC 2q Fi 3Nz)."iV\62 8 `  j*jK`m*HTkh[άe'"s c0; GPHNO"9H`pHi+MK0 l,Vjʥ %Kl*9:;@ QĀ[?Ŧ2d Xni=+NP[] I\Nl5b`†*8< (j@DP(E? GJJ"m ['ljbaNJ+B9M>'T*kebֻ&3COJtLZ8f<#GTxtFb+C@`@‚:K`Ft W!uN%f PtI9JT@:tC+]S`Kn qKMIiMf}Hp0C@Z4QOShK̄" JtdP*Z(1G9" endstream endobj 126 0 obj 3460 endobj 124 0 obj << /Type /Page /Parent 113 0 R /Resources << /Font 127 0 R /ProcSet 2 0 R >> /Contents 125 0 R >> endobj 127 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 129 0 obj << /Length 130 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`Aa!j@71H4ah'( H6#(: 2ı-32:O!`@: Hң z? $,2Q42,4r4e2Џ(9cx4Ӓ]DCᒉ[ArE#tJKr0ͳ$4MSd8NR͑>@7#4 2:rY< eiEW-sGA cT%$"N[M G kZ,nCA-B"2`!E7yfymһZe O3U(C $H9|Pb5ATg2&"+!2C# Ql6M'h92lIWSIj۔-M2&:Z{ORNfVi AҮi]Am_'9%^C "jҊJ],X0OQeΪU'fUcNjW˄iA$|G# ;O^Qd8v ^M a (*CcMA֤+9^@9%tك 6 Pp7T QC בPSW|lL[9nnbt-=d>Dy ^󱇤þke\ ]xd2־)OI@BeJ[oH6kT$HeQ!rtDm s~{ćU-F|h~A;_ KMNo}¾֟{7`AG/ $mڍn"P @^"p$(GnL 06,0D<H,%R&m&0_L=^"梦Hp'nbNf9.l颊$Xi"t.bV'&hOi6 C P_)p ~Rsk怂L nذ(LF^NNja @hhޞ tl6O  /`$Oǖ ""9@@OD3RcF S\O`!Odejn|tXbrQ1TxqM^$:d*P.ÑȐ9ZӐobT"Q/0 6Ѐüpq" ,y ֲ Ȑl%0"#1+ wm rrH%0A$QJN]ORS!p^Fk &25U'(› 姺'@c pV0@Tǀ Î0o*]!Ρ-znNMђZ9AR'G (>f^ɨve;g$c\OK3gDJQQ@ A/V O.KvqozEπ3lM #Lݥr@QP>3"-\q7 9#3;L&ӸHR=!: 6;$Ӭ]/;9C=7͢ %oS&y:q4' gt=u4@7Bl9$rr<,6=V&@PH8:-)v(*âc&ˎ| & MĶ j$҂@ 04FlɊQ`R Q1 /N%~0%JB?!v.|a G"fHts,lԌ;3A]XX%>Vp\uV"!nw>#u85#?7@-W]BXJ]U]^_0=#U^L(fD`bte b Pc|+T&Bv$KPKK 1/}OIRMd9Md%XX3O141H[Lu/2{G4Q5OU_RdsiI6aSѓTql}S7_^XUmH9[Ykm\A;puo Dv0GeCe-r @>Dƚ/>yfB.NVu6<"P弱)~' gqtk`h#h%k0fkxw2lQVʇO7mhpY]/8VUc&t(v]7TxSx;Ӂq$X ]6"UWXs2=w8m1:4;U"!i5tdboY \mhђgm;y#'@n/`ڍO=CG=hGLMo >p=sW9]ko L endstream endobj 130 0 obj 4182 endobj 128 0 obj << /Type /Page /Parent 113 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 129 0 R >> endobj 132 0 obj << /Length 133 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*Nn9H2s0dR&d: #9]ʫt)˒$q5r&A@7,ֻ LH+nè: X@&4;?"ǯP88d.#xC(0ɣx ^ILLLҠrLN p;3;ϕʶKsETФ220p L R TX9 x9a34hYe8+x2pI%0Ⱞ3ʔjlCkM3C!!dj AKg=h8gy>ZnDک6tjA ?چcbXepeXfX m!h8nkzHjo:JکvHhGA*7i24;r7@s&}Kt=o5Nsu}3%<_ mBGA;o [ʙn0Kc5$& \R(2l5x%Օp H]^0Jr xEl%EP[,$H.XC!7vRJBeBYP \cpp05; SPM3"6יQ6 <:!MʹgnAUp"jTSCZ$t[mo9mt%*Q*Vなtqlf:&@><cc$4D< $trRdҝy6HI !@rNRV^Ћ2~% a1tUpڪ_a7%؛2*mVǔ#rzfpGϹ%TOA0sʋX yinEX\PIRoB(C^dze%.k`vtqt ӽYk lނ9f4QwvV!!h!Z,t-J39%sjv.z2ȻvnɼnǺ 0Wq5jRVy$V$VU{mSݔT\L'&4ȡ(~4(s:9Eٿ8jIwƼvQaRx=gt\'ѐH@ acɒ9(V&Le ( ԕRUBq>4bSAO-R4tl y/j.u?ʙy dzX][>`QL E>V|Xt zzmc])Skim#gxzziM w`nՖ^ttM!Z"]__[j܇[]^nKgC>m65ݳvһt\'qw,#1ore,r!,,F=:$K&,zhFcsS)}QrS'  YQ/F\pbCˉ(G¤ BIqG$0p% 69ˆk@ 3#' ^jT_ ң@+ KI@ U@$hr*T#$dLF,X ` IDR@  %_@@RT M)%^Ɖ 1*䩚 q_0FM>x04'|( -$"U Q/0|=LdUKS.FE % @~n' Q9F$ l%CфVk.~P_ўΆl@QǑZä &F2gZ@d`] qE,k,\Q'O  \ESО2x ` #tGv RĞ4HGsД!mK @_B2FK"2O(^_')~  %'' "\F*k1e@_// *C,}RX,Sґ W b,T/ %*Lz$I<[ fh Sesԣ6sg<2-Q rwTd&ի3d9ISv VF7yӾ\OVeQSvR<ɾUO endstream endobj 133 0 obj 3879 endobj 131 0 obj << /Type /Page /Parent 113 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 132 0 R >> endobj 136 0 obj << /Length 137 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1(2tXHdVÊZ܉l;JR4ͣ;#6^D7#5cZ͓HC3.u&74%)#WM[j !\'4&cǓVn#:Z3l;sXնJ`ao\[yb6L&;W9T82JeՎ`2Y9Rc_XRGR.)"ʼnIdylsZ7Z) b@6c]Ch3|96sH)ҊT>y@pt8@1aZbp6o6\2˒@\?~9VWX?hB#?4 :H&#tWm pZwr_VGkzxԻ2 #`RNJ(4EeytA4CUB> Ʉ@!_A P8&܇y < Be \f(S *-$#=}#u7 B2A`@'KݚVD4X"c D2ĴC*Q4hRL!w.) ̇#ծj d`rq&:Ȝy\ 53@)ؒ` CѰ-*RLDy!*RՎSI4Ɨi-ҌRkL1ȗg5eԹ%5mM5E50.v zz(fxMjؔ-86j'!umE_ cPJ3.ЧC3t%?<)g <$b0wd5>3TK)h&I3Lj%؁mɕSTŪ D"PH=#y/Aƭ֠VH52έNi,;Eq tg5jctu010!fl۝YCf;6XtH|$#>ıA2t 8yA`}%*'AT$CJ3m?F|a1|r[ξ U.b8g$8G#$,pe ݁+,BZj5M V/̄9X[Nb&r^"0Y" pP-.)N+R"F =0Ƕ^8620lPUSOdh'VS+l>Fd(Crh3#$k:Ebb| qPi _{b$l $d޳Qhm] FVy0 I.S7K\IaS)0isW#hf.8@Ee*n]09f9_pa)(M<=3)y䐱]ݖS2Xuc%ēM<ݵ qCW[ r'xVs 2KNAӃWX0ʘܸnPq9ݜN_jD"nW[d=\E@9 pJVlM=?zl!r R0c֡aJAaV}aqg.)I@IL MR$Z ܊0 1s "3!Jʐ,A4,xAB-bT$)@ j (n@)f\6j:Jj?7jG2&0$HbmކdJtuz ~_@ȨH ~s*J=Q=N[=P;B1S#@;>qq47AjɢگpLoB,"1CC3#EBRMDkdE%lpkx3.|0Sҋι *&nRtkkd&,: Iю&dq;Ti;%d+br1))t*M9,N2AωNluOs4qO1N18"3OMP2R7E1RUEECuNͰ!PsQ_H]UsFu;WjRJf);>r"0fnK ϱLӥ 0IH,)r~ _W@ IСGhJ*G\4Ƞs: XPfCS :?']-]^muR__O _lgP+KL&@]V] "9 V,DCOvgju P$PH S+_8vձ x:3Ri[(2\ |w9 (0B4ҐT"Ŕ A2 MkN .C7*smS56q\VRB1Cnl1Um6΃)qVSm@WrɌ&3:!b FE!D{nnou &W/uGv2_ iuWK%1w*vt7EwQ+W0F"],vgUYgICa8KdYl ֏{+i6iu{g)h6m &{7ohvki 7 HLu<̛Wn͐mX+mՋLP1>+3Ys튩o$5qR\) ")n=*W7$ׅ834woh"Wa+$Ƙߒ9?o=$>Y;qty[7Vb?-%L剗z#zoePrx|_|NFne Uƌ'W݄ہO,~#fV) xgv4086 d }a^c"YL3*v_V{SY-qh hip28֡ mG@ؙMX)GIle,ZKPS ߔZ_$8vz]R_R9N9"9Y$GCMys ̛N +'[%쫬 m ѻҭU*RȒ:$LaZo}?v@s>z )gR*y![,:D2zߪ$^$Fa b endstream endobj 137 0 obj 4534 endobj 134 0 obj << /Type /Page /Parent 135 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 136 0 R >> endobj 139 0 obj << /Length 140 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1P\NA IAsN4u?HT MDtd E=TN)J Q%ZR%\APSLX=%cMWaCO6`pY6hYu4/ Y51Tj*3ejғAuR5:^mQ^NS%~ݔ"%4zaiӒ endstream endobj 140 0 obj 752 endobj 138 0 obj << /Type /Page /Parent 135 0 R /Resources << /Font << /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 139 0 R >> endobj 142 0 obj << /Length 143 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*N(r@+9ʑ K2$@)! bx(4Jd7"h@!p7Ch7 o(: #6pQ6 hD #=;FJ%Xo @0c9 2Pb?W2!VU4VÊC@ h= c(;[A9 2VH 9 BMFg+[SH4-s0c:fӥq^h9ȕ}Zckׅ6w{b^#x͒SDˉV(C`b" -N6 6 N* `sU4:1B96ۧ  ;H|#t#Ŭ`ahdod9Ґjeד*V VyY B;^c+JN\#XKa\(\,۱qܺ.壕BcDRArOpv9eOQwUيTE{U`j7UU3[X)nܔ6e;H٣ w;0ާxwn$quWX*U>9j xT0HVB#!}ji5f iQAs)Uc l0AE!4(fRAo-`$ZyHHfԒ4)!WDk% 9cd^PtS闹P"'-~:Ff[L!:vtM([+m gVDF6`"n5ǒtસ3ȕXI96 !BAJdg!JY15JtޒUZEY"S*)K, V{%J^8|ADG NJIO 7vrZ^6ZrN5iȑznP:P(y?N:rItr '$ Yeg9ңg1w⳨;Х0$7;̟SڕRP6HprP){~t< B $d3vJLZ~Y{`x{ZL|ňp7WSL-Ą*SdMxEqa Hq^^.J,qmeȵ m5νQ]D.<3;V6:ߜ,uF1Qxɕ_#Ӝ즁p edq3೺ ך31Ҕejr$Xw\Es5 5tWBEoZe7pךn YLzuHS0L HBl&`+vQ@Vh' 5iRZwGN<00?268mm gXM -6B@`3#ց^W33fp}{ ?eW:Wv}9V[q8`Ai݃:VsW:>cBsՑϜEA#/t1l'L[#Ay2;ΆZ;%o네v2Mԫz[sL{7h3la:5ΟҊ{wu{5|iϝhi⼕4S20_,tӔdΣ7 r2ᑱW֖LۑK-B-S/&0.ғ5*g$&uNW; v [n{<44@! ˀ6)7>bABNRyN-ܷR &Nh8B.. r jC  4U<.$-pWBʮ)R0Cj<ISxKJg@:p; /6%0,*Pp)S  w +A c 8 m= &OXdOlUcBfN]hR[MwCeH$dHa ֦݆ xܜo蠯""F0tHU &C%'O(X%VQ&mq&Vn-ʗOޓØ0Bl#ݑb#iX*\8: uPļ0Qf Gpp*Iy!QRP! M#c/RQ=PjЊu̴"2a% tU nд2tC'=#&&/9$xry OI=mJghMH0iު',0|c  jx`ʇq %Q&I~ADܺd8R"ݦT l2_2 C@Q`j/`e{+kX4ۭ^ї-00mv^>ߤ~q3,>" )g0G@rvP q  ry93g88n;r'";L#/2<%<*E9 ;$ 2'PҌV>gs Ҳ& t T1tOr P!OB"sPP,ӯT@U;;(-R #'=IQ| 'SJ);NB"kV',QW NuwRWuV#UP!Y5iR"!CYD)+30JrE4#NkRk&5߈I6g=;gm`'GOn.\=qJ nBgL_a2 6m\;I/(h7 ]u8̽nAV4o #!Sp!p6@$՚!B+Z0Xp"bD+EIX+u F5-E.x۶͇#-ʛ]h:Qhb!5C7÷x;o-AQR3;q\ۻϹKOU[Nf K鯾:)U:k:ߘR9BP!s9[.٘ԔD BX— Q[1 ]`d~&?']xxtE#آNVfj4ٵ{_p*@p;oݰ/͛=tֹR}oAy9o}f yRCk@5h7 9!z-=/ ^7w䪜}8KSLx*9h,297=5?撜Zg­)akOH~  endstream endobj 143 0 obj 5154 endobj 141 0 obj << /Type /Page /Parent 135 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 142 0 R >> endobj 145 0 obj << /Length 146 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1H`E@0һty*/Ч-˲8Ʋ+`2 R2!2N "@fPP#4IA2#p7 : N\>AR4X<3˒A;O2%. 6R7;hPcV/*0\(M2OJKQ z/ "U^70cX@: ,;Ҭ&^=b a:I?<vOBGl;o 6jxS%QmbZ N`qPGe ;ΔXj{|&ȫu֐Zt-=>Vڨ8j򵶻[NP[c,( ݐBTp>8Dr݋`ko>`_h iQ SL*:@tJ&,I!!De R(z+5C CF$Vt 9)e0iNS$`9')y(&~4Y7Ʈp DAE 6 DAin( NDH#ܑrUr0\#%VW{` ^# e=W%{p!MC=l@( Rd-N(kØsB 8Y-6gr 6 ^[lΊs߫EU+*yV[PUuW,POL˙^iaC.CE֌\lt=?{`'~9BY>PnY,ۻa*]sz1|7RA/!T63ʠ~9(]^ o@U/SmyN6q4o:KVpCc2)e\ŠYcf{NK86D131w-Ƕ/, 쟍̥ ]K]vM ؒnk2g|(rlw~W hݽ;G؂ 5:MKj Eij,%D4F}W'-$֧< Kb[HHKBu i9^ԗi+ʰQAi'A9^$35iYMjO;"}D6i;[kO zn}.u| CB7tK4EwZB7a3݇O6q>C9%j\o]W'4OgF`?AthQ9L=.$sx|XPX?vMmxzPsXeڸCӽrvH1NN'>đdix$IG9{'#(TR9 cN( P` }iR BC~G.>2Tl||/($$9ˆi@ 3$~Ŏ@O),WExUexY\`eVxFfiDzLJ ?j90dCfNV] Pr_p'-sIe" ) Eo> `Y'QIOf"0I O 00"M[`0 AJNƒ"R w*xC%F);"C%#.]!6x*1V&di R " `\`f ZGJQGR(e^ QQMU  Ě"R ,KrU.  r pr@ 2. $a a-S01kxflJsRn2/Ps'-q C e^Y 1& U},83-I3M5  j$c)d&P4'@Ư X\rW=5! ĕ4oN 1d2y!BpJ89PHO"` #=ґ A!0 r R=:!:]; Y$b/ @mu]S_iSQQn]sFS-R9s<6E_;e> /Contents 145 0 R >> endobj 147 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> endobj 149 0 obj << /Length 150 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1&"4~e3S%s`Ӎ&x ;*3𥉚7LmA& _9^@82pޠ"k:G(}'*ni!PxA ѬF 4 )heIF bl!XfvBg нb/bG] dkX0<ބsL;,H KK/E_J#"~QmYyH2C.4F% \)Hn 9BBhokaBs|_X^N+# 'AMid<$qت|@B@m40Ң°R3:49/O Y  i !Є:SZGJpXP(,)4"nђHhjqFT>hA2MH)!9Lglƶ7զ4|NG '<靠ʛ .cfB'A%d ҅@TNg$UjU9QLPV˜C +ց_ 8{@'(JTN^u` 'y۔zH6!kg=:h5YR~"3r? )l]ynaRo1r;- q da4;.Rtf2.y#w,ˍJŦg,qAw^wC0w_koOW_[X{=ě\#<(f et \0C yEc,rИ<\̙3fx 3Njyq܅Ը85XjuJ[ q(R=SRZEt%jhu8֯ X:JIIE)ƴ7RHT}-<&eDSP*h. 4?&: =qUεNLf.xH[k}eZm(G sEfrYI %I~r]&-oVŝX`ۗ ϶j>mqr7M0C{8M׀"㐮0G|Ʈ uww97]rcᆣ#n#nsznCLe:o<x ݔVp3v'ɘ@9r.:96"h+E,l{`9@Р}hh`40P2Bta8 Jw A?D҅0[k:' l;O =n aG=wg6LhqPN~>$O@z'O3JH&Xz,lB_p*@ ʟ̟@-r P~ԱZSFlnhDM]dT<ɘ$`ޕ\IdlɘϢI UϰPLXƯ f`J.ĀQ,g i pȩ*&O)PPIT @ U a ,h3˃NeN0 Ц-6Pdjح jł@i 8 %ce 1c^)EDO c :@pw̘0Ibǂt No1QEBVdA,z'iF@@e?1jbK+qBN @DfBP~-Hm c`@ȍ8Iĥ03~f2͓"`|*|4kr2dC"2: _)씱Dn ʢqP8 (gS"_2SRj&6n8K PrO V),$I#,S Ϙ 4:[Rɑ< )rV8 fL @)0 oq<""Edy2䞩" @)o2$p>es2I3p2L;P!ST% 2 3rA7wj41E|='o3+02:RD:AS!Ӵ(oC "${C)dV0&ͨO2H M!rffICBOHTLщ|Ͳ`+h%- mJ# P \ \   vlP{ iv=nHv J^-P'T `Z?kB*D(15P҇h:õQk?Q52}:qHk43P'7ňgRKswEuKpMv ?ts8sKw p] <+OBHBnE($b-& ΁ Q@@-2/4P4UNXli%dL LUwGfOnlT~C 4F Xt0&*#f@ DXo/fPXm+!H*Q\[D ^TL MvMo@EN`@OiViiQOEzs08I+u!1Yur(fmSwxm՜ +nuqolXYiù5OrW)9RBH%w;1H7bA$u9o97y&9uer99vSV=r#]^!Pd An,ye& b"t lOme@Ȗg2&$M$Bt yY TOciEr;|"=}RebG~4/f-BC9N/ 4T 4Յ LoM )4ܼJq +֩n8ˤ&kq:$5alK)YgKۤNu{Xn>Z|Mxp1[.=}y0^CSvEyNWTӬSvѓhr:ѫ8CԆ@@a `V1-*OqBt?`;OFyqvVijU@ ,[dV~Ofqqٳś/:W+,OdGY~~[2Q*!6uO P ::"oVr+qEMm w-x. ▴"ZY#ZulVѪCt٪ A)ۨQ[ZbZpwxqBMZMn0c:N3R> /ProcSet 2 0 R >> /Contents 149 0 R >> endobj 152 0 obj << /Length 153 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1H`E@0һty*/Ч-˲8R+`2 R2!2N "@fPP#4IA2#p7 : N\>AR4X<3˒A;O2%. 6R7;hPcV/*0\(M2OJLn*zR P9 pWHá!lI# @$4spI$0뾅0 wA3ʔjlCk%3z!!a A#eegdlYٖLg ?hYwa bXcdM怅jv !`YrԺ~jE}AçnF6ΟM͹CG"ù9AwN)r|/`j N1!\g8Fq kјCj)= 6@XVTJ?^r8#l^!fiEl8. t 88^Y*1xLb`tk7i|a:nz * &EeaԨy<8>xC` a<0y$AܛD'Qêp2^VсC:RK턯T;ul%&XۖSX8c'tqeI!K|L y4儡Hn_57)llѤÚmf je7FfĺSQ6y{8e 5:&#Nf8Pg`-?(dp2lÅwʥ; p+l6)A!$Р"4$Im9fS 4fUY'>Eb qBo7 ||/($}!MEY 09(C,g 5#|qI)-H*u'o'VUwY Vl?_!ŕ!I To20YQEE&Ԅ9fdŜpKO"){@/>eNKxj]fv`ۀ\{t?\ ŽJ4wjQJFp L6Xއ_*&~|ygmZkӡRdz*X3J[ӯ"J<2یf3vr 9a|@2eg'0Fs&K7*{JYq穭*2Dk)Q\q- Řh3hIdҦG7z9Jv:g TO@12rkSH@5PJj :'~XQ 'ДkÜQ%QHI<\.٧A"f :\i+xB1:l(CAf5x3L*zGu]^F?R v8TRqvnaŭ텲jF0eWHpx@>ƨ9[H'4$O%YY(TC zZ!@vdoE ày%9^vüzt[,v&!m}܏0AcZ}GƔvP9)PS<<FGA0 +!C,.<)*G.ҒY 23#kS#=1.N0 3zp0OƁ䮒Ċ{t e_' gS$eb} (SvQEO Ru-1%hE\g|6 4@Z{ &mx(|^%R^X2banBm CXQM$ܯDE hHFL%%[HFGtz<[,?T:Lm^gԴ"KBJtk5 FD~bY*XԾj s*Xk @*(ZTb ȕ" Pn8`aM]tU՚d (auOJŕ`,_!V Plauad cuJ5(>b"r<# Ut J@ eyUVf:D0T*tVh5l;EJ[e 9L"kU\,LavrC!l5Ȥ85!mHY*V]ZAny,hco"lUa5BܿƂȢ #pupLU+kƻ_&^Nsm Y73sbVcGLa#St#Nqdsgt@phf$pM>2_tqzЭW]!x3xj3uMxxr4׫w endstream endobj 153 0 obj 3819 endobj 151 0 obj << /Type /Page /Parent 135 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 152 0 R >> endobj 156 0 obj << /Length 157 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1~OIB\.C7[ (J6'Cʑ6J^u N͆KԲ<~SbNAR*$9Q:j:ulݹ^FmOS!Ne\Ʋ=+Cr)RjK.7iJw+Se]eSuSZjrզէe+ZYiI !wc515@U}$=f(wQS>rPJ9XۤC( C 1x2Ԧ!€ 2ܐܖAs8'CY2 J0\`yB-)z4HԍyZ (,A1<Cqw@tCl1P CM jl I,4Ye'R1;LRexG ,:0ra3uL;"Ddݶ S @ʮ2܄@6OġsnyOWRY4׵}&_L8VXjP B(`ZP PrB &ϐ9$(Z/?})̡dxswUz!5I .`ȉ#xwp0yCЃ$1ûA~5Z00q~d- *Ci9*_VL$&e!'Pr89e&躷A?"% 'ƕ%=ٜ$#@ Y7HMF2+Y[q>IP,#͘09'ePigYs~yPlQ'Gkt2}4ZIRP`> hl3Ai8)ǔ8^y^ݔH h]m7|5kuà H6NR_7Eo~Rpr C1dC1AdA3C@,U)tθ7 B|ixg7}㌭J7]C=VE5̺8w|oU6j=k~]᩹A-BRo4HO d|Ш@z0g^r+cP&np*4V /*GbLJb =CĔscZ9v iJ,xԵ'fņRE09Np0 Z@ n E _gHN < DĠ k*\*/~H&E6nQ4'L kp ^=d-$+-d_\Q^N% RnO4M μ03$i;y2dTbnc``Z?tBJ!HlO1AygI!/A0^kqpP>P ! @p)!,$b!""A"{cVhY#gRyRYPg-&p=f}''PK"''*&B%Pm(=#(Hs k ' H  Q 0 º `bē,KߋBMh>֚#)dV0&Q*Q.-14-:@ܹ0, QXCc nͬ1uA1 k15QX$E_-*979:Ff"us:g;BkRNgIJ8U]m_+X0/XwcXոmvϵw Y}v4ZT)_ev *bU8E$)%rg{'FĬwՓ})}gZ)o7HV}׽F 3F{25lj`cr'K$Dh 'R P0TG3[gQgn>lCbT2T2O5i1kux xYMq_xvyWXGSHzѧD(]J@$y.33'Mr)t; )^[ z<rSOdؠ)O&MF72jm?9/0.蹋PSNׂ P`OCa"뫉s8>qihXS2k-2[m0̠JV'f([z80뗢 !bMZ R9+SK:n?[sS=w-@sh]g1h4Q}oHF$ڛY?ҋe%Z@>\[yǚ||zzש\S> /ProcSet 2 0 R >> /Contents 156 0 R >> endobj 159 0 obj << /Length 160 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*Nn9H2s0dR&d: #9]ʫt)˒$q5r&A@7,ֻ LH+nè: X@&4;(ST?MN@9pL#(8 4s;TնKsE 82#20f ړ$ʄ>ᔱC/6(C|934h[VH'# a:I@C<L`[nIVr_Z1(gZ6t0Dd͠kI&DBp+;(X颙:yZ]fǡl| %%U4]Қ@עH&r0z+"èb8$vC , 1Cm%S–JPy@  3$@GnYh3,K0̤xf T1@Cha 2@@rZa&ĨH!/ bxp7s(FKeU ֨㒃g>'VOy@ 1E$*uC#EpQZ.h;|q>Wg-"&3f,iLR~)9ِJrTLbmA8L8ezV KITTx!<%-,> FQy9ڿRj+;HUjѦu )6vХa/`̵@*NTo—;i>@׺ q>b̓f ʾ;(b+sG%j-0>kd?q[UelXXX6'VҵЭ9a+m54\F[ u֢45s7]z%n9:Oz?ДPv!9k>$!Xs#E0֧Ӿ,A\P7&xdVX 9җrOUj_bthiP;~g{?r69@dXTR  K%BwĸMb:b3%|P(_-&Ԗh 2d:@*Jy `M@ZCp9heyr9'TZ7pГt<7A-e_E!SKG)ef2qtO+BwpΤg jvg Ccԁ/E#HzaA'0XTVǿ"1 : 7Le$O6P'L 'MnHҞ:LklO5R(9Dpsu%7tW1F߁-_w仝0ξU<;o;{+$Y~K&%1@Ø_=7m]@޺d֜S:疫 2f銿.))idr\c!f;žΦ9s )-.> 9ChpAsb-:7G%m)Tft H Q$(>k2fm!/3 &=-It unFL6ľ/on*&N i 4VE^zIMm~ O@@ :f[N@ -l OnOep)>%<9t'"{͈M~8Z5 jMl0 "l(] 6H* @%* Z ũ>I~@@ Ė]l.3Z)hF,OB@ bD= Pͱ e’Ä%Z 0O.r.L L8kl$ۍˢzOhѯl.(O@ -EbNq\@@,͠l9nBbtCnm@I)Pjh[ TD覓@ZZ-~QZ^, / x堆` [JƗ T@O@OɜUnlEO%I@ 22eIPͩ, 6ERQ4BECQlZvɥb欢O MFajyz^b[/*\ng0(E13ޤ? AFgksLwJ`ƂKLH5&6o5TH7slXw#Jk+7F8쩇dv܁G)/Lbc'`ĞI$7 =2c)m 5d[xT;0(`@ L> :F0*C/&f iXN PtBt 0K8y~$z5dA7' !b4Xh`kVcNjT5Wpi@sD5zh heZ5fF5YffDUVSVՙHhK5YW7u[45ȍ\ӑJ8PoY4_aFWS_J_}^kgXc^ aswaԬcPt4ҕt.E$ o)%Xt":I, QfEfFfONDDP" uU؋d +W*ƊxHԷ\5Mo1jFY+ooAYաop{(aU5WhJ`nZFUlkn~utupfGb7/B5`$BF endstream endobj 160 0 obj 3937 endobj 158 0 obj << /Type /Page /Parent 155 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 159 0 R >> endobj 162 0 obj << /Length 163 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1(r@+9ʬ2$$K˝)n bx(4Jd7"h@!p7Ch7 o(: #6pQ2SH4*;FJBa@0cJ*%#%X`s&&* *x((@ N@ C7H2aH 9 BMFz١3£>] !Vt1lO(Y֡uo}x#H3KWXNrl.5Xܱ`Kl/ <Ļ#A, bhSa7Pt-$> /ProcSet 2 0 R >> /Contents 162 0 R >> endobj 165 0 obj << /Length 166 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NX;H| A(r@+9q(>D$!0Êx7h7l:-1qeዼAo0HO?=È!j"˕*1p2cRH |04c6<@: p@8Cx:!sJB!,- 38# E5i+ </2D" )-Ч-!+X-8  >Ftld+mlA(D}j\]+K \iL/X^IB7 2(: ˢT # n=iax9 #ä-x9?Q,9nBdmUqf`书˛$a56Kjζ򊃐7c09-0.c;AWC"^tBj#C1%=ɭ ҆hWaW2 aYUml2Mf76kq܀O\esY|hDkc.LSYn|/ƘK?vB8U LL'1W8ز}GFI1A,u:XvPujfԃwn-< q>9B% m 6^pf !:Fd.`# Sq\P:o =wV A8Ϥ(BJ;-P "DJ)U*xbἜ#b\MWJ ylr,@bV-՗#!1( q1Ufyz n90ҮhX399# d'p޻8Y,9"KQZUwe} Y#oKɳI{ȏΌdw]e% ֵxj@5JTInS2P^U (/+:Da4LGg<0c;k3 ,s44e4RǩDdhf`6єG}Ru J2 &WtTLj0R 2’U=U%ÕIOV'W2u4X$)+1gN=?2Fs S.nʋ)p8C%K@az 99yȟziXa)(yPe!ջ^*33j=YֹC q!-'Ƿ~I Gv>9@JĵGB)%C>gdy=ywi;}}K-uCxW^vho)Nig,)j\w> /Contents 165 0 R >> endobj 167 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 169 0 obj << /Length 170 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`% !ۅp6C( 0@9#h62 #7cH: #x9Ɓ0Ix#pH x2P44A20`ˋA c1֕Q oe86 I H:T}#I:@8N09 (r4@@02x7 M;Ѣ;)ӂ5=3T)dx@<.S{Y<(rKm.aV.4%U b(7@',."p BKk3!iѾYLBبSR V7 u4 6U:PK9uc&:j endstream endobj 170 0 obj 767 endobj 168 0 obj << /Type /Page /Parent 155 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> /ProcSet 2 0 R >> /Contents 169 0 R >> endobj 172 0 obj << /Length 173 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1X2Z C!nC2.0p\y8\&>3UI+AT*:1 / @1ܣ"iZ4"aL 5. L9/vheM7yK, EJƌÔai6)d#H 39.i\zpGY%Ԃ2-.#:Ϟj鋃&72vD\oHaaΐ c셑%3led#fuŦP9*! 6!ãalmv̀&N2k.2ޔT2>t 8Ռ [pGE$:C!FPUG$ H@,?@TAӭ8Y@e 166@x>8.о\ ,'VCaΊ/dS AQRW 3׸ +0hwa] s hz\{:rOɂwz!7YϬ2`4G t`)Bxk*Z IΆʼIud\(t"\u \JYiĻ T E^E x0BXX0 TSKK>_ n!7&{A5&8ClN B '۞&E˘:2&JpV0$2s4kTD(iZIȧ'&j2TI;ff0o^0EٙGϒF-TN==x1*`OR<_GaW^f Br y}^^ݱokEu/'yؽ;&e Ki9-q#.P4Sh +q- K4 =7Bܷuj|{73hsm<\ݤλ03%T<)콻g:p7pXJmMs+FPk qR6ڋsyۏF%'^;^Cp# ׵Zs]-5jC>}ޥX (ݶ6bެ9R?R`klP?Ɏ攙 _ ɖ(ɾ i*M* i mƪj@G!^Btyf+>lEڢ@{ ef}4-gH :k r@0f\pj Pp]mp3fdC]2\|dH`opl Sk$OG£J^ͶJ@F Rv: endstream endobj 173 0 obj 2559 endobj 171 0 obj << /Type /Page /Parent 155 0 R /Resources << /Font 174 0 R /ProcSet 2 0 R >> /Contents 172 0 R >> endobj 174 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 177 0 obj << /Length 178 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1tnۥ̢H2sj+woCth$Jmeh$9}fq~ϝvraauxS︃tQ:|FL&ʒ$(5=d P6iIٔ<f pGt"GϒmSddt !7C@Y+X4j k) t2`jQD֗tfI>R'y!ԆMDfř:B 9ޣH 5@YQh4`*GU{%R ###ji%a rHKh ƹ]Uz֧#*̦ ^( JIo$`CiJ1A!O!Pm`K)(RPC8eE>&I4[9">Rp bQ0ypcSr/†IRc;G:89s!}Q"m7c&כ4\F+D}h0JBK*V5 `1`Ij!U+u8^>a, )C; Zl>[eK^?bm45SpUb8s`\n&{fqAf*!;@0EW b<:4٥dFP0jPBWGDۙ|j`K[EQ(RHtJ%t0HPP 0nvphlv UIP%zA00 o ›"9P"1 X` Ơ`Gr  40"l6ҹ0!0  endstream endobj 178 0 obj 2748 endobj 175 0 obj << /Type /Page /Parent 176 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 177 0 R >> endobj 180 0 obj << /Length 181 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1D&\ۅӄ:N2 07Tc> #8 CQ@12 :6#=GU4s1*0?MbXSdܰ769Ȉ0ח8v`9cx46@;<_=PO-:]oQ!F-#!(+IB)35%,(r\00q{HH;3Hc"\.:#2$鳮SCHV9N6ovuvP;Wo-s]ף2 WbW\c79^7t' 2AhO\pvX@7]ySWUu:bOOTeK9uV:i57cpU ZfixҳV>䕴bS!EJJoiɹ=P]D(Jg @dMI`6_ Et$hbjT*UT(c k$ pv xAGk&[a ŋ Ã]87gxFkAw,leŴf% egf.lRa+.4Њ\A'A@sj1AHʋ I ёC &TaPbs}JYh2|VCe^J$C-"UnĬ?EW)X",GRN2Pd7Js1D5LS(`Fāl%S2i4(3!S*C'W:&u[Yh7!4=meH&*3<38C920$}"Q%h"BL(H(90a79$A c+X#2 fHw;fdPqpDc">qC#LrrNTZB A?SKa{!q^[XʊϼdRB(S"A?GD‚. 6O'i8ƢϺK(պX䈍ZɨZK]9x d9i ahsMhI!Vejy: Mt+Sq %ϑK14páI5<@Gx |?4dW!AqJK8$ܫCJy.\+ r͸snjj(%%s:Gt^9G]KeBg-kuв ER(i߶ëĸ @;)k\ 97Uwҁ(^39U}xL#s:_ hԙO3~z׵+m-9: o!XIb8 "۾Ǣ g|BLx\7|G~/8ɯϛSAJf6׭*O$B/n n9"`d?ČT%8ėpchHɮthInD.&3!Z0M/,4_VfNNn..SFx F0G0д!&+M|'PBZL XG`Q  oio(PP/PzqPV0 1&..In oAP1PBPK\A 㰩 %FMo-1>+Љ /;Ic߉0C&> :PHI fI-ܭۢέE8)7Ț*`B$ Q&h^+E4M%  Zu\j}MA`8  /j@c-(ldM"%ꚠk,VTWYkDXpnXͬ= E5Ķ9I2 'E%GK(jNVVt+ijP ,b< +Rr"H=)`i6M岶lPtXET%r@+xr` s1,Fopꀊ5r6D`4+`ťWS- vY ̷g ||G1go/H PK?[" 3(*B+̖6Q.;fc\Sm@ RbC٬v Ɍ\U5 '" <._ q [ye$,hRnA-#r:$&^5W=HN%IB -1:0\"f**@<#og&x?bV~2UNK->RδS*Ϡ endstream endobj 181 0 obj 3222 endobj 179 0 obj << /Type /Page /Parent 176 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 180 0 R >> endobj 183 0 obj << /Length 184 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1et>N}5ҝ!=MJ5 DFa{U{^HZx :Wpj@lpiOBX;hL%'X<)G]^٠bR/A錑Y>WP(prM[S2rJ0a;|[a6\35H0@b ;ApG ވAʥ >$L *exoU.i`|Œ0wߖ҉2u,ÇҒY> PBP},iamky.i_$|A~=s<ǂϹy$9a#U:>GaĔqp>CɓPĠR̞'= RMQ5Mڷ8 ``iueZyGGފSc{~S! >@dmqA K%m !W tF[ۧmԊ.|Ӏ#5\L޽a7Q)kOG1Ms0҅R1po{ܿ6߳JGsYCaf%y'H3?2lU(@B\dM.4fOA;ViUwBVi_OVTlFL6LB 4VT:)H&b( c0 U[c1d5&/P,?Qh!@5=en46mKTnKIGn6}Vvn6]sF5q4MqX-rV rn3>uk< ){m$V:,d8̮ntǷiew(ouRLP+gyv&vl'6z'82G0mf&I2vIK IXYk"I:J6砦  cuFr y8w Qi)vfk x3Y 졘Gr\uL4-|]uCMRjX"F&8oSXs33axk;&VI8&RJH3-0=x8RR`I%臃m.#BL5bdt@ 3LXՎ>mkEv Rb #v7~ 7MS$CtS*JDD5l9ՕVY`c<  A0H Mo *` O4V @ y `(@@ #J#"JNh${'@Ⱥ  | `џYp pܺ9 ̀ 9ܠ@b (Й9TрEBCs[tq]%BbhP#AL@C ߢ#YZ ˛ YsGEfUV$ 1N[`zzYeJPH0b0<`@`: \"@DM *Z箠Ӯ 4Cc\q#L*Z YzY #`oY} Vo4Vz c˵u:eD4l9FD Em@GYp ~[--f  9` > /ProcSet 2 0 R >> /Contents 183 0 R >> endobj 186 0 obj << /Length 187 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb10biݓ\\!#^^ ҃G؂[G@ |q$x;Ol=خqFzqP+ Sd7dT (ιJTg*YQV\&rJ̟\1>"!Y+f|e^3f^EMȹlPٱ+͏9l XDVvt !J{&嚵[' -ې[$L&q"4*Zsn~Tdu_=Y \J@piV,ڗkMU2$nU}- ܓ{AZ;9ڻs鱺y՗ սin;U~j(|r]^ᄚCqu|;bz gC?as7CYқs<7˧qΣҫp@rc)zIv 2:۶ga<-CmZihA9. @.[!G۳Ӡj)t.nU gd#Hw!!pnnN$M8.I0;L|\AoO5䷾m}8G=;zs΂3\) M x-Kcno7B|OTIS&}ƾVD9'Ǔ¯.V\5Nl/|THv pl"뎤L}z}](nOrQlL(p/(O)k?$B ` * 2\xGh Rͬ.|)s pSN*S2Ѹ+3e/2d2@Rɻ3 44$`nɋ*>펁B@}< B  ’@ '/8'V="b/1Pxc\b |/%% bK9ȷrRW ¨5] C &&r >Ki%lœ)r-)A1@Ϧ4(23AɏAi,)C1?B3qA1QC21]5TUDMARG-33DCȧ0P 43c)1E* s%3 JkI6TbJ1xtJH64 =W:\x3}88g4 sFhta0B: 9(xPb(k;n ;Pc Q?ll3JV/t/z|dpeWRD/r+nXBR"COWtV4EYj2.Yh"MY^j[cN YZ1qLG{\5P e]S ^0t"l4_ߪa ?a4yb;Lv'`"셉`m/MN4tD&&0 p? @sD=n"@ҴH3;  bt.Vu#g]{Hc{sU;V[>%?5d3mnk@`Blʵ1lT `DV!R]nunDa[ZE\qC\F꫗]tu5qvGtpt_qJ1WA+eBwKa6SaD[c. sSvOtԙ_7u6TssGݜK:ݜʁL1ntz;űD^9[%bw35X+XU5ruMqzoN7 {t)20k[h;;a P mieMiș=uUv69>lG=/{Yrρݪ콻у8)y}, rPC[)-{yyؿל$f5ɉVFKvMĶȩKRSBcfm2]5`u,KI3H/l&y|_;;EɌ62Oɗ_kL+Ȋ5mK<,> /ProcSet 2 0 R >> /Contents 186 0 R >> endobj 189 0 obj << /Length 190 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1òզJ f A'I fM4KB87 6-2!72A.%#1(aB], Ê@91H7S>4HNC 4CU41Uu#p 5PAV<"503 2*0?LY6QnGg03x40\AhdQoN8Z0#2>`TÈ"@B/a \,PHβ (B,Bx** BH*+8CLGBn4m=aYV' 6ۏ߲B0(i~A[sEa@<ٵqI#&A@)@#Řٛ8Z$'Y 1Pk]g@3OHo:g#t9Ӄ}9߀ %M,@L1P..;At>"VuG*@:j`;A<@ AahJ@RuԚ6H"#!D"(h!|dh ' |Gy jA7rpB"Jhq?hS]q6lJi/4yb:"R'F#4k(JH,&v;@pZ l`ܩELp7Pr>@䊌pFz"̦HU !*D)8gK҈6a)XMJI+ `)lLĨ1Z_M.a 291 $Kg,% =-KwQL)<`rҞI?Ӡí$BHna߂B#.;͝hX(w@YAD~HR((8TIQ՘Z&#I·3WϺ}sG?>L/aB-q4+PA*>Y$NL:įCk5;h}?DХ`65!5D4(fN*7s /b֧ƸrkǰAE RvE 9qVs{ N f xn@{&9>q\|N=lxH{c//_mGVk~{?{?}*۔Nl.rNvIo߮ѭFNO0+oL0,.7D㌕.MPV-^l\( :b;*eh9eր@o*G2$O>Bۥ FCRq Zo` Poj/gF ^GN/0 0q/lhϰ,q],;PBQ G]/.=nZɇI[;lqU) m/bq0h АtjKјБJ1*RRз?s&T..:/%CFmC1҂i0rECTI&D>ѽC2bFI34a/@3†o"e?n?nbe.'O/ /~@  e[[ 6'6kD 22CP͠6ps/V#Pj$X|P~! t&6'GSԬ| jhP>;3u3'DR I'җD5*S2SM;=5TST?{U5?gQ+i>RVI,WW!U%%U,QPlЄr ZD5C[\REuUtw[U+Te[5\^UcMN|Q"(+REa4"<"4Ԕ uI 3`L ԷaD7/FttٰHX*+ 4>vs68p I%9)i Q:֤$ϲmjc;Gb81k#j6~mvΊl0{ljln撿o}@XR-7XBYq-37%< N A['EQ073F1);E1t3%]–I-7I237;GtpPr#?]C hX `$vCLvS Ըb n8t!ObKg P `"_fs| MLxOPPu [(h1}xSr)~kwIkrwʖZ[_(;Y;&uv"I]ۙvƎK1ü{X T؆{__ $)CdeH/a;㾣dQ@`'H#Jİc/hDI`CHVN\^%cOdO BP_|Jm|Br&S%6S?5e|6:WvnU^@ Y[^ l]eUżZ[ƅuԂ&adE 3_ dNQ eGa#`(HZc5'f'GTR[$Dq:G59;Q'G:I)~k=%> YBW5_ʓh>I/ŷI`ޭoQc꾍)*=~瘔IЎ Q>>ڰW>mx][ȿɱkKP8!_ qT04~@4;*30C bPh<=9eR$߀ V6:#I@io|y4X 5Q ey$"EVb_z}2k7G8r}{ch<Obe* o: 3)R *R4j3 FBj.EA94@h.шԀd8 L "Rȼf7ФrY<20QvADICIQU`ڨ\8V%!ȺZѫx+LV$3K1(9SVKE]LwZ3t1!WeUl03s3`4}8ˏq R9殳N67j*xY]d+DD77jcδzbFnpx}wJ-Wvg z{YWϤyoӵ/n7/#@ϲN!!`aPШ1NbP: #(: f9B2(Zt>"DAbCDH`2xHd.rH qP,KrZĉ#"1, NTPNBB?:X#(9F07Zl+:!{6)XsDIa@< #h\2գh2Rhڒ7!4-SEt`4^: 9Ej: ć" 0ۥ)[n ioRx܁sjw[in]P^N5A^MܳU}0Ow^:++тʪ4]Ksш=Y c؆3M4cD9bөچjN2od-i9wn̹f,֜z橅-Z0~ѭ=ٔ4SZ٣i! #kgAː &@BŶde=G(Rǰd!HbIt(сŅKLsQ?Sez O=;KHnԮdؖW5 BrI7- ch@: GvΖk;%\)QG`7WdmH "_ p/S;:FXeJ'f WE.x 5L-0̓Z3@⡍e羁3( 8t=GR;s+4W:iڊH5>bD4MC5PT1L,Tn&ZIR ~JuYqG!!C`5Pxc ao`Ȗ%#P*Mh$KA#KSJn1HeOƈ9 HܼɨCX "4'd b_\ v/Ĺ;\JyT{r!$Tb?AUJˏOeXiV.ңwzK}M']8wJn{+p/NK7ޛ9Y52+TrjijZ) UYWeǛ`Q&$r9+Qk>$ʶ* Mqx m37HA 4C` nSPCAXKːRQKHBǒA-&"qoI*gB`T8j'SO2oǽRӕY1Oi(ҥςj2В*{ܓkgmcEW[a>Q-hh:{}Izt=(:Z"R5Fd,\q~\$qOQfoڀ_76m{6Ώ%2xdf7L uiERn&`"co wkz/f@.ô\tc]U1.]dx뤜OrqXv6pC?6GD9x8pϭYn{й?> /ProcSet 2 0 R >> /Contents 189 0 R >> endobj 192 0 obj << /Length 193 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1DF$*03 @: h1H7K53 YEpY]U! R풢R;7 : #H9ѕ*D!ZPd{FH-^+Z3%+(uXM,v~>oWTÈcx64@"(D yGG#$S>tO?%r@1*̜(>C4mAT'#@Rw)-!>q2C1!t*0t=r)ʭR+G6g,HPa-I/GeI4/+OIVIKQ/K $t}M"N3"͹;b@GTH%G>r65t ԏW?^%QY>r$ՋC0؞&E'CҋJC4KZE/)[AՋFu=UV,_m]/+HK/Zmo*6 J)6.Mߴa6)J^ LoL ]8CE :|@e8 & `NnZF @NB+N;;tU<   LjQ7`Z|8:.QU&A ^rlp?+/bs["u`Z)vo lL[TI[qJw\TH7\ҍc pttq9ms_4v6<ɴ6!`v+qV@,aucQuJJ3}aovj!d ^+M$23&'@I z qN3UsB£X$D)ORt&Do?D i]kO ' D v(U!)P5'E4+ou&ҕX2ՙX\ZRs\UT)CA͕aIg\ۡʖ/؏\ϻ`s+0Ͻ2B,/}0һsvxAc)ԝ;)N}SFIBH%nrM| SHz`m-|QUhiߩۗqܔ Z"t޽{QlX lxM*YHpESݬ{Tt)b@pߛo s<ɣ AߛrskOUwP31ߝ;ɭrej((WKҺ4+v>lq>v{?m4`~]b/[1 lQlO}zm\'&-g=hݝ=Hݽ^ZYܣe8s!yBK at3Xh}}?1B;_)/3>^U"2y<(+O8Cti>GdQB~> ?(_lp#[J.ҁʕ?pLs@jQ5%,s?۽w".Qȸd8 a\4FQ3A\7H$Pc1tv 18J)FP(t  aIĮv3J! &Ne:UN3ͦJL6cAӆx5H%" tIM2YkTBzw*gDۮ4iU:`PJ} r;Av endstream endobj 193 0 obj 5593 endobj 191 0 obj << /Type /Page /Parent 176 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 192 0 R >> endobj 196 0 obj << /Length 197 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb18ӕ->nȡ$'B; #pRd(ꗓ{٫{kk* !6 H}:<j06WXAqlXYD'6de^0U9R+4WBǝFЍAR,cXAD2l ɱW A߂/< WټXPEPv'ĻqN ʪ؍ `sA^`k4=6sFKq=H⎑$l+=d[q=;|E.^M*p y@0%~CT !tpdwiP?FXTWöL0j+Kn^'4w>9]@NCJrw L2?ORmJ$,r;FOS, !)EDv0Cr\yw-b oF,}U7-J܃KMUn6GS) XHRqLv 9Zcʘ8Z͕cJY.ax8EZj/S3=(x9ձR38zhe4$vfhLIő!(E%ݳenlY4V !:Yf[Vz_Fp].b]3pjS&)"UL|,@*ШSNl*QUN^':ȨΐA:婜:~FqlG| ޛfpqAV$o|A-}S|bl{XW/5(67~bx@͍q>;)I|=?ɲ-L$nl\޸#/U6Ŏ{lFkCǠt] zco^zgG'#h>|P{=OXdlrk$uށ3t%hg_:SjU5pn 5|̹kvJkP߶]%f3tdUHlsډt0M p xwɶ'[tNԿ;ʷoQZ| k'F9C(qq/8"o>'{%2?"Cg-/~ hlrosAkrrP ܊݌H 趟hOЂl[ ·p/Je~fhlp0Wh҄0zqHL.#0Fl\wHa nKOΡPe$'>.9ˆNdɾQT{P~c /1|:@@ PL* -z:kzDeU\>rd'` @1o1:?d_m5 O`ZO0$(8*XV+&bK? q T @.-)"*P[HQHV:IJ @JdJ$٭tL$3oNN'c"tOQkoMQ%UOMHQho'e.S%6T%E@DTM+Ue[ F%l`i-W%vW~X%X_%YZ,[=&\]]h]f^2e/ ,ZS Vs4f3h48e&$T(c 4$#672B5SBwE5P⇎>"6nA95Pʈl5k(γsbJ:S78(Ӊ1d '7<:sVF`n|1>z}>y~3z8@L]?/vA3GUA%AH=T>&UB-&(h+,#G`H+cEh)r 'Ez,Fn t׉ /(,dO2$)ۂCk)bID0M< WF#@ J+C%0†jpl›4SNiHNjNƉ;k7 PScSRS Q9hTRQE @o;.eA%Pj<9q}cÙEGkě\6;|ěC{ j)fki EY`eSٹUMG9f;^kC+V+zV.Z)ctVW,@QAeD zF ,V )3a_(` F dLs7_v%YAդbq$_vT@ Ig{H<@Aճ/mÑcVYWN՝ Zk =B$ץZ@ X%PRD Z6+VKc "< $ 1k|7ޯy&5,JH {^[ jm[h[WU{: %N{n5\4-=ɝpeM G5hy m^mǃ~>c%8.A]Bއt^xX}ɦӥhs|yl ^-YaWy`Tc"|u*`P<Ɵ<<@i 3'2z==S0aV.<&bE!:tT ! *ؼJh H  7Fl l4̧1NH Yxׅp"5 8RnT܍H Dx~NbCFc['񌸣8S1o; ˆA͸i h7pB #ꁵ2H/p"&o:ZpD3 LNڄ HIJ94zar;.3⣔4P9bx$Ø0͠]#"$؊ 4R)!? h&zgˬ.+ꡢ.ADԊTQ4ڡW3'5Y*е ^?aT^TjԶ;MDUWBOXZv%5b(ȏ0 v(h`^!\Rte|Q7(6l:WXBQJ꾰8z{㽋Xx`*"x ]2l1g3 FҴKV6-k*-<, D..!8u@Oڻf@iȋ> B5 A{>.#q9M$E`11P7 A r4lnc6wgZQ8Ĝ&I҄*n3=VbPX>kժf@ya*xzVW Oy7ETUe'qxb![}~uf6)yot$-Uȟ=l@޻d,rW^+z뤉< x/R4 e:~O\[1/Ƚ,CW!y4cR$Cy'eCD8'~W 46%P8gD`!DCb> /ProcSet 2 0 R >> /Contents 196 0 R >> endobj 199 0 obj << /Length 200 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1%d8^H f*Ə3VAƎcMdYo4YaaoGcP>X.ezA {)lD3Phl^n z,QJas-L9xÅ4Ti/ @ 7#Ґd3*"?FYpTWp%08j WqtMzA(d O+ԭdˢuA =!›- Ɣ}9Q$_vbHm6N#Dw4^sM*LΨޢyڦF^2;ފB1iX҇yO$SrS'N]ZdNIBhzi:U 7TjeUL bcPj;׹Moˠ'3&@d+cJ^ 8i 5ڷԇ\兮1Xa匯تa4B&!"" Z0Td gK|fi Մxd8! sLJi Á&rmE3Q}=pi9|PPvbz ^^B0 TL1\n 9Pz k;uo饦rW~H:(țUuW:[h4X ,о_p+e mk:MSh/N 0&("1T>qKvB`SQ̥ E0N}1ǡ[DdQVIRګ>2샐):25'H5/PeEzДưt]HlΔM b'ir!Jma27Ohʁ`!5ԴC'.frYh1voX-Nb&L5m_Uk Augzm2id}:I+ zrk7VܶM٤z5'xM۽#$y#kVZ=ը.;$&ŇRRL :~ݚR1^m -p&gs]P]keTUUsv]L \YiHe:&'<ȼ4NP̢ol/0C2]l:Mȉ-Ev;hoZ:H6=.߿rn4Yh<,[J#f)F˴ZWtnsuea֓sX}Y[6ctձ>c;Z%fھ,k{#q} b pIfھ$?{z?x(gs}O Kez5cd/rRNBFZ 3,bn?U˪cM)./ E`PNCK36޿$*Pxh0Vq G-&v &MPB%cRVЮVPm ̓P Ц*Ю OIM(`n5bNK1 4'oΚM(,p"3f-H qIMδO!d h%ctD00r ɢ KP:)0>IfQKN%\ RCM*E(I-) ω Q - (15 Y OE MR*Q /rvߒ/#w)?i?mP}%;7B&1Lr;v^r{!u%"r-(J'i?$ҕM&`Ҁnri1`Vk!\Lh*20ष v  $XE Qk^Ikp&-E:ӶR-Oq# ]NA Sx=/k$5`}$+RH%57>ϕC7&&t/M'O oU1ULvB]U3BVM'@D$yB6'VgW5u&Dz?fð0'RIy_%/ RNW/qќ$D[00Q NI%)KP]#],0Tv LqV(?JV)3{%d ncf9AAӚݖK:e30bPWv*2ae5 Me;Rje3$+hr{-e5Ah$|(O Y2!V7h k2(vZtv)wlUgk{6l;TOmV+Vm#wpV%w z 4pY A-%-iz56'@Z.QM3/iHI+I#KQ51^S&3[nd$; SY-Vj(D2W趂ÛydSk7פ$efl7SLV7(vi"^q|O_V7gsgw|S'|w|6}uIjVeontM3(M' 56[Alӂ8Ag-ms8?mn8?nd*=kH%  Z[XXt^ ru.\/&@E^n?윣3_uBuܾxʷx[׀vGmU/xyhnyN'w Ip {P8*pW{s8Vk9Wڂ~~wVvS;WژMXL/( ÁY\rxHIvDkx3@WYjᗈqB'Z3nt x,Ɉ1U6_< 4wv.\UHI0W?]ItiFQ@5sQuAJ-vߌ 8{,3׍Ax-xȘ6/5xz:DtducvQO[_Y"Wziz1<+~6ӧzH륙?l{hzY=iL^`pKZ p)A| AavXmZɠB:Ӭ ߚ۬Nzz㙒F%I,re($NWxiq@Zl εbV؎IjQYntWC827ZiNiƿ+G 3յreaszbe& MqZ(:AfzSzǸYd͹UZѺ[:t ʄ![ѮͻK+zyo:nVn+;.;z~x9WWu{X)ϗš4F g_% 3'vD)Zx5}ؓHx\F2lOPZ ǔ  +{hZq[yZ(X-CQ)>\>[!y$HA:yۯ͐x\ᦥc)ϙ<4-qy3}f/==#wQU{ԙ'4Q&'\ Xp|+=Ii\cׁ">@bW`]`ؘW]br ` $睼_+H<dȳuQuIm=`Kx@ k ;<:WɊlńl؆Wt $ ̀C -"">> Ei̬+$C7W]3{gΔ ^gm9w;} FA{Ii~h+hޥWMSg@C< ^)/E~/Bwb`Gts '2JVU#'/DKS-V+Fl)XTWd~\ 43F `evw̬lcW_оacەiߵ`_C:^>fcޏQe'o]?.!\4 q.9D`h`1 xHf5 !Pp'hT $DR,52BerS k'Qm 9S(J7 Q{Xf1Zi֊aFc1 \ͭb+mVU:̮9V!"{lmB 60 DS@b1iNAn:"yt2Άl: thg -n!uP\j͌)9hE{"Baqa@!b /l?ׅ0Ԇ6˜܅;rݏ"䎂r3,"xh(jދڷG GX\2 {0q p*I#PdɁȳLĝ,Mk+hsJN+.RU?KrT$Ow>е7Ӎ죬mT}IS2bBȬ:XRgU*ќ)t² Pq[)њ'W1䴭W̬s}_CPTjVEY4ږ{ YUum,FL)XCce1p7Ḯ8K:-20Kܺa A/>wC4Hp,')\շUb+d?IuPS⬟7JVZ{sȔ0uخzmCH\F*֒eH LҖƧ+R)mMp6m;1mW=Pq['2r֋`h^]+cM}Z\vա=1˖Cڽ: ]Twx]#s&^ avȳ6γuޭLlr7P*#(780@&@7anyCblUV `0 ɏ3@|6eݘPR!̌dI>`{趗HF9wE=lŠ (s E g|P(>p(N.!hxNl#dI4lVih}4з2j$P@՝۰P,6˜˔o#H24rM}۷ܒ.I ]C*'lFVa]O-OarVDZg8#q.紥2#y+Ljm!hɢ6V˕Oo:b`.ya*{wq<+C`(C 6,5L6&@ğ<'b\JBdɋn̬A]1a (y\ FFvz6z #H6yP]K1QM*S%(IU "cgrH4sMުz'ŬRKbnҮ_Q\oI4+Sn !w:zG,WzEYbf'ymB;>_& R jfɜ*MV-u{c JP샐hi' Cӌ 8r !Q{1%h!tC<eAmL$Rv)[1cbHb2/Ls XPOld8ԇRSlX)K[$eTꊬ"ٰԎ8^$kn.T8ATV'/WE-̰/{ Dkz8ʜ <)rdG65deP ʄ<o+IXfVrG*feaB.mTU̎y'f hpzȮ|^ej)BN6MlY/himYuFj|H endstream endobj 200 0 obj 7327 endobj 198 0 obj << /Type /Page /Parent 195 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 199 0 R >> endobj 202 0 obj << /Length 203 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1'[Y&}y\'>r|!x灯FKmz6 MsM6b` 9y:: CApE@f([ceΥ:m} ~Lvv5Agj!4تC Vt(V[䐼?N0:&gc_ }!zE)7B_bjV?J SB\OOK. 2[.C,S~_!0,^Oyu/_ 2xc?;MȲ@=pgY|H)(j?;[PAIE"y;if.4( )Qo2=&P[QV@jM*$V4GS$<Ҫ6FP)5fħV=P:V^.4jK\F0+V$tE֩9HHLt~ Wb[w|靣 kjPSYzd:ٗg#=Vj,:4ci,޴ N;3M2ҍ,YlI&. O;ْ/Lm`h2YO'x~KŲE8)ƹ c䍒G>R4 *g3<_[p\Cu}=sɺQڻ6Un]jNiݛwL8zg*̢~'fECt Ur a=`MbASrlPnE˾hto(E%% &p} "!CMY_ *z= W O 3sLo}ǴXcT/}51gWH*)>g>\2CPPn@bu+br!)u+#;t)t/+ʪc& Nv(DuQt4YkMIljoF\3u\uY]G7[Ѯ24_UZ>&ԛ]"tu^V4nOK+5*YLUe6TYZ-F#3{b^3 ӑO=Od.#9:V5 Ε6&nWeӼ %Y@YNu?SC NM*)Tɥ)sVbub(]Z8y0蘆Wglev MP] ~%zp $hJ$oII%t .:$fn>d[d/%0E s@in)o{YMMCbQlI+?bMu[Ob3lh$G3qu-/wS(wvuʖ;w3yLx{^{`NIU{47 ̴{7L th}.4Knt~Yh~uf@n}OضNDMQxHDX8L+X n8H81Ch^Q1xXW}G5~\ endstream endobj 203 0 obj 4017 endobj 201 0 obj << /Type /Page /Parent 195 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 202 0 R >> endobj 205 0 obj << /Length 206 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1طtsS4rt 4C?Y* Q@XDDH,P*3vu4KxЬ)Wof0xP+8gU7f 7 o-`. %Þ bPy=^Ţ)y@: #t9a4 4xBjk(R``d.s4H2 KA*eCC`lt2n$A 54q=/IѢ:RWef^\azoT8w4H>bz)b$P&qH[n'HkPE=  jQ=|}@i,Y Jw 9Xikґ5Y0o:I[C(q7" "A8CHg\CM(6!]_$pdߒPQUۻVTR^:qCHn 408XpOUAtOc>XOh 3CwshY&yRǯD`7q$)|XщbX74v}h'Hx4T],Ǧ.h.O7~ C TBfMcD: =8U`YnX ]"*YiU(]21Q"s5Je,UuV;:ZkwtR*G: y#'zJV-1GIc 2P崉jRLܱrY˗/$P1L0i1P0 >e,fTp9Dכ4mg&*&NCa9L<2psz9]^LYErR$>+Myzp 1Ȅ8URJڵô<F$n ?b ؆ة*f6%J3A\wÐ6,Ky!\e;̆ᇣg(;4:W \hV]s% 19fvvO6<{v!бYM;}"\iw4'@{ja ޥZ3 HQ J#/1jF ޙXL$^53o1ZBHAu;L"3j9k%Tځ4csN uVX3Èۃub&Fo8oJFwL9/cN$vO8[q2E97ٔ ʹ:sxBz&yݹ^s\^.θ W]++X-~,L:rNͺnPA힬) K6Zu=cHl`4p+%VMNMRƵa%7&n$]kE G؛_2ɹ7WĒ\)u|6څާ斋DU_skVz CwJѼ"|;Ρ;Rg433ZN'ԡ$'$//'5 ¶LmP"/lP.k MF̰( gHPQ.Ztgg0GlE;/f$Kn ~/zMTKo$ob򢒙L癩I歎dbĎ n3 MS ~f4 \ d +uFrAn*Q-"l ݏQ寘o . Q*Z*Kop!pOqL ʙ.oNNlNQbpKp ,@p;Ah .b!n1+αήfIJ-%¦S"ZoKX)$, ZkX elМ?dՇD}0м)0X? pfD; M &Kk/Md%rn sI*B ρ'h>ܐ ܱ(+R(o?>lC?+{@2ݓ3AQ+Lrz-qh2B/~.ɒBEI>Q?eNP-LPKFUP5*w pSGF̊3FEAHԆGtu5|X--0@ 97p Dԓ3!կ&)֒=8o4vfMzf)س N3O'"!%@&>'UI'mRtuRM?tSh@p2IC35TU4ŒuU4*/TTWT=BVTE.2U4MXTV.USE5FuFf1U?ZoT@չ\1\ս?]QQ]{4SQ\ղ)l6 4k8e0o R8P9 bDR*#\C,'OGPPSQC[%;&$=&2sKS~&g5:ZQzUMU-Up^hPjRj̄,?76WqXhhXG.YoDZJH4>(t~oğGH4I&JIpK4vp 4rtqMA %c0L~[p ЕMﳝa"t-] ic3P·u ̚C*XT)3x^_=QR3f'4D(EhO|*i-pwWr|j-qVr|ֲ4-,k_7lT5l+z׸F1yـv~Q~tTK.^f0@1C\U?3?UmsH[41Yf0=Q%/Z iW 4NSWo^ F*4bKb&7u 7xE}$8$F]0e)YVc@< 튀“(rK,߂f Vё01|m))ђw$oC觹1V4AXw~rr#)XU.9VS91(61nd$Mur@z3HJ=qoj9Dy_䙳 A^X}L&$Vo  I. 3 lOXgOZ 9"eN"0V1:wk ev&H8QyvNX 6 ȕʕYsGizh>Y?x992 d7E@Y++P7QDT}"kxS9Q-k.[}~zZv[Y'IǽHKAyqzCp Zװ{g9އxvY-7]R BN3?N8F!Nr#"d]-JcSȵf5wy yv9e&zVjd3 0w+YCY"lRKW[ŧ;®V ߽qojE9M[高YmwY]ՂSZ>h3ox6śzœ7?[ 2G_k\->)p)Pfͳem> 봀 !OWZ !"Cw;q"٣ ;iA;ǍX#݌xfL%7&@Z X ݏ| <8׉6^/@ & f f 祚]Fmo@dY%@R=(`}-||]Kub['sqj={߼C6<"BݩUh~݃ѐ}9STo.:ǖM/}Mk]kr~+ȇȻJS}',KY(N\ޗ<#D1g@ D @ ;^Z0"`k21z8ˤRSX+@n=ѽafFl]uջ !꽅߾\V>ٓ(/~JA{,lzjی3(nnI1^,'5Y)N޽V.'A{_%25(s;Y9݇nztqµ`P~KOh< K_B@ƕvrn0@m\xSnQ }`vY ߑ@Rqǟo[rhTB5 \7A0@f@2A !ȸk BLoGcpXdGDCqˢᜊ%Fc3-Le)=e8Rgrp$Dc1cG)Z.K* eKƣEdS.kU3*wWҭ'1V7,DmSeQn1gB"%fsI沨MhXjhuGf{8bpwY,xY 2 * 1# w 2 #x4 #0c(: #6/PhbB$,*>ks/ئ8 3A1:0 Cp6(ADP. <>n.1( mha#j"nK!k(P" rf\,>Oc/|5,-p:2 \n 0᢮R+rjM24 ,;<Ɇtp#k]>'kR5-U(S&KKm_.X* [֋-ޜ.V9%b P?IۂK_Y+2csEͺ4&iack }M"\ )0%blt3lb9CoTأOfPeRRLmHU U'E>nbʷWqKgoy+q()Ÿ[߽&WW6INt닔-tUYV?gq.RzV11a+[2TNYg̳rqB^a[#>fmo-ã%JROs6~@HQWOO8#>&)$Pr=^lnXiGrWFR #&#42݀0R2خ1 F>rMdޯVbvmmUюqvBwKi=M J8%(, &63C?iFft'Y69;\&'Z>atB~k8 vpcyUت^W C_SWGS{zؼ]h gz$nlݤ_$|Vn R8 )G !:P @C:^8vlf8_^3a1u' PjCҗ٥[e|?RS`/u3.]:mq5ߧW|ڎej3 9>3bd?KGJ?KAGx .*@U rۭ.l5l@,A2}*F` ¼9> /ProcSet 2 0 R >> /Contents 205 0 R >> endobj 208 0 obj << /Length 209 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1u2nV !#_c`AIA,ۅ@$Ҙ; p :"ak2^WA8W82;Y^Z~TAs}BVtjH."QPLi4 M)QČ3^Us` \Dbr*xX;dN*ExZAyFab8Z:ER&l9" m7xgn*#lEE Lyf~Kx֚h5$3ҔR&OGiR仆qG+^ x45dC)Lrj8Yz[Egb`$48bW42:X z@|t ʈ4V01$Z6~(BGЛ&N!Y@S0Lm띆Kę +4k݌m]\3䝁O0懄A=ȍN{gSmy:7m%ʮfqkOz[[+3}3\4 *58uܰ:3q9@e6uK:ɇ>HARմ 4"PTLpipZ6R·JL+fzCZ+dDL wN638ub?ΝZlw.>9!j^ *\ dr-/[_="IM$3>9(~ AS}#% Rrn? '@:@ %UƂ 2 ROg"د5s :j>Gt8>8d @C$}sB4 TVPmMnFbpp%X;N0J ` DN U $Z*Hc`ZjoG*HITJ$Z/+èID LMU$M\NPDO *OPQ%LsW*R+o,Ie4S !I5MIcw cpn1e振cӌGffe2k@3q;4pvY<`2ihbg@x .6/G{p'˃k$%all3kvk+mn-EdNU?GKsIL~  @ UF@Lr "}@,⣥ң4|MeWc?UhNuZiMrS@WHX6 ZT @J喁@z܀ sXLY]V)'@ x=`h ^]ÎW.`[% (W|h 7&uLe,t8nv#b.$3ccbK4p_V>Tjx9eQ9߀/d3eǓ{j مshf6j օ/pqmEkn?8lG jIv2l98so-_ =g:@VSwI -Yl"D oEr*+K+ wos229[#Gjpc[mUj' H1F&b9[A2eN$!YfⲒؕrᕙN-')X7yNjÛ+R9s'!06ݙ x2.3m%B:΍BoŞOg3}ͅmXo%<ťn9AD`Aq tkr/ J/J~ kfoLG2[m-$%zY}Wd2wׁq HZY0X]f1lڥN"yYwyg ǬkGWkz٩d:] Y:UkL;g:oŲqS^;1l;2C岙k: x̗o:,$XbW@ 6Txɤ%0ّiE2:N",*u'@u5Yvz3l*=t@ E z/+`KZ)3h{8ckH1/[,&yr]q9|#x55C+Βu#˰#M'=)&Fm ֻWOqk,><}9|o98ɛDaai}#LB Zˇ ?EI}0^6~ qtj]Bm Q}eO$w3އػl`so&+V>^O8^d}5 r>-P6L/`eɳmIxM)Xv]]Rk%?fY7rf?j%gO8?_[Fʸɇod06"pT%Lܴ L#8hK4CgBBX1B?CAZ% @ !n u6E"4Ac Hx0à1(T;H,6 Q X^FB21- =:'iB&Dx@f.%Q@1&pQ81J2ZwGEDf6wAŢ 1Q 0G4b3Hh)P@I)ʄO"ib PR$B)O>ўi/c[>}I'Cb: qB#IT:8ua3:qW,2BҔdןF`7.btlpP+ C|@j:К`21+oްL:pkC.2Z/tNjqxGϴJ0A$ps I6j#!!Rx]!J3D0!Ĭ˒rA5p9Kr*q/ҦKCiDsTBO/ⶭucP'/ߌ}%G5^ q[6]y_Mc81~!VV As*G(Z( 8!b=wX!.$: #(:{@77:zv$a c(:q iM\8Y-XBRU쯩 dӋktć )+ˠc!3/:"-  cp: 1\w-TX$W0]Ys }uE6θ|BW; te`T/M`Y&VJ#V5\cָv1wHZݭ@f_dύ.8@86k*b ;/tTs Ǵ(`El400͉*!*pd,פ:d[-ud׋y9'fHՔ A[(lm--6Nۓ?;䑺, 0omK@!u\B;0$8d 7a }MF!%RV EQJ%9\Ftj*iHt^(JUb[0#o'fYs:Yus%fw<=z@(1ܭcboK$RD%|S(N$KeR1 0O@OB`-{K67 ̡E2*رzp(F(_lA#=UZq+dd1M`2Nc Ci Ṥlnr-F #ٙGJc܆kf@3Q !R"D8]]r66gJ*kKؒsW`z]>M)ّa&Zsq̲@$X -.X@66=g[WhvY'O3Ei="ʙ\kˀ>C$kh\H?CWAôܜ%UyL]P"w-[+Eۺ]BW. ‡)]xK&@_e2C!}0<;}?*t\z2݂]LT,Ѭ'J0ljXE҉q=͉4#ߵ @@ endstream endobj 209 0 obj 7149 endobj 207 0 obj << /Type /Page /Parent 195 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 208 0 R >> endobj 211 0 obj << /Length 212 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:\@nn?cpgo&zij)m;~%`)o=:!m>6 JmƗZ7(2#H2OIbM(YbR҆uDgUcw I5qMO`Aø75ֹ7?:v Ke\$A?v( e^@xޓ;AV[|kJb !$-*/Rwk.3?jš9A'3[g4)(5l`Kaqkp.(Jcsmjm彗cSwl˜rsøcdh ӊ2,gF+'0K0E#;@t !7@@҄4iHP?FLjMw3J0 a8YH82xYƊ ^a'O=6w{!.8ACPlCfxS /X :;vU(V+c1vyCd|)hnК3PZhe<46̦FW!9ш 8=4@ƣ旲FJ7}ԝC/I46 p|ir?S6K3 !Z"s丹+}VB+ sNxZkmoxG /5ҴV*\|[jbV G $I&Qvr)P˻w+1LK*h+;=5h8zdM8\zιN$9&t)@T;ݞ*ywZl}ʊڐ*yKROQwv}l=R% /[R&yi5aZZx*.'A=X2y0%Bj{BoiZaVF>b ynsYZ^Ÿ25KՌDɚqyNG)ȹG/ܝjjB_X(rVʉ@`GGc ,K>oָÄ@Hg ҄7+R&b$-&׶r%y;|F:n2uV?+K2V;j1&xԮ8izQ]K%p,lnV #e zUo qX%3쪉n:k1RZ\RGQ$m=U%lʻTwu}ʖ %Z)NRÛ#X:eP!yR#`a%w7f|4Ϛ(^i clޮUOeK^c/O,eu Ԁ_-O@uuej5ַڷ{el.7, ?*43%z:mզ'|* 1p>{nxWP6Acy\u& DϟeUB=R/ezNJ5fK֢9 PJ)T:I %da4 ,z|Vw+:aQmQ*Gp2ֳl¶/+lgR/ 'IԸDQςҦRO40 @ H @ob\6Hhnlhؼ*+0^ĮjpePrКp*j0( -|j Bl P иʯ |atf%ދp$0fKrw nw㌰,IB(>d%̣ʴOg VkZ?oz+dzjB$ڛzh d޸ Ӑ&w +nx ֊q 2[!Q*0Kk1!P0qM]O 1  B$LMr M,\tL`D 1 ha#r2)#OZ2N0cO$χr*\Z bzC\!06'KBg-̓LNJ hr$Mks1M4"FԃO-1r1- DR w100. Q S1w1!1 /%1!P3!&"M+)/5P3e% o%J w6% 3~Ɏ$Sω7s^c2hAB\RpMhbFIfX~x'02qn  a,:,n-:QW!BpfO -oTN.Kسm?3#Ap B+(ƕ.3 Q&!2%; OC<YDp#CM SBԑP=f9W?czt`koGGǷ8E.4I(!IQ_2SQJmK.ǔ紩1MLDI+KD4{LOmN,MT0JPu#m3HO(&`%78 zh-S&(Si[IV7Th.=b"LnUXG^>P/'W\$ )OB>$'`Hi.9TNN2JXv+.]OS@S@@ LʘiL@D4Zi*bE$#][>讖Ӭ]IpV < iD :ZXG\53<$ؙL=v0Ϡ@wV `]^pF!^AZ#Xa/65?YcYAX$XcV&U0beU[VHqXqLGjD0\>T%G(EC;y^W^_N__HI`lf6J'VavV i{b%* PmVXGec E! KZ5Zciu1hռ>z3hw` $J aC]@^VW$h _iGuBcH&< d qw@@ {nk,@@  kbVؘ]u UIbB>RNJvVZ7V{wK|1}v^3crIz3h7gupCYhUf֋`si"iUiVAw1zVrMdxVw89w}yF$_-_؁j&wPN~0Qo6$d ixwHIĠJEp F` 婊 '{x<ؐ<͂@ρShd ب0& 8*x@ɍ7XŢ" :"5]|Jx@A6]]iL{8 TRhs0 p?@ @}cǎˍPO|7wx=F%|}T4H9ńeϬw`d @ C~yHiKdxB O&Y͝`I;< FUEidjM LJmr{ i0jEVI @+QmugWg|'@s^d]yLB <"̔@B, y\ .i;*[Yv)dOͧ)v+ ѣL + 'DNpOP$ ($Q;2QTn K }f/00V]eM`wYb4Vq,{5*󆻷eVeԛ4|\T(8;+ !r#@FیoHti{‚(b۹(LoTp˒cRɔ sO e cm}meT;SP. [(3-T`k 2,[AbN{`JN>\V[595@pXsk"t b @ =0Y?fV6Z=blۚ[\IڝpaŻ@}={ăܼ8^Ye# \] `{ ʸFVO.ԟk&qC枼o$q~_*3[;\JmKQ}!1_CFGH~Ddo _=TgO}f~1濑5Tqhe|C9\=W>'8A_Ο_1n?1}lUtۋ?H*..b604d3 (s EA;C1g#BdeGpxl7FQ *fI7,2 n6#.\3 eTڄ@r2'  CW!&LLSkV  iJ,B*ca̟, *0 @EPA1S5D(h |"*<Ї!*`@t˼/`k3 Ccx6p@"#8 (:E:!72pkJ@@ $l42{Ԑ02 Bl7`;#pEфeHDZ4a1X7 Dh7q$D $;H4H`k$>#> /ProcSet 2 0 R >> /Contents 211 0 R >> endobj 215 0 obj << /Length 216 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1#yKR;Қ)Ǫ*dŬÚfu2>[*|VBKY]Y)|DV N4C5unXNggL؍ID1K_;mʱL[o8ލu(Un}A܈8 }yNy':d҆w]eln X @ThF8  U ȋԒQH&d@C:MR jC'V!E,'TVja 1L@oA3VZ۽mƝƦa 2PHմ0o˸k Qa0Ȱ4!\\n.5xǖvL fG땏N\<>oe<#9NW fbH:ǔڶj%嗭eLKk3eWSJ̕4ͼ5΢铛֎Pݫt52OZt]}YwXXYO9ּ<zOSD-|U|2GD/ |Ip84,/Lӻ*ф8˃L{jwn! V8 PUSJfybVc9犗 iF V/!›-8 [=šv*YB YfqƎ+2[5(] NAРnrVLso3K5I3"V{CL^nѝg!]+TUXtg&Tj>i2>=]Ns~@wէ]JkSSD]; RfT o*Ym)}~ߣDnOCHr H:$kA#y`eLϡ?;}@^ E17q|i!qWZc)x~W-;V[70ܗffbTˮ@ڏ.O/Gˬ5 -(C0(ӍtIX ^c֋PZ kHICgp^$$GčUd \8\t%.٤LR j&XTDr\)/`- F$‡v-)/*Rߏ&,X;iZQ:./Ḛ3,R̝an*^ʏHrQrQhl謉,4V!.6qQlMc vføA T"ȼ-/1i!QL(`sQE e!.ĝ*B e #czJ^=eHPEN?Jpm P $@rAj/h),"qjb.RHY@Z 3FJn:0i+qW+ p( ?,Q-/Ы؋cQ{"ҭpq=Q/q!R0/q1R!J/p>,,Prqd3>p{4qu5)#e423]5m"]42(J!D,cM^P<` f%ͺbRW&PP'i # ( gkRj17wbˤx3->,?2Ԙ@Q.L3@{tBC0ұHw.A3uD&а902t413ɚ!yA3+G564H(t6kԈ7?Ktp8FF"cp 4[>JLwH(j& `N<E̼5&{jT=TTuJUdfUS ֢POb VuHTVϠŲ,+3 `2! YA-u[SO.T'\.([eCJUADuFM/ ]C6\_#_fkZ50A"Y[ ƞ,6& I c,ecpj–965bwFJ cdѲBSZAuc([f4͆eúpaMt pU0 Ef %QOP#P_'(J2`ooj!}Rbu>q 4((಩dIH3աe6ZZ54pq%D(wq^S67+Ep cH5Γmsrq/@9quot6(DeH(yHwu-G5KgJN"(aJf7x37wtļ2+/:+E9h9W@`` +NxN;jL;jjoskPkI uiO d4= x/nՓ3TOo3+M@|;]yyxXErq;r2X;]4t=Y0T _mwOEWW!VeV(xma1GX;oqx6%wI8(M5ӋԣCSM'yl7cO[}w |MOM~jjRJ%;P v'9m,;(V^t{ʅqu9[hؤtQAݙT(ݚLۍZw>zn#0݈h[@taa;.yvv ʕ"R ;f#z3kZB Q&oZ'HYu채9BLQ;^oy8{k$(JxSrC.FChbt8ռ;bͫ(:۠xs+/Rؙ_t´[L33i{)=#b(gD0Sd $qbc0KC`05$dLX6> /ProcSet 2 0 R >> /Contents 215 0 R >> endobj 218 0 obj << /Length 219 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NCP02!9a>(r@+9k#H $nk(2C #h3h2`҅PCዼAoϬ$ hEl84 #@94u>8T#H44<:CH:h@;Ӄ #$Q@0 Aacj1 = f TOlis*wBpu @92#M(t827iA~y$dCr˄>t[@܅%xNi_bl۝ky:Øoڂz Ueh 躄&+FwXKX(jP@T}}3P+@;.Ed6SV't:MddYa"TP› I!pP+aR{# $?q DBb I SMcvGeBۉ, +Lە*쐏R+^%X9$.u_j%l̵6^`q.fJ^VK{|%0$b{q_k~v%$_πo W` fޚ 1,lۛ&N$} !@05zXxRK9~@ ƒZ*p5Ş CCvA-$FJJnCz/wWc$iUTɛd LX[fKjpJGe173v4\IG&wDM #lisyܩ]g"'MWn>֧>밦z> w4MBQhA$@$AeFW Y!63iGss.F{k3?3sm8e TڦwUP@1k.[Uꙏ3ࡃZ~sw$9C{K,E Ag H_ExPJ e 0%O0r MUzͰ<{ф3곆خ!+la7V8[hƟhDM/:{ڈԚ`[PTWxM|&>'i7u!:M;B1ؕ'̃$WmeUy&̊|䇌l'LgJIt4vIp)(`r"$O'N莌(DJs j)>5O^A<0mODHNz^pk(PP褸2/ Љ Zo' 0, $׮ɖPد3 /Q,l7 ntR~ޭ(qM۰ޭhqM6+0)<ț;8C2!pSr$o/0/I':G gJpNnlKxe1ԉj`U h Y@X:]$r/涆8\G:aϙ=.hq Ȼ (*I"I|!!1"$*1qI4t"Z *I 0 Lu,;l, P[2r(\3 ?"tR∆R5:;:p#&$dR;o ;0e&a=9K~r{/S& sn*?>(S&?2e/99R#޲M` |"0C_D-ICR.bE1+EE* `(cqf**3!2@2J"31N7>=sK4|TgE7BHs.g@7ta7eLv y]HS#2eFTJACFfH;r=JR)2 B$C_9+δZ0sk:#Q/&R  t;BUC>p<5MR%S_=2Z-=QVWV'_EWV2W@u@ҙYl7Y%*(P FnugC[wCs̖Ehu[]tM?]m\#Lh/5ƇvOS* IS8|=IQ`OJjJnqVM=q*")G]NT`tOq| q&bEJ`Qb5 (!ӡBn2 S;s>7BRY#t^R47G)n1`~, @ NTN `27xiⱱ,}4R%v>g.+eLz `gst|b7d̳{Ss A&-S!4/V֬V~0t~5[ 5_ּV>X4Qyrnt?,~6:2Y~ *Y4iZz(BNWQkx[U_l!EpuI_/FcZxq,%vlx`by1@|qwCxX ` @` '8@gvy*aX,hc5NRFXmb` &qwds|Lu@8l3ZNvB~VSy'%v"0(gkS\9;lUYB#Gem9OVWvYnX83/3Ytc@MxCB똗ZJ6W"--i}Jmq8vWx(#)8s᝹^7٬޸?)m1"HvLǪɊwxYX z[-50V83XQIq kJl 9 |Ii"u9qwyё‰}vQ{4u:>gZ,Z=5U9[9W7lZw\炸񺡗:~(iZV9lj$]SB9mIT .Zɰz^mum]-4_ 4 ^ggfU fxqyo_ytõLFb{˗$vkJ*tV n\XEk*p,M*$E,Y :ءiz| &R2"gYG;Kuw"x {پ 92sڜ%Włk\ F/ ) RG s<]uW5ƔDܼg] yoǕ[( 5"f Gh# je7|Қv]63aԝ;o,@:m˃_Qa=КۍKc9%9߱Y͈7]F a؏u([eg]: w: w8nj-Ŷ#̷M:@P Age$MV*g%S{σӉ"=dҫ>S|HȯAһ0\u7=HCDY9Waղg]Ats%1AqiC޵5^~mu%|W^𛏲QBGˠcً}݋x#8[~5'd|b/}=x鏸}F\g _@ K:gSoRJ>x)1l>;:~CMX#TG6p%k]'K]UΈyIi !^/k-|u.#(  @1 X$4n2 ($ F1XJ)F#Q DV..9j0Ϧd_ FcQpODaJD !Q O "  FVRWNqm0̧1 'SIt4›1lbmAf"\mcf+]Ed0 uN8GFUlkEf { )Vh3bPa]%=77:@Ǟ7phƛCFSO ^#=@ pEU'2!! "9Js8t) Y ̔GLwM崾U/S]HJ i/r|lJ*1V&$R}ӟ͓aɺ:nLdԭTZD1WdmܥeVSlrq[뜟Djs}iwSOʷTTCWN x9!8 ރV+pUf]SN-`dyhW-~(hNX(g9}0~GR DN@CeA@P>oP" wKQky 5<}10g c~>*(Y#u( DfVjf.8j" IK{h^)!\^j-%"C[b4FE" +clm 2ǂ"[nɺ4Hdrp B8H|CPUҨ}-:KIgm(P26DY)) URm&3r Z,b5mƦHq_JkN-c)g>e{A0Mٟ/%F[trPG+)"PLp<Ҧt$ ʦYՓi2aUy ؆|$Tɖ\:/B NsVrfE%syS$4za/g endstream endobj 219 0 obj 6768 endobj 217 0 obj << /Type /Page /Parent 214 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 218 0 R >> endobj 221 0 obj << /Length 222 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1,VÈ"@E(G\4AXVUl 㡨~k/94*WRn{ –v5PbHⳌ}0B\ 1gZQ4kB\{XnhB 'Sa #t9c}9qSG$N MÝJ1H..;Ay$֑sUGP$Jk4`HIy4G(D %9˜e 2Ðo|1b3<-W>O G)\D2Z˙x/bai@9 ՛2!g-ܾ1&%lӅ3^R9[:b-H@mZY+/3s3H@r5mZWSg=X.U akV\B 3A*; eaqOlDޘn^Kc䊇l p%[emY7>ำbݓlOvLJ<pH&& I-83r/Cs` ҝ "3CLsow*7EKrQ9]\lsnJӣIY:U;{CRV +D2 ޘP%xQ4$~pĚ:1]8B,cF5)8W!^=q42#?Hi/ۯSdn'ZJ=My#LgՈNryg4A:bgMW=6SB}$$r6wIis}KBn\ϕtK6 _]BaenumDM jʈOPm tc@GS4S<ÅF}LUnXfwFI ezU@o%U%z`[%IRr_d_>`*iFQcHŴ~b DFApcpctd&dNra&[ #I&eM4pgGP2nJ)"1Ռ\dK)z&(@  kllhlFmZnIngIxo|i/"$G^r'|%RG"ruq%-&s5s>~5rMZ'%[V'dMǂvrV'vog|xk\(yr ]¬4z{'{|%:q&BFW~~nW'{Y6%m($B*.3464Pbө@(((cl=ck*r_d48t cf1d gkMF5Ɋ;E oVJTX*# j* d?@tPn%Bt Xv7&HtIJ]ag`&&`tpWŘ E  B GbI衯2 0M Ltw-vVbh ˟WvJv;~g)KdRkRer~q syg 6Lɳ<-x1kԴoss=j3&|ֳ~!~ShǏe.YmixQׇ3f)3eqqPnC=υ%@{]&@wT"SupMR`QvnvvP2uфK$w%CQ4#B, ϔzY`Az:f c$j \ EeXR{l`qWُ`i 6)~H~L@ĭqȽ0 9/+i1*'Y)R9ٚޙO.x2OON 1VwolViiov]Vonz+z/;ؓmKZKp mA ;`$W-XrBEy$H5wWQẇ)9x "Hr Z 궫WaHx393G[9πSyZZ+ZӮȏ՜՝,; 9䌮xw.R[%8ן//zzW ȼCFH0BQӋd_ω-{Yؗ;ɵ;gx{}0}by, ߎ! qR7}!͇wq$tW *duNreҩ'V&C(N-c8AQ ٗAy/*q"xo1KF)yk781ę۱p?<ı`ޜQTIq|Ż9q5zQ|Q);n=(/(IdžrGʼnŊ}ۛ\ ܵ1ӺIgMb_ES` 'l |0'ُ;ǏbD̝Ǘsax9xfv9'V+hD7E93}=z{ }'R%O<9r:Qsc| p<3uUu*خ#0pãM1I=I2=]:ljô8[<܍۽{1iمh亂]~^˾ \kᱵ~p)%ش4p$Bawn%uCu%6 y`<퐛C!#W^D=Fөhy7;+Ixe !>}7zwzB套v%=m=s^ d|#|'q!*ݎے>O{1[!129ݳ _#|^޳>6ϒ-͌ Insi3ܷ\c)Wn^_Lgl)u9. WS;u7 Wi-r%(D([EV?5zY;K{?A*d(Y猖R? (ȸ) i Qs"@0 #( #hj82É K'G)2.I̠< l.MTY5 d4Ve(V8U5Cb2 &upGbah\3Y1v-iX/44~ e;f8Sfl1b F-6jW\(0l<ئ$mQp*WNjf= DN_;imO'i&Y.sx;=#%al+ -00]?oA:NBp#,OD <$<Ćkb] ;*,W' DM2,2Iqi'4DJMh{ٵt Gd90i[3) * Fn50O endstream endobj 222 0 obj 5874 endobj 220 0 obj << /Type /Page /Parent 214 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 221 0 R >> endobj 224 0 obj << /Length 225 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1NnKk {?ewzGw!b@m3|*6ycIdn#;%³Gf]:id*[)˝֬mF~'63~akP{VmFeWaxnaQCgyCGv>桦ntoN<7AgbvopdڵޏjXHmt#*?t( #6" :82;l(bH^Y^@ 6XԪ4ESp1PMI MT'F6a1OЈ:抣գ8Re)pPHl! PdZ a63UJ\eOP1h#sm:6FXk2ԃC22A5lgUG #Yj )FәQ{VfJH&TX׏ ︦!;{ъhN-IBLzzIejrFF&Ojg)2X6iZD6g.aيG Vj6${q%,kw+^tӝ˩0 ߝǜ>g +@[DL;O"N򲁰#@7Ax1lbYm/XhP;0O'ܗ)d/ ]2 KS)VI/Rn_jC T2²i )1QEhlqqvxsXZX(8A mb+N*Jc^)ws!+j ֿ1`[6q5HZ3ldN5;mny;'y]HLoRxXx,$n' j~rr.Մ=lyKlBv-ѪM'apgdg&m#rgkkO;:^uոw*wdqvĞRyw'ᦟȳKR`UlS9(gʇ@hg ZB"s?_JW!TX CPD:+3 W,PP.C"uIH7!g>7WK:"Q1ʳj\ \V 492dZTfOں_U˷.E짘k轣fP0ثQg VkA{$W;+:g|ReLlʒi;g.~Gd:=$CR} >{ݻs ޴Z})u67-u3k>Tfׯ[`YК , Ep|M# OZ؊Ij 0VeW246ۤc쁐Ԩ,QŕU944弟54zO\nqNr;Z9It22jΔٯ%vhwO uL>sK-[ԍ3MIy#V[^=Y;<-;9Hキkú=s ;dB635﹇ j6,|z O`"a vR mCǶ^h.3In!L> 'sPԙwZk/ ޱjj 7B0(_ r;xf>t Ǫ ܩ-ԃmD&&&jnD.H-Y-_V4jn\dHnf-^+B p@(d3fPl cm 0ZVv-%(ДpTPP"Ԃly e*q U pJ͎} Nٯ Y KƑp ܺ/.VDO8+E"W@_/NW.M^-rPh.$@nDPM,I RM)I Vop?@}= ^N+9s"'<(mzN T#) C)Cn<@l'DgkKE,/%C&!D,4,*L:B,cOU-䦿.k./R53900dX%1 $M.358 8psX3>j4J|o4[5KY58aIM6sY6t^*O|M$@!:rnRUt*wKSPBu@҃;T!mB*- NUS2ɇ=JUi>0#\S$OWX=X5-%PEuUpwU&tuURAVTb{jQ—Ot?[YU5eVuCUƻ\/1]D\tTtI]_]uI^$!aG8&tXrH./23?WS Jt1`1UD5T[t`Y2]Lb4ߊQ4aTN1sNm5edOU6BX3h7vlQP8cd6QR,,&ib–)=:SZG‡m1mk!6m!)6KujVoUu?Uyn|o3vZr3Yq.o$ZF)Ak׮Kt^.TgXruFRprbvKwwwO+V@f0qG~6- #.RI-ItFCGJSes"Խ2Of3h-6t)* 8;:;b3'HXY@hiCgaS&QrPnhbJKBUDvQe'kYn-k[7H[.?TT'5cW5TuIPbd]kuVmo mlH?W{w@mY8r0ؑq4tstRp_rSZW+Iunدgَ5݌w*͔2ˎ[8ethqVDG/Vx@GzHbc[W~J3JK3QeԿ}QLH7~qgM<Tix4 x qKWׁQ $cKJ8x=9,l2uL2VL=8Q%<ޭ'h o S!YVˇɵpWoW% xv 0$ ηx9<ÐGtYn&/x MxU*Mz_ЙZg:Kuxؙ7y`!acԇ{V.U3e{YEdYKe}(%h-}g3e~ ysyv@h151JϝeA9CS}kٳ5$uN xQWmXQ%ЭV.nyu)n[Q72+sݣW=.Xa GS [&1x:WH%5n-l' $f"D)VmWS[S(Ai}&pB˾{Ѳ G!lej;ɾ/{ݽ))I:ՠ| %>Xu8;MYc?;&fm:s^GsG ć (sg\Q\=y}`GHJ(dZA&F! )O">$<>9q2G>Qjc[*:@6 Xdf{*2%67- k|@ ƀ\Y%SIJ}, 1]4WCZ࿈ |T:<Y*hLGᑴr^3I":j|> @"X_#d hFbDلF7|9x  $ =#['R97N@JKܼ>OQ~R+G~E4S^U~^W0WXYeYEBpgKm\e\/F7l8^eOx_3#(Ň3)50b1;NK9jӽg[_+~P]4Pcwq) _q~]|Gq_WOrog )r}bP=ed()xp\?u#?BkS)0b8 F@fF4!qgAj.11h4FxH4TrHd *0@1ΡCʇq@):]tDOpqL ka֙I Gze8PI6Le*!, ЩchE-`dGM6XJ3(*(bwcMLȏ|ф+#Jw%t%)C/RXfrt 1J_=Izbj jܬ&t c8 (/#@( #640LRñ.K1lNlH7g"- B*Of,.+P s4L*{O$ u [b,@@u2Cp*N\0c04Ue1.W irR!O)VTVBUMZ\ S~Ajk}dr< cRfJ 樽W0lUKpqJp`nn̒G\9dQMƸ1iCmj/s;Jtu$WNS4^:xA܇!f)7xCJ$à980H6m27}mi6&̚ 4e9%,f M 8'0xM H# I)j!C?(LJaJl,ٖ2 X3oͣ3StLCn9m9_~F*Vۢm$ Mk!VH- x` lkU&cF 6*pP:ppiItFA$+M Ig`мSEVGIG#s`*YP$y#rT 㶔/ڒxܬMx5Ϭ)sA@B !jxc{2-7& ,A3s+X )V78 hX͇E1Jw42 e4C`w !jIU!4S .8]$t1#-̾登q88K hbPsKM.Ƶ8jMFt&MdQYdNy Vb 9mʛ+6AjفpiB nղȪ ])Һ)BJ*W||?'4vRZR_P-؋$Uy]-$բDEM,'f)uJG̙vnz'祕5E:\Қ۫,ldeIW&E?VHݵn6dGD% p|WܒYnc13]$LSZCW_~o} H endstream endobj 225 0 obj 7051 endobj 223 0 obj << /Type /Page /Parent 214 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 224 0 R >> endobj 227 0 obj << /Length 228 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/Pkjl&ua)mF>x݈ͬnaPΝmFHkfrX\s,<yjȀmGnt hf7P_ y:Ԥ}ZeZUw/y>kuz=|^rfڵޏjXH% ?WS`ÈCHe cy!Gy-I ++S&Ҙ ]Fӝ *o* "ɼ*DT5i hjz>#@J /@h T6pVQ fp갦p F/jz*0HbRM/2ąH12g7ZgYƆS4we;6JX}ǖ@6b96,$Rl~C7bdl VHHڼ4^Nə*$vp%̸&#rq=k stdңj|tMFښ%/U{/[eNKI0&̓nc.&ԣ?BhL.Kkyʟ# !(١Ef$ -g @1G<@ "Q-WAVoA9U ͡%VNّg"sȘfTb3>Qiivl42/1zN׻SӚ56GDg)VȪ*b5i^Ujtg]g'PAoV8CX^zWk`KF%wN\ӂtH 읚VraչlɪsCy_jfrYdNK+j,O$r>1K%r8R_A"PrS4E B! \@DY~9GL "Bj9IS'ƔJ̃@0ChVzd 2lVeQZc*lUKA,Lg+ Hw a\!Xd1ť _Z-~3?RVVnl6 !3JLYR$;nݝxx;K:~b5JxV(͙һOq͹3tXjij !!_0',UPtz 0Pqm # -(&ɼ=Xb?(B# vN8*/*HDd yOHpoRנbelX^K$k ރŶ59oZ k \ |z@ J Ƥ+n?t h·` @/Bon;j;ooz fr.VPvcLp5 h,Sa  :". _1jr 0^} m g! -G#Q䰒;hmp{G ,& &ɱ КRy &fВ r)r)&M";) Hco$$"to˼ $@l(( Q$N(0^4) f Qц!J@PTTS[qaCAr]xB!ąBps:1y^ C)/4,OR6}(eoꧯRvr6el0f`Ҍru:S$!R;?92"R;ǰ79>) M9/ j+p^ '0\ W^J@ ؇'8)m%֪C)BOOB wb*Lp(oNAvW4ˌ)t]O}E&(0nY Ht< Y _-H"EIhJKjtmIԼbpQ0'S1tD_@tlt .u#X%vp@tMFaDw7OLt7GzM{. Ks@A{Ԙb3iaOu|#8^w7Q}c}Km6CAOkk!VfY :)6ĊɆÄ-hjX}Wlk>vP3"U;eT21pm9u8lk8lm50#P#nX8 sXjs<8 r할kC)͎xq' *vm0+M1:@܇u_hկlB֕JSHBׄo8bJ+|qcQ]2h|@{5T L4|]ߘѦFVy^ٍ5\0.Ҫ!Ro!YV6sgݞ6{P փhşVe"yvTYiO.Up`3 yYXŏf"oQDi&OL*UZ/iͦuC^rm9S;GEؼM'Fv `W37QWHHMJ`+ՕDEHO%VVL)0yQNڠ3#W@湫~X|Dc)I Ҧj]O X@f B$.W|,ꈀA,S_75+vԤ>aax')n)ɓS\5N]wtӗXSkb~!V]ǹk[4NJZ!+Mp>~S+#0-+ЗCW (c6k+cZqҶy睆]y!Ef$:q0 ˮ%8 AbGsZ~]fxelQo?YqCoY>Ny_i]3[=eFN[+jqV(BݗX/l-jV>?n6i|A>Q fk۷w}^o/s8Nvsϣ%N -=Jp<(*Լ/0C-DJ@0|= 3+m3R-~ID2:m!Ҕ+$okC"I Jr=ğ-LД7.Ms.D 1Lش24Lj٥Mn٬ʘTR B "25 ,)iJV f: La+ TKIJQʲ&WrU (.̘j%j,0, ñ,X5H3  c@qSR7s6M9Ml1Rx9Wzű+`8\ KY{buCyt.1N*3|D`y#@-t!-e8&i C'){;·ŐxF+[uyAk-엀Q{AdK~չ;.@[׽O,Ś ˬƴ/t3/D|QRTÍ<: h:u&eH 匝tB<1h6 5"U=X'dM];_Tax6*购qa*:\›OR7_;T:Qf5I/dL Q$A 5#`PKA1nvfOrٕjߒ3n i SS "`L9;@ՑOКeL*F5 c9;6>yCzL 4!HB cA Hk( V/*!5Dpdun׻rZx/ Wgc]MgXt{igS sUoȫ ,dh_y6.[XdШ:U^MS+n=+0ɮTjBiA^pY*Me4PBر£LC~eaAC&6öU2Q&AtJBiƜR%Ci ʏ"YN%8ף -dTlU'tvGIUd3uZWk)8hYMM$j+C2lSNf%Xl !JHt !ϺЌcrz kMkƙ/lRL+E^@JC p"MNXIrIE$HF0C0f!2P@gN 0& (N-`*;s6:._ Ogܻ׀1"*_U*BH"A4.`JՂj4-|CYXz,q!ƄX^#rӨK1D jiP$&CԑR JgWSiQVId*;V2l?]= .QԒTݻnM뭯Ģ{5z'{/D'I>:<3>[tb^ߏ8@I(tu7;F/ endstream endobj 228 0 obj 7642 endobj 226 0 obj << /Type /Page /Parent 214 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 227 0 R >> endobj 230 0 obj << /Length 231 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:|v Nh@5 k)g[lΡMnXHjh:AD&QoNfrx\s<<}Q9&a3i"2$}Wyݗ;}YM}ݷXx~Z^Ozv,zםܽ;YF:6#]H]}cؔCa7@c9pdwz[iB%i]1Pʧ4Z 0&]MPէb;yOGݴ-d^[ʻ0Cy "@ 0 L(vYAU$dP\:ahB 6AjêšCI2Qzb m9]#$v =YrBLFbGdh5dd}i٩=4<ʘ-+b*,YIw[swNHv%{n&YLcLLn39YSr&GMG4{SALo@ȇ =<%>w(ʘ [J#cIP9@x$6£!یPQ00 @WBs'UrWC Q`(%jNCTeCt',èbIt#޳_ :/co,Fz+ ktIH`ge}'S3y; 3,L˷׬S>% 3e-|"xC@Zˢm-Rڶnag]z~Zr먹y ܪ-j5|OvZ<`=ܑjJ0A0B$R*@qU9 *!IRje$' d-V^fDS4}ɼ2aK<w^gU|3%<]- Cm :?2Vp5O,fV58v1: aFi!I 8䎁_U2\9c*;yq/?$w5͸纂|Osҷ}#Y%d /+A˾-Ww7t%f;Ӻg:Mf5j˕/ݷwkB>[O$]ǜ#q}2kx/<3ۗsM[BxPdP78n8-OBm%^CPh!YdIqKL"/'.kk/1ztN~ |PvN;M)MK04`я OP!gXu4oMKbMNɷd<4:隔O\ y$Зk-Р+0 o7 p0#? и0 /HPf@hm$HBBHkI 0v @ eUJ#5B[/>%ߤpCNg/HlplMb V?9@tL]О`P&  e6fN0I 9 Ɖt+OG6QaQo50AQoqG~WB~Pp谫!$Go7"R2#٭;" OU29!P4DK$op0DS<`HMŨ"2N0/D1JZPhRR $⌀//Άq !n@xJʭ! - #-QH2vfRR" 3.P%/1{/p;/>r6ls//C10c1jA S9l3@3 Q42FS".˫$гe6ļ7q6y!<280$sL3e RW f;&(+%w/{(L=oYQ,2oh.$3(d&R/N,L*%;E1}-.U8OqRemBnpr0L0Dt>i0bCQg20Vtg2g30`4w3-Tg4K9]H]5rt5sLTJS77TkL.8PBk#3$/JӴLttK+!^0RϜW]@Q;rm3*#Zrh`Q<0%)DU,()j9M,e)2Az!JАK*8LrQKX15Fb1 Lw,UG5GMZm!H4ZMY[QY9]]T-O X4K-_VN*;u 4a3g`7]M^/_pqJJ_v ܀@ &M c( .,Q=rœ#dU>ƒ5J^R^&ⲸDUahRA\#OckzukeCP@ FՙlYm^17MELn[\nT#p55nU4q21o5Nq{ԵX=0lfrFPnR[Zז wP5 u+MbO^Stc=g;(%tɠ@ ޳f?sQ>pgPj;iiTh1eiRAVjs屉W5D56Co(x1 uZL]Ox[ؘ ~tp+b8r2=^8qNX?wsosf)Jd_.)kwYjeutˇ_08uxY8pNXWJ{me:luQ'lRy #RYB e(\mS6qzϡ{Y{H=>jv>*"||@9VTVoFPk(;йlR91 D5k#mn; ΂oLe1X)#XGcb9}^9e%Mُ]9e^RՋ7C*ky0OH ~u/5uKb9۝Q2ќ^_ocYTB+GA.:[Xw<À݌f4=$Laer/ ^ *V{֝|#eV~)6J·!9.9,.y\MqTULo/Y:$vς:^62Y٨82"_98W3hK]IQ*f0^YNb x"!w#}7[M%ݘqYpM;V:rMOG03;ku'vQeVYef y\)5(s furբd)׆MQ,,y++J.[@_mA_jwߒ㦗粤._QlTKYs!0ũj[ݙjŗyk--0sU3+řw]$Ś2j'4OG8֘Y8aq<<@?bGa||QĜU)<] 佻4qбHAJÐۙ7ͣ]t }gwUq~Zgun\mIX# >S2^AUn[dPNS04(C[jԵl|^aj9z}±cH[r4{ I1lԟ$JP)L,R !0zGpǩjaڃ$L@!#(+ #b$n, *N&d 1.:BȌ+JLKT HeLUBmZΧR^RJ@ 2@./FqڌC SYn[ŮӶsra{\.swuHOU{Kt8avW =RW>{X"ayKU0 :z}$`ضE B.ZͿS2ՉֶaZ1$,S\qmg7wzLTޖʑvZ̻Fmsl1VǪ9懴{ė83³) 4@ PH48@)c@6 9#80xHB)EJӉzbT!GVԸb!buWcgZuqVJ+T2yKu!hmkZ:~4r&WwF+]MľEWHw6,$5? ` ZP5@J0e{bA[%/$6l%IpA>b$0yЙZ4rQ75cIn1-0H"Sd=YD\eP~$E֧ +QZ(ŇIo|8Z SĘ@jPr D((C(a T3&S/]Bfݨ1f;NUQEy%%7@PL 䞫 <XoU=˻YρhApi#Z3ph~KVsb)H`s_8fřu}BkRnh1pMť8e#sZqa0Xe 2D8TgQk YթCq:%;]Fgѿ~Wo)1H[4w?$)TZIJ3;c˜KK#?eM 8PA6AAt6àr:S&VHH-$J>[]&HeN`"(EjKI g$&MM#W*T+%p;TkєY${\ Al\ݛ4]U;KM9.\):\9˅ȝL^v\w oqw6OhuK\ (ӻj uӢ =O'Dr2d6]RbK_x*<1VM4 iv$'Eul~i | Q>< 3i&ujIUA&r<²o*<ɶze YR,~y+biSXN<韇J7yI:ou3aЗ[Cԏ:}w CDi㦉6B[Ab5ҋ1& Xٜu?nmQofzIN l-sLF8Y9)'$FzG5;9G,Ӝdf"Ð9[ϺF]<$G #l[(Z>‘k(L4) /d /g0Q))% /3DD# aA,&Q dӜ<61Ay$?t A%A.: $ؔ3\"B-A%IBH#SQ¬)60'-2QC-Ccõ"@ci@DA?)iQJ0;(HKxiX ˂O  )LDʈP1hapx;+6 drBP:ctZL*&i\pʴ5v p0 p'"&,ܷth' **$*"HJ>`ŷ {}p0HΥ=*h* Eh5 Д`*8F(dKh+\9F0+i\LHM2Dă p$3yhar 8١ C QOajH3> /ProcSet 2 0 R >> /Contents 230 0 R >> endobj 234 0 obj << /Length 235 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1[]΋Y=5{1iE׿ a7 2 endstream endobj 235 0 obj 1098 endobj 232 0 obj << /Type /Page /Parent 233 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 234 0 R >> endobj 237 0 obj << /Length 238 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NCP2!9a>(r@+9k#H $k )[n x  x:,h d>̔lbR"* Hc(: #xN3 #-6: (:1TU PY O-X3*(oBAldi}2QOQFz@8 C5Zd[ BbAڲ[n|[=06,hZd!1m*X-hdb497O67cM-nMwQU:Р]q<3M>Wy~fD>7Ȉ|Zʔ1i{?}Fޫ}a?0ڛHL 3r$e,ASPU->H/SAPAh GT(? M2UBrRMzϼ2_ 0?Bh 􁪎q,7F"h"#ǩJ$?~ȇG[J9+h$DXg45OLFLoЀ cl%QC'"$ j(D#?o(7$#ǡ =!|e@ |&+RCu b* tJ,a"LP;CJ ti! !۪Ui0sqixP ηUS9 AQ$HVe2 |o>gN"DB( 2BgܔzP!G>l==F"W%- Hz1O#3ވOS?!Q{ӿi6@JUPiDxU*GP$ئ*=@j j8A(_2%VIJ5R#|uHF"Wj*Uzj6`cJR!B$Q7 8>d "+doL`-1%'S 2fr le9nvrM@U ap.]#KT`$hSn#RteA\YS9; 'O ] = ՞Ўd!X3 c;=D\ u?qøpB\A\rB`O|OI+ Se\AaI(X=6+c< ]D%liX[+IRBl%^'5dAAL7Vr9ҡc҄%ζ\ 0{y*앲:"-NͺFk=)*`4FF`n{6PcWJ7[Hs x(L(n U0Z My|Y| JvߠQ'F{vSq.I*Š*XF4mߓUfٻ{0=%ûd )!XJJ^Vq*PHy )|^(l_brkBXʐh6ʼaݐ[{-^8.W,vvNΓsiMԡuvr>F S#oe̺Ygh넧F6X"+í$c:Qmwn5b%sqIj[.߮WPi}m}6g6-֦qc:߿(p_/kN@QQDt x~!{ ϣv <~%1OR|3un!+ˏ:2}Ǧ9>Kt\ʆ$O̤}PܢJZ oLlYDH <4Lp`)E鰉"BC50X?rgt rivVfhZ/\QɊ?mx MI0`ab&o^ot/dYN2ZFup p@ ƫόD*/mDmPBMñq8‰-DBN=V,m1BpW <ޑX P̐g1W QaQapzAΕB$,޲6MgN~1 2̋1qАp0fM"s U pX?c o4Q$/(ΎD>PaQ1K $jlsFur" `J̍(+Cq(O11(.h.*XƊG᯷Ⲷf3(RRfr-2ԇg,R).ҙ9&MB113!?,(s)3J33+Ls=15L bf"<@!' A Q L!"C8#H9`6N@P`E@TR&O I`𼷲2O#"$2I %W;0%0i|op zLxҀ0mn҈kO@1  QAl*QO*Ar!T!+]+CqƱB!,O3AAr֕;s/u\ o0?wL dW Y&a`4r\ZZ@5JJԄZK\uY>'Y&%0o@hiz)6Y^_%Q5\Xclc` 8kcaSTia(M3fը(c 66o#cudW#dN]eh_ug6Idog2LJwfV~KDM`kn̼:] D^_ ef6$oic`6'V`qTW7lLb6ZU.cF7o?d7dMe |Tner+oQbZE=\yh|0s/ sw;\\g%+i…jU PsfukQk`wV wwx6a"!sy-p]=78;}dWzw evZGsdԘ+bcZ`oW=[7@Jh\>~5l!6s OOf kw_jQ0< 7 xOd4"' t@> /ProcSet 2 0 R >> /Contents 237 0 R >> endobj 240 0 obj << /Length 241 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb16UN#[4UaV.V2#`Z+#e'CUAeV2ݗ@1a'sJZ!pfG%8aJNJ鳈po0k(;DQϼ pqB&(P4BJ fŰKHkKjSVfjz>lh@e'ku=i8$B3N]\0 ! d|j&4J).i6;ѶUFK |hf0s2Ŀv$cLPRnN "QZIGyM$C'c($9=8O?3ROMA콡FRaK4AQ &sنg45M3B qNHg,g^܉QH55%gB]!ͷЌUk CNa|)ЪB#B: |ƾBb!g~RA&Nc&2`EWqPC\aw6J}Rk]sΟ5N@]4+¨Vm 2!NdeYaɸ4PnZF9{GdKtD݈v//{ީ_bo;{[K65~gm|I&_dp ܼ{kJQ}ޫE{hAR| pyo>>Xpm6ԗ?9:c#JDy$В܌2[Z!EZ@~* 0l˪Nt)* 4NH}q.J )-j+n< c8:*Od . h.(z-= ԰/pLO LՏ^0 c (d2jj/ 08j <قM /VodłO5bFٰۃT,g{ Ϯ"ރ on(q/;q3 *2&ϱJ.&sdvC&6eSɯfO '.h@PD\Ķ0.m0zĎ oPPHirBHx'P[TQuPy֎PeK -4Qzr rЭ 2o֢ '!K# * "" <" ?R10#rC%$m2R''.l1C" 61/))/0(2p+R1WjLN=F(R.>dY,ܫfMnk, jb(|*ͪ 憒'RDCR lP \ @O("@+)B7q%0S&o7m(F "'+:r2q2:o/<0rI)s%B3;'s&R&!L0; '=O~)ҭPsk4(4ђ!B48!)ҪAkƴkXH4BTP@2BGA*tNi4;Y,3+`jr T9EHHԻG븜w-RU=AI@,TLBittGrKtDTc"g+ RSkh'H&zDʼmzR5(0Ej ^: `tq`jhq`qX/hB:@G<3S`Z"@:*(rΒ=P@Xr({ ?A3^(\bP'4 } b 5pJ#ȂZ@X4`S*w>Ȃ-UW$`1|XWW3L#/! e/ hNO> /ProcSet 2 0 R >> /Contents 240 0 R >> endobj 243 0 obj << /Length 244 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1> /ProcSet 2 0 R >> /Contents 243 0 R >> endobj 246 0 obj << /Length 247 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NdAZ# XHr,$!I#?jps4 8@( BJ|# ΫH$B,ƁCcHI(D PdoզRM,J )CМ,El89 #:<W@$(: #01H7VpZ ҺtÓ4N8Y]BC%,VZR"@BPGKHILgK`aj@ZoS c@i25j?P(4]%Q\8Jw<>69 f^*L<7ofr&"츻-Ig2E+ 8@]g^2+XX":3،!h Y BP.bꈂ`Lh^VQ*Kkμ X1@' #hxmAjjƌ* #;JC׼X gȫn+ XCP+V 6\ߎUW; p1v:DHppL#O*Sb `4HL_'1? l#G1-( ;>:8O<0$|<G !UCGX"_tX#ݻ6H[Ѳ%)*ZՊ:(l ]`d[,/ h,`dĩ{  =7~M)ԌysQS5֛d7"ipFPJVj2g}.ih~é8@c 843A/R<*#APS5@",Ήi%1J.Di LUG:}8:tyPbK$"Ci!?rx ru2KGy(rvUj UbuGɈ#*UT>T*Z^2j1r{ɘ P| 6 u/XX2 氖#tL Ԟ%"i*66C`u<^ ![^boLt8-=<֫pS *tBVH)㎈s/hI, Dj]!塧/ᢻpzca,,Dyswë $i1Q7(< 2` <*11a钼i.Zaɳ-Q.'{.nko/G/(Sދ1mQEӑE(a3NijF<*N]73i4ET5(aHaH Tq$ZTeI5—48s6KWb˳sl:Q דԣMNjR4NSRN3ܳOuLHhA"KDN)>3?IQH Ȗxr+AFAʼkBBQpD5-PzCG@PY2QYUoG4O iV4WX4sՕ2)#F+Ytɔ9lIq7YI(- u4[ՠ\c],O9y8]8\sfp<4e v H` -POV#&!v+M%3a)2:/N"fjb+R+e)))qBtS-TXot,4``mUT[B~c@ aV|v#02/󒆂FӋY1F0K94j‡l( SI3v][^VN[UmI)jإ5n/F//'N(SP͘Yoi(xh "a)κᢴOظhN.F*lHX|n[&-Ql C.dCZ8ff X/7?Cl O"Xˀ6rޘOisozh:Omm:&҂"p/"oUZoOrPKs4 '8st|^v)Z[6_.Yx-yeǏA7U#C'dƌ5viǪPESQ!FHg$!H Z+gz5? K G!1&ZOzZ+"J<+"99#V5" =Z湧mU(C;:|rZ:tJߣm:SػhE~)DIphKDQˁB˂QSrQrb0Vy,?g]y)avcC/Ƙ1YoW8}oZFq0i'[Kr4]Ev=rqbu:{[M1*jIQzڍ/v5y[ƛڍ;ۻ۫Z˩1YB19>q)FW  >CŮu%$+y-CiC_W[3DX5D5D'}w0w7F;HlQuv7kƊOskׁkrKIZyxը|izW{upuWLZm7E[ћ Ƽڍ\$O:=yw\c[1/d~e;?@Rd:+,Ost'/9›+VOo#0 d+X1s'=QN]_bG &H?ܜٞݚ~!z]|\\]}>4==s>"r}%\/uZ߫p@f  2rF~`X><4bGVaNLrP$ϘΆ_+vidbbu`bK<=38ob^c'ؤ-mߑpFX~x 28[x/1hޏ 4#XJdDH;j@ =c2l͊~&{RX/ϤK,ϻXC".'>t6a_[Qw j"ᰀ` h\0@(+0h.ap؀a F#DR>1H|Dd9Ju;H&Cc2',E*F1pw?A2WNT04d6]c\ 8oFtl2ElVK4 gVWm2ㆻboU93HvÌ,M]͍sL8a/ @ ٍgYs-7}dum@Ayjx#Q+KLj 9Z $b&}muzzKH+n|' 6@pt 8.l)@ &!*a! פh^#jNk:Ni 8(:crx/#"Ev!PBP!`b"S,l$BxrNni L-M<2h=$j ,*J&A񼭳&!Jʺ3* "85p@nVuQ]2lMbUÐU;=d-k_YMBv5dVn%n;lAk\_V<GߕڰXU,MVN CAb_ 0 ~bc2E:~;ETXkjeqA,$BIҀ7: #29cH7K I#6 $L:Q5M7.Fl@oH3$OxIKe* ?qL9ʭm+{.sERd<X[;}KhW̿,N%o[e]ٝWj=:߹yp]`~osM~^Wm[ׁ͞D12xVkgKx T ҀlU_1e=N )*909h$a BJh5)w %5ZOPB*fM 6SI=8ޛ| 8"Kvq3v㜂xrju?]U *_Th.Y Av :;2 3K "rAlcf/(/d:>9̇)=#\~^P-(;r]9q| - h+#n2 -rbLx /d>H$WfdɂarBc> /ProcSet 2 0 R >> /Contents 246 0 R >> endobj 249 0 obj << /Length 250 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 LMv21:DI" 2`h<0F԰TVʔU\T+UJmG\C͒B`VEwjV6+Z]sZaoFZum=YoQfܗiް|zegV _E}xUpjٗ"X61(XVX=vqG9[puz^٦}h9uWUx2!Moxk9~W=:!>6 JRpVìLST1Ap 2r: #x[E s랝 @aɇ!bDR9 DCS-E@$Jl65sc3365MfG} e5Qk$_pusuz^=bXvg6Ł֫{W6|\0^ܷV"꒖џ .F J[1Hc%7SzBY&-x@ҙg &&m%֝`J C8u k}ՊTi,/$ٔqnjeMPph 8C`_ ]:e^Ap3An)VWrdD9H1آl5EGRzgqND;38k5:6^Zg\\Ae̞Jh'*\'W[/l\fPy˥T(Wf!R0cMX{ٙ*g) 椷s%Pm6Мpʉ0aKsj8]< VdOHV!Q%JPm@f(Rb4!}:"A+=z4vLC-(RyδLG'q_(}Î?7۬^ M n! 3-cG /rROzZH@1"HI#ULa5Us'Lwn(.%ŕRic=#e5F֫`My>~Lbk--+dl]5GMc 5wכC9SZ:k'${5Wmt֯ɅGD);ϯg-p^OǂJ'q%-o\Cmm+2\unע,1O|RkLlܛ]n6c*j1̏1)j QH8*OGHWdUI']41QVmz$6'ٙtHf4}ܜOm-;k=mȸ-K7b~Ge _WVJ6 uqF6`_ek=y< ~{(ݡxg r(mJis&v=h o[ jӼ`DГRhEݢy  f 4'JrJ\ꌂsUG4U$[Q6+Ib+^nr"mZ~Ӫ4\* NAvmKv>6,׺RǷ?pml{\*F'6VDĿ-9gM%yipѐ}qJkH{Ǧ>'qmx T򣖊I$كFX龟M6)}CkQV^5y֘"?LRpn8GH+W$?&u;BQ* P*xdobNF]&d"m;y6ʴo Q# |.S+7̏gpUü|ʹ3]^cX}z֑ʙyw&_g]칖*G(VU/ ,#E 0Cb<ۦR^nW^z`OΆ7g)"QL$(,Bt#LqOdbĬF8xƟۊΎl2nRۧ`me0?O3o*u Tʰ@ `b0kp23LN"LLnkJ/.ОGK ЖNVf䨈jоM$kPTnrrbjmzӯ+ \pƌ p8=j?nj.en*$8`|Gd4L'Q*`p|pP,Н''ǬL̉ L"ydqpg ]+Y'}qЎЁ0 .xAP&jPQ C исo1q4ѳ ϗ =D6F(Gp视N䌀ʿ@)1, o؏omuP_%PR qBtAQ70Q%pFtF%@A'Na'*'y2DԤ }rMҢr$!RВ̳+Q Ҋ.' J j4⚗. /- 2..11P['|ReVާ 2o"hg`$3 qq/.J:Gs @]/rAT J0 @1T2T "cFX+5Eu6PoS@ o4`6 s57C6gd%Stى a7e8D&M!?:t(0u(oJ q)M)wKS\ :;QL҄ߔMtMԯ>4NMKRԶLr86 r *2R0QR511R~2bQ Pn 3 g p6;Ѿ\dMji6$J׏66%4NGT4&IrWINp"dⳘU35\p7Q]U!\ү'L[uOf=M1,ѫ]`5'(-A .v oa*%Bb9O?:q 0c:L@jkT%ԽQ3wU]Vo 5kЏoWj#JU;0iGRPOԄN[H83cG;ZSaO*/][a֯^3XV#klq{Nc1m%nmoe^-S=n cЭo*C)@TwWqt!/7!=7'sKo;C t1dnj B?FEVY3o ` UcfvPlT6{5v\A$EX́G'V%7աjSjDKn{7sl7.qklƭ{ȂwL-}^w_9|^7m˳~W|"!c @IKi.ڑ#Aq  swA3S8'2$QIubz#RQDE7kvswvd hoVruuxtgguyH5RMY6Yvht{ Z3{^7Ë|lb!8K+}sq^3nY*x+љѽ=Kxkf|D%aX%#+RHp!T+t2G8KS|!TAuqL =5UV U pf5uWbԋM76zezI&xĒqq/X'8xKrs 4}Kx!Yy㜳~ێ7,3QXHXqQ(.|(5)G&EZAc.gt.襅[OvRi lkprggh2  鑈y$7١Xn˚whIvq7Ay˷ɫT67lӢ;G׭/x/ܳzt)8o~GW(ush;۱p)Y7?;5C`sTYTY_5RgxPP4*a595uzq4qXC+֋ՕHxYBQZ*3Pܛ/z$q̉۬OsɺX7ύ?79YPw {Xx-qQw["/3%RºC‚zIfwcEf dIkj~pEH񷎨UhןY$x%MAc|$Wu\ʗ\ ~ܷ7œ<{ZC(|y\󃨓~|x<-σ[=t} M=ő>"Ϥk)xYeo ˇ"MQq wbD=|kyzZ:֣܉&$ķy]ɷqq8}~Uދ~WОz۹>C>SlK1wI|]FDz>Z9,hjTKe)8L/`/5ԘqMtcynα#IXq~_oEՓR}=!Ik=l NZ## vX?UV޲[_p_y$P܉bq,6߻ŵ橞 ]uc=H, _0fSnDCP4d2 c!A "Ek*#P\ %``20p4n2 XG(͡Qpi@bco" d 4f9 T Ʈ5h.5)EZ"+$XgHbseApʓy '.`!j ^l[ x XR31:\ 6Xas7[}~Zi|l`3Y8l_wNfsƃwlIϩjmm̆ab*3 A| Bnd04Ð2 5ȴM4NApAE #;l7,$E@hԼ:Iڒ<"i2P#2J  c #03("쮏2Ҁ)ZZ!h)#k:(k) ҧ* r&ʷ?/,ԈANe?PRj !4"s\"Uk^:ul5G+cM^0j1UmZYۻ5u5 yvٗŘۗ6`,)1,dXr=h8fc6b1JA dp)Fޔ@`۔i)|{%LgS)k?&SԂhoɬXN)ۑKو hժMZ[4 @'ɊTeƎ K`i q50#6(HjZA{Yge(MEJp2Q"#jQj[ 9{N^+(R\ۑi)+W::W[.N\zG fҧzo${O^\*5HW{zUP:o Y&<ת>0K0Q kPpoe' r%eg\6bXI2&Bf ݙGIsZvX7,sICdQ J $PKs>C![T3+$_eTˣS}]-*Ϟo0+R2 9 ']3`B(; ΩS>3?@[0›@(cA4δ [H0;ޜA@Bz>š> »jZ-Sc*Ёъi@kzɃ4p7C36x2'BԻ.2[q3'@s2@K- xykÜ*F**Џ;:FƒV0jK0m>F:tkBer[9SXCX |8loL= 5C٩D>JwCs[QG3$ZB44cNT=JZԵ Ә &KT/Ns1Nod6 ,Gisa swh:kC}`6x;/4˃h4L@'Q?|CH̢2;黲v",ͼ%D@")M$ M4Z 066T46tޣ5E"QN5ZB:RtR ٽcO.̨ J ̬J2\JSM3KTs[^ӝ7¤$E;$%#&if8JreLT!PqṖvLD|.Ӂ*Q.XUWiͅWU]:=Fե]RtZc+DVdҥfNDVV5ZΪ_Z̶r84lBW;SuıWu:΅xӅoJzSO g8T:ĉEKð6}~<84 =P ԻаUO̴LĉUS[HLM[}4% TUO#-sY6#]jOYNm4R=Vmh҅VOk-VYV׽W 29 ]^7~86 1964?1턈;X?mʻa `(uSL8N| 1J-@ؤdᲐe^ZWe Z:J\8_bNUʕ)/ecҲ]e^,&heqDrl.ofzP<*RDg>8 NuvtWS"[l|L|#j`;Pp<Խ ds$H&JQ2}}Ւ&PU9ΫSW"j[܎^k7v^j߸i˖F[7j>k&r5jVv޼xl< &= endstream endobj 250 0 obj 9903 endobj 248 0 obj << /Type /Page /Parent 233 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 249 0 R >> endobj 253 0 obj << /Length 254 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 LMv21:DI" 2`h<0;oRµR![#O*RVasRCbGl=V)Vv oW@5 eSXզ;eigv5iakYŴe Eg^7ͥzCelyelW]U^V\6[a%!D\=j#)iVN@dav5eXCb9YvJk^痝n8^ h)xhYOo=:!>6#\Qc:45N-4K/ : 2 C0ZZ @aɇ!bE;KK!TF\N#^vBKXzQ%@cq_VqTZ69Ww^|=aؾ^iz/saC}U%NM7ޒV` mVc}6Fb֬:6YTWo57Ԛ2}$٤Be#[},BA aT,BC lО4ŤCj@Z =r`,$PSg) 7xl*aZ8瓧"k 6tyQ-3joC #w۰N1\\Z)rQ眚#x(xCȈiBF,\ky^9]- 6tBk.!liPe*3sXM42]M2&tțЁ}9|ʁxNfR^qy8 Dȍ/%;P&XB} j HQ(DA's6ĆH5!|H,!.MԳs-2F`\C mW $lvQؒ"`ۜr) g ߓHH“"5MԺ]|n͑TL@FH>UԓK)KήlP/aU.y-9bk%P:_}TZ j:uIg,%wtZ2MillDY:A=&m!kv<)G, 4AIFl -CÛC-̥4fͽˣmh^lܻ m]!G7^QMuԭL#Pb,U ()vm彇äk|07Ht(nLJ-L2ʹpUmY\UdT?IY.r%ru&܊!:qkgs× oUuɼx2{vj ^TrT >> O?qθh::3-(D΁9q TAC;sWBe3DU4?ES"oB#Kjj1G$N o]8gL898E4?'gY$w3 r:0NsrO 4P GDBf 4AC# 3B't3=;9O2C k.J U0AYDUE"P֦((>GG@ԇ7RPN=tI38*t32n.LDW?oVSnU aapoob7pV+qOc WMz;XW0TYW;sR"C" ?.Ju/YDT q#w#}6ԃXe8RS%`%YIfmx+`mjs''Q1kŋQ*.6Ms })q|Wnfg̥|cwPb"SI΀|[nkoXrF7 W.S: 'Tl2wX7DQWS9"7]qv'3]6r6AFhh`wz ,V1ivzӝj jO;(תtc)U`Nʑ=!|x*V}t+ŃK>~Á<^*Wꚸ Gq8WQ8́!W(TyWH2D9)e! y;Vke9Aȝv"_'tpvwgowy4 pxr_5y" UKr’t4ή6ょ/&yӛӜM-~yՎWY3=߁aYGq949OsT˝t'W h(!WWVcEfh(fWru|sH}XՐꮯ-9Z8+`sۙ[P[T8;*Q]YeMƓ3z%=I񤚽 )~zowמxQxYì-AEu롷/(L lý+; 7~%JuQMV)bes*giohTYmh;iJV)Z٥2'(zl _;)E2w[_;O3)Þ8z-!['z({Ѯ弗;;7r]"*B(Ӄ VwIy?U!\ZN`5Rf=xiwd J-a7kLX] {ϒD}![}<)i}ܟ-X鏜`{\/IPsWW˹ɂ*%="Ό7O ϖWc:Aηa!GrbN䕦5H oG&BO־=lo`ʨ@Q ` zh#]&hP;xmq]A\Y2:șN/ Fؔ >wǹه‰>N^)}2>K ; y K[ Fĵ5fVy^⶝}EZ)/lt`g1ʵGgwuh=4 / |UNǧوOƗ9X*5^cZgT)A*1j]9_q> 7֗zz=߂W[?ME^^93߿; dP,L 3)ވ9[?]5#!j2 0 1cD#0T;r(d;M3(c _0 &dcÍ@ѐ$0F$HN+K!1ApQEЌ\ʔQ*TڣB  p826ҙ2 ackH1FbN$1F "tgtV,/3gFC/C]v]iԈ5c(~UovT7 }Qpai:?WZ5ltp}˷`gq>כojƃ 8n:`k>{f?/칰$Sa&@0jqdža b֣+0l^F0$h036}Ʋфe t{(-2BP#(4q$0I2b8 f*hȆ a>$I"L%Ib\&Cp\6 p/cxҖ`6#Hc*´+`0+8\4k0+/+H/̳xaY!&ʊk0SXGDㆭk03kEQнOkzl=r$n;yEU߭7z4# ~Ic?|v\#[ir \y 8 DlO Q/˳k[^N-?0lTzx)$$Yᚂ$v袁szR2Bo 9U6fTt9ª4ۜQYqt' \l-0S2z2:(K8D:( XƀD26XWrL >>]wF>j]&K OG쑓IyQ)sT el`}![WGߨ L Iy3A8Ӂ MvJڲ]rcfݙ}7'щN,h'8n go8PC|Bp yWn-ȺYSN2BrYE+V`etkҹ%L%包 &,6.tLĮr&rR/Re:W=Su"ˇ,)Z9+ML M*8)3:a4GSMFx+{- /a_J`UusXK!:l(4|HPnQ Eu Cq8 Kb{pMQPDETFյs1ƈIGXuͦ NBk9gBsX]܈Wu{!.䨲hޛTa.~o\a߫ɴw~ՙZ3 .l<6k{"`7vy_QpsBX- VEw9Ely q?.>[5G]l-~вCbqtIU[q)Cw6#Qq,E1)u4;ݣmeB|yi9xT f-{Bjq&pCK`)tϾi8$u M[6JujP&wؖW `s}V5{glO_NԳ ׃gg(ExEIi|=&a7 O ! r6_ƞ,E]Ws] dI %[m?nt(ЁwCrNY5MTG񄱡> <DŽm9R>6%o.MZc>iMyu|37IXlyGG|n)&YfbZ:c҄ϵ6ga1X>2Uc!6cQ޷D portg[k'ǸO^gYoAy/;RB?6Q;sU j: mn! ɷ kr+sH"p5/' 27!({}(<8r8684 k3[г׸ځ ʘr)ӓD&9)B+J0d.?,ª02>;\0CZ C+h|4B\BC*,(h92_ 2F-/ j9+lK2j5BiìGA,;SƤ:ī阜=4?jM+>!kGkH;J;@$l;$$5+61D1`DX("@S!42r$7̢ߕP (Eʍ ##+)7)()K:[)k"ſ{ʊBGٚ/DȊ/C JrHRCK,68K[L?$ʔwW BȬK_tH DL"G*hLH3tƶz 5@:L|@k=R*}AIp:0: 3s,2AB"9 śI2$.*{׊L;C#\$LDl#|l4/ϓOOl0YOyL<{Oܾ)4O zb ipЫJG\O :8$6,2I3OPQ\sL2 G-G;Ru=R,KЍ$ұPTQ})RsR&: endstream endobj 254 0 obj 8667 endobj 251 0 obj << /Type /Page /Parent 252 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 253 0 R >> endobj 256 0 obj << /Length 257 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NArIU# XHr,$)pHiXL; #pÐ6h2`Һpn `Ǫ鳈uL"21S+1ZeG[ BbA e11H2"tD]iQ%RDPQ,VnN IḒsǬ:zUlfAV2>hHm|Z0 ,oS xpÉx*A) )R.⠒}0V!JWsO%s+Mcn^S闫ikm,w~EokN#{)7} `Jv 6]f8E2Y۵节HKu-/c$!)k)Ӫ" b.1vsQ5*_?=ҝ#9 #:t$;Kv] P%A*p,.Ÿ/`k0҂ +/O x( QDql㮂0p Q.σqСqē0q G Lx; dͫ!y#+/"⬳#!'!: IJ? NZ ؘ`w&&- `F/fK{1 R ӇPp5mגHQR!ąO.Pi/(2޽ U27.y29G1ܳ2o=22 r/0Bnp/V!Ly5#P6:m7C3@jM7!%b;=%ixbf*,Z{@2O qNqTR*1d|2hũ+Bt `*P2*͢nΦ0&דVs~%@/qlAneA4 1 At&A!C7t@ 9OBT/E T Tc/t*CC"G)seHT%3ttl'Iss!JJ޴8>Y 󔲅T49oQ+' >v ,( rS)kdN)i<+X#'==]ɖm6S3S-G*;PxFGR;\ST3TQS̛BĒuQStBU7T4!CӏSUeD2To3Ws8wFRWtm4̽X8b ZuQc ի?bUUX խI\5"[8zHR`&b-s:bnjf4;)C1iu=e&>U5?I,H-v(`pUPh KAmH6NCѹke=UT5Ybߵ^Mf!CgtBuhxu}dQWր"\6iTgi=Yt!]65*^KIakl,05Yl[IV#,ivmq $Tm]6uPu5M\ ' |6 ia*maҬ#PF5=hW9R2>и@@R!}Sw MeEZ v{f6px/gjiI6w7WWi1I6\x\Y!S1j44{uk!H7hU~tV^iB~{XJ4MSͅ\Z!Nӧq/q HQMrғsO_w;uWA=WDyHHMbYX?v<ԷgtmZ {S$}BU4$(xpYV8hTu1\ŋV{37\ÉUkO~}M:^5T[yUIX!nY8.3UޕK4y)?epZq N > ` f hO+)S`ҘrWO8|+T>O[cQRnauw7wAɏfuOWV$V9YWYË|YɌw!D"Jye-jՑu=sHZWԣnZ5YJ!h ]d_,mn $ oo땸Mo9g[a^{8b6+Qs,qԕ?+ZYwVWE;e3kቢymũKys2ns'GXɝ"x|k樇~5[ |[lO%d@rﻓ@q K+W`]N*+ukt5rXgsl[6;ex;!ǯt}hT-{g[!jǝ=({ӚJ{wzYc< lh5 o)޼)o(`pIcpm\Cpʹ) 9J ?`[Xϸ`sҟ75)qj!bzw+2뚃uٍzRtۿψq)\|ܳVY˻jC=2ɽSt|ý3{'▯˻;hy:DиJH%\V΅M%Up3.TIWUq?r_n&qWț1`IuyoNrPQn(}\ac9{"eK䇝t}l۳g=ٜݲÜ؆0}1͵:BW=U=M -dA Aؾqԝj^¼1 ]Fx1WP=LH=MO[TϋYEƝ`onivfלl7SO݅\y[RvC0(3~wM8 x|:^y<鞽=]ݾi} ~~/ ^ j^m|<&ů?Z%K'@] @ϕell[Qq}t Ֆ1`>Ǖܥ!QYx[۟]|V|ܟ _~<3;W>!н Fڟ, b6 qb4 `G#( "@3 p8, #A- ($ !hh1cq|e# F2ȼY>dSI 4TF8e/# ArBF5ҡ<1mqELc #&(c8Fq|t02;q<0 z(. 4CȲ6#9bb Kga@m э{@- FC%z()81(y zQ6BDmf]:~\E eZxf%O0)(>d8Җ! @%t(i5?/ #HS ?! DLĐO"!/Q]?H<*%!I2 `őU *sa"$%)"Dob$)+pg6|9(Q73>њ+@ӂr2UAQjd槧 =RT,+1tȊb3&3 Ʋ*"3n4XeZ.򽯫0l+Ď628 7 4+ @!ܸ;MKVֵ.:ۋJb9S[k;0&oCZ70MPlت,LP!P(]0JL;.&1dp=04SI8U9c㔔*IQ䝛Ҍ"eI8*P:\!Ҳ֟uj.k;n%AFYhCe P瑢U픢mƻ{\L㼩"rTEV.UrܵVVU/00]eVH9 h:v7k;V8'Op4Wfܯ]/k~i|V+?y'_. Ào+*t!ib 6:kb/"jo舧W RL! [kfmYr+G 5&v[ 483ap-B%*Ӓt)dG KTKp 5PR4ryo2Dؑ'2W\''qj*8 }# L>Fo{>"Ũ+̀t@D3"h! Qm81HGL0",ɦ5ɼ.cPtCnkPsCɥ֠"NػrΊGі0Q)h|f@}3h*o]7,  rR+Rbu3T_`iZRYIf*ݓˆP9Hn課ei7NY幘_MK0l$?#2ʧRXaJҝ&/p=3Cf¿y턲%N5f;`tX E=m3qR!hlmfYSF-]eS[^tE\@]q2R=nǢЦ[I2=NRw/NUNcӟU `,'_Si/ժS(46SKJĻk#Ԭ֥uem;gt"`e}6|k fbըM6&9cF;C3rɷJqt[)g+l(dO[=X֒k_2fW{Hżc*XN%ǜ.֓E|~{g5r'@4-w<6ܫirSC& ߺbc ;ZUNUj`7oIaӋA/K]1;`ab#_ ƱVN3nmZ# (] IݹVŇUE0A 47. mnxoКsW͔-˱rx1‹'t6}V{C59R^:y]TW2t|N^uoΣNgrØiZL=7UkqK`3Z[# l&L⷟fWQY\y˺=cqPG19-{+}o_ܭ>*OÖIh12 c^7.mkmri zPUǨ}_|e$JӾpZ㺶_>CT^R$Xu0^ iAk pfxq~ݻha/]Zv]! SikjB:"g;֨\sc֬oc,|Z= ;O >D׌H16`$x:IߵK0U:^:y{dC;"\;1$C6+;st" D9"dQJ1T< 'IdP@؄O3'E7X2E-^As <_2XcAڅD苙.@(’xAk kFFr!;UHEs&ńid.BDkFu,pqls> CvŔ]$^œy.'}GTc Gi )2(/-HT,t, "{ endstream endobj 257 0 obj 8194 endobj 255 0 obj << /Type /Page /Parent 252 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 256 0 R >> endobj 261 0 obj << /Length 262 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1(r@+9kAA $p?h@k3 @ 7 o(6#8)@& 6 +Z;Ьx@8Q-##%1cTq&(P4SS)$-I:Ll #IAkKhUzզAD*wE*0e B@ej6D cN!qL^>xçZmDorDY BMDWOt%}P7 x*Ձ) )b.`nƪVWaKcI=qNR cWz~Y ["K.`YB0ZDfRer}ImqLj_>v:"#-. kHmLK`b_h@f["崹 b,ꈂ`Lf;?Qb6iЂ:1' #m @N+[=)lJC5 *#w;_O6H)XnM4RNpP+8ׇCOqK*U3:^4yФlBCǐdݐIR#))Lы$Ά@BRMCpVJFDku$`iB(/ `| Y /\@`j! 0@X 4-!Q DPhbL$C_bq4(BD"3S1rWKEHg')DGBe! m=4;B8"!DN H!]RKH3Ag:>I O@5CD)JTσUq "WS0m'D|1tvj/`i $2NcTjȂa7,$@֞vR Ya7xD)w |@C$҄sp?d!X6FR-H.k\+ DzP8%h8 HU(QX(\TrFȺ#B=eE4Hhw*%1è&0u8MTb3IC#&͝$#UIx),Uk>#EtbUY'\uW^|V**IiTve2 < o ;3f|њhl-k9M[8Am$N:<3%gs#O8 g̠ d3uz8>R3 =0QJ^LywC_SGyWjM/uAez$A@lhj_HWyiI/A\s ;:Bt$% T @R At !B 41A(dD.?34L! I T OxPH0ظ6,6i6K&Y$q)78԰usY3JBF91N;קHߐ tq(tKA)L?J<; mNs,)OBmO˔/OQT5Qޡ зL t'STR)4)Tq03AI1SC)1LNd)`]*BlNhTQ3NFO NɳSG H-5 H4D:Xe J k›$4ǎy5%d?=hqCRDB Lt ] Lc^$=MmAUB.UӿNQ3NVO7CMN aHOO/`>`uaU$ȡR dTʔAMLVQSUduEafT!fvO (vWQ1¤'CQ31W03 P X(qIYkyYEAtUwQR%#d(mS`tvNuf,vTT/wCWS!UTP}(]#(\s3WNoi@kGmY2aZH[DrԲMI\%W& u$REEVRsK}',۳s vT%w8m8a:8VV(?  x1VV6Y?yL^^rg0NoewX}xY7:)cPQϤ]*IzX[)y ֙\ {·YW[H@k|%AIW$v^|}s&f劎X7gяVS턲Px,6Oe >rW3P7xCM1bOR[RCmtS$53[v<6}McBE43uyC(cr\nTD׭ @ޘصY7xx|iUmn}%13nXtWERe7^Z⇝E?m@P 6ox5 9st-OztHfZ +=ANYvv)z%^0YɆ4 J落ywQۥxu t:JpZ6E/ 4y5Tl8$L%q!mں 77džčs+ ` _:˟7NZӠVo: zA願=3-:,Yz$v|ʰ51mX.m˖Yt{=E9yELŮiXO:w5y"IZZt}G7Ѫ6J7nڳ3C$2ģgw9cGpM;) A;r;ϢeM)MU{uKVyy`<wmof_ݴ6Db<3 3{dvj6jjjn8JVόz$Z'WߍY~u6rbHfiOʯ"0vO< pex (*3˚r<\!#=Ea\5c˛ <5= AT\;AY\:flfTgZwWAjj Owb:ũ .ٛYUo$87&v7]hܘ}۵=kh3r=˭=<<ѝv<:'c{ۺ4 [^e$)!G=S;3Ye0;?[AJNTEBuG|<9D5OP1%DqY٬d%_ּk~׸߸z8d;y {mq;kr>Tȧ=^ܜ^ h>|J+ 0e ΞКgGXLޗyr/I׋[Η䮝+ۄۉIuUǺJxn˓3&m^ϖO_] eV׼q?O>ϡw^f߃[?Oi[b)WB[!ٟúE[0Q UUC?];{] i1L㠤@T5FCȸb2 $L\0 R';Dh`9 F!pSF1pq!NA3MƓȸaA@1yNԆa^);FGlR"p$3%IFQ4 +5oQ+6ky/ޓ1{zuGYژ53GjU4 ?E*:95^߅9O\DD~lϚoej~/tWO?Fx*Bi ¬bzID)$R]K1TΚSZmM9"rm&)R0R~!-@VJA&n7%B Rn%K52;92fbQUi8畓H4᫶ EX/y1@c 6|!Z:%xqznGHсxF7HP\z !##S΀=F dX {!?)?_U`ƈ2F2j匬ˊA+,ɑ[90%䪗 0m/fKh:CMJHՒYr6JAA|6C] ۙ0ݻtH.!D@b2 QIJT~ ,4s͖*-LsEe}s (ٰ9jMG^ad ,ui]Ҋw$#w}LjEh{UPKEOو}Z,u ?#XeuVs0k-k/꼺޲| nJ4`2.HQl۰vp5Bסaɱ0f'dT3yAޔS9gRTPE2T"} r.2*+BdJ5**!6baG lb`-Ă;6erOw1AZ۹ҞkRj 1.vUb͕ZoukKV"GQUW[Ы~X"B8:Vel-)#af?_+An7ZJUv)B#b !8ho 4YuMfajVvr *5©D-c(8;eCN -F".ƍ\55ͤ9۱ GsTv4 \IcœVv/24T=3[y@ot5:u/ޅE; ^wWCLj,&#k ?(ٖ(Ƅ{R[tZ̈O%f+V:rc dAq7!/mNJ.dI,<%d{I?'eNr#2q*EeVZQr/j$gE}@83Yc ߼9q)!EgŤw{{;Ip)^9.rCV22sjIe=0sև\X>0;7_YkH '&N.K6]1ڻ_l&ۄM3Jv;Ũܢ᷍ @=Un\9ɽ-Fy sxW"\5GA y˿KK.>N=NYohޑ~>L5y,܆]OI=}VLNQ~/qs'Xav3(l 7[(0`/H2;w'Ϸ;Ҳ%":, $9J7"Z? Yz91ݣ́.;=$s.p=d:#| F/bGS4L B3پ›*'?/(9i;+ۣ=$0; :;CS/D7C48$ɗC{; :P6*~j a d ?qy4kp9+'n'o2{2t @t &yQ| t9jg=22Ah&CY\l7Ed4) EY=R: h /bF`jkBcHkBD-BE2CYC$y9dz=d|8t~5>9 ::+ ?1:&m'1SDcri@@!N1;yHE R. ST $U( -SE-XZ $[2+;JAFzF<ʌe8JkAqJi#UJ5tJl Jn5+4p> c[:D<(@$6=$G#t:㩾,HHXϺkt6TFP6p5Ĵv)<:63@P̖( IS̛4IsI2Y̢-Lk;(A9\Nl<ʲCeD0=d N:dG* JQB˪O|E&rC${%O>CPCXe۪Ptɠy lζK'+/(2(/о4x6I=0Ԕ͈ #DPK7[{<-s)E+ykŭża$ERj<ɁS5( +Jg-B= 2O1/t=2Se,K2OR4K\ a[T=[=uEcu-HБ C"ȺC 㲿QX:h9A:qŐ:n̓p H!Ql5CxR@ A RYLˮ2 Қ5gSJi#W 2-4urcWLvKtW|- M[0P5R]=Fm}\5ȺKL|I?uYU Y42L*t] ]Z6" xZ"Sw<%aIkz{e2=8` #.^A`MJ`$g_DL`d/_> G ׀5î؎_;_ᕊa ae{ endstream endobj 262 0 obj 9382 endobj 260 0 obj << /Type /Page /Parent 252 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 261 0 R >> endobj 264 0 obj << /Length 265 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 OX;21:DI" 2`h<0F԰TVʔU\T+UJmG\C͒B`VEwjV6+Z]sZaoFZum=YoQfܗiް|zegV _E}xUpjٗ"X61(XVX=vqG9[puz^٦}h9uWUx2!Moxk9~W=:!>6"HM).B45NH2:Ð2 #/r3 f2Cs  @a͇!bDRҦ DCSOOQN0A;O8-.;+ڒ\#T 6=9(5Ecu}Q5ϵoU…{0cz{Z?NWZMgs|_CߗW}KyJ߁p֌ip40~lq@ A,|Кz5P_a6hЕkv=Hai3i-n 4 /fè k}ՊTiZ@4۪Sg7xp3ؕrp&}9̹ZhFtNj.Qը\@Sviڻ-" ӹ3QZ AQNEXDTyO)isJzQ!2xVHrp. 6A ^3(eĐE oN!(q L՛K bUB Л3NZMf TrՂy75qOu9ܧjmDlQ5Q&B- YOքH `kEgBѨ -VQjEF"֊+$E\?d!UbP w3 Lkq)G'"B[+O17$A :U!BzGX ڬ;tT6u1ؼY9TeWщ<֞ &s+n k3^K^QÛs؉w_V"vW!͇Sj-IBl0Xe*:ʵm-j4R&٪`)8WȚ0Q~J%-ful;T1|T9% v;c矞ۣ!Xm׼<ʶanlzVe~> h~tLZCqH)RVwMr3wO!WUJg}5QiqWZgG<ӖJ{EԪfy?ޙcq8l /PcU}Su]O GUfUem}'|KSV\MYє:?x圭-? ofiv;::t\k燰Yհُ'F^z%#V==iԇS+}7}.c+XRHQ%$rZM1Wd ( ( >DbsDM(aEэ$JĬ.Q N),Sg(lnid!O0/4#&2ΝFn隘0. olN##>l P 4 lllovF/w аe0fΌjˎkPTn+HurM`]P` Nק$`a.0`Nt8͜@PJ۬1*I@D `pҮ(+! ʐ~$i,Q/JA G (p5qNApȲKck K /1 %ұ ) .Q$`jh6կmb,<` QFJذ*J45f1kQW&Mac" 8Fo*o@"Hϱ,21,!1*O;q-oq"jLf2 p-^2,1I1! '$r41"S51p/#l5 #7E&XRHKVH cQHrP@k'r{)1c(>LPxWo'Q}#Npi*ƼR;/22Q?!ѽ?/T/@s+ks00 A:+h*,7C-Cw!3D0T-CQ4ڽ:$e5BDZbaG`K6rO6 %܎~gHO'm9[9l;SP :QxBlє)*T@CM.Mpt=N1t@.qDtOtN OI@3=PT!0QKAB/s+b!ВS55ESOE(S=" UtarrSj 'TidDq"7OX73'!K#$QmFմ?Ww$)x h`D7S2dCc 9yPDku{Lu͂2|MՃF|E Xً?}uX.n p wabuxq5xΗ *B)*D%co9uIY' )t/UtZW4%_ve-Vu|X@b)EI96`UvZK6w()/U]9`Wǔ)5}9-ș,P qu~ϛ9ݎ;3y xݐ9↕ąӠQZI%eY +eƹ FFk悸ig%<D9"[YЯ3zzuG/KL5{B|ضwgmvx:,9nz~yժG߀ZўZ158yp%mAru0ܖ*;T]z' u!Zp`obeaZYaxn)%QӀדח jyy(Ի){w{{넂3)[hU[Tv{U;Ӑ;o͎[8ۻB纵_yǰrÖC~ム3O#49A-fm(*wZM ` T׹tWGe%y~`t04dk0Gzڍ'8?L)xьo(x=}^];\DzᎼʋ;p eb+Llx;͠%=23ے;ΘOuWO9]+!Ł5F&umc\(-q6q`̨xsGEyV)xAۧԱ({q ]3Ɖ3XōbJ.I}je܇rstrz݅׸P"ߞG.䇎NŴ=˃܉SG؟-.f]fKЂ >I mĶI\%/4 Syգ/CIr}DtKz3ZՔ:UlLo^bfvT~k~s`s\}o)=k绽[WZlKeC*]Fǿ= ѡ.z^Ͼtbl_ȱ꬟_w 7@lmfm`^?ԛfOק[@Q {im\s1 ^o?_tnă_o\[_g`~X }(RQ^_y^ĺ_dHE ~[_VA[0uw g/ͦy_8NFHj#pl "Hи`1CjJw r,c"bQɥc9Hf2 `.FY8LFqh=N 5oGkVdR6.#Kt|o$D#(x Pj.0L6"&6^Sa2ޣ/iGF{QuW~wZ5粃wax\cvor[7?]"`f |vȆ k%Bs:cQ=%7C }tdSen$FxŔdqL7j($lePjJ$2W1L45d8NS٠ |[.< 0%{sVEy5$w,cϝs׫90-'ÜS(?@k:GRoN8f iiA MaG"PɚGh/l bZ4p5Tb$ KK ޒ@S]Khz5xX֡{]kC`e,dEOU@VEU*kP@Q?SAA11"zJ٧j+d+JԊ-q+nVi#\~BAjSUQ_5RԯKҦKE&Nv U9+7v1+M7% gbrKkJhAk!4'@`P A&DlM8*;=PB`Ի%aB[ ]*e$+XJ +78RMrB C̡[]kq8V(A <)T[dc v'( "7p<%sE,V/T !$<`BxNƄVE~o:d(¥n⎅4)] _$1(V@p|՟27re1HbŸl!T1֧ ¢n+`(czinIc 8V EK0y~4R֧&k>P˒ ]s"!"IY%DEp1_l InOcJSl26fS;mȤBIS,mw wL Ծ~r8F=< PN8Q?n)¥nf[eY-6"f@| rrn9g 'By"os4ђsICSRPI1U8,(;N|rJ`{C o ,:hzòza7ET[f AoG$X"N$g&DYvm$ iϗɃ[Y6N$RowJ9ؘ*dy:lm7P7&.wgoeJ!uuN:4SX3c9>]y ЊWװR<"[9.4* צ>ÉM :ZЦ> /ProcSet 2 0 R >> /Contents 264 0 R >> endobj 267 0 obj << /Length 268 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb16]"h@eIK 6 #l2à6/Ô#$6˓ZXOzt.,"KJ3u NDG?@Tϡ4O ?; 6?W}o:\TMUEXa6%]׵XyǏVsܾwۗ>gy~wWCgޗajk> i k|hy>Zx 1 x*U k0>l քmM a\4h(9 `5 `æu =#\ m*1a@@š<彂q a|8Pt8VWrĵ̃ A &H]vN֕80LP1ܙghNɥ0hFx0y馕By ɛJea%e/J3)WbTdfpQ.f70𗲞\Afĩ/1fcif|P.sLi,!^Sh<JqMg5Q5h( b3j *߆8P&!ZC$%4~ DZQGq_:(}R@Ι)XcoACl !8`?qʹx3HWY!H3RvG6duCə)\u "…E7^ADFia=&O { ?tvΥ!&v *?]Z͖]$y;5:lfcڒ '-rl)rOf*'E&EҴ:U|,] Cr&Z3vR SwnrҖcMB=317wOCl7Qj;q)!w'Sc"`H!P.JI;v 2Ej&wm( UWz|q1R`Xt^?dٌei5j>di%Txڼ'%VlhmrYg[H.JXڷteF9L9V%K=ھqFmS?%rs`Xg0VZ*7;%<C OX0ɅJǪ)8_Y 6*7$k"o&I֣orTbԎ`c}̮`se[:,Nݹt䍿Kwu [|e7fY^p#;I:;~nn_*YiNs-`&љ6}rܚoZgl\q,-k3:/zuufo68-e]%n,ڙ; eSX&b ~;KmdK1cn* l*mGq(_x<~7=lp2mYxBǙ]ٓ+pwE^)Ѽgח3x/d~heDd\篛a6B"%>A43}-f>7h~sDnW[Iw_w~od /x϶t/h8Ύ|.z~p=b"L%PJr P@F>$&HM>9ˆN@ cKLHҊ¶L)ЌML& @ @ N(J^>ӢdRz8NP0PmCЎ/1h0 6_%"]C>t `@ "|. @,ä.8*,9{Ѕ>y4U A BXc>auieHOq0s~Mq0QnR8{5wPqd**h uq5[XV 87R )*Q{1BqH&f*2z.`NF0䢠5 ; Hп'@IĠJDpKn3Ld3BMXS$dOPnX 9. x;VIn"D-ۧJNN)"u A2kxgs52Ȃ.>|ɥ4|5H~L"T|D_5O6)6g6!6^ z56S3xc/O˛9n猑: " F9^;y83(r$c$%0I0J`@c ?` @dC*Ғʓ  |/XrD4!gXit.;t `r=Cj4D b PQ0E}9tpWzW,Bm0btASrr mAjADt GVeCh* 5RهXuJ9S9L9nkYvxsv<؟wHl=CRP%gם 8׍zzꗎ{k"|XE֝TNTSVYuY~oY)FΦ)Ø* 2mOzHL-(z^YY^kJ{ocI)OçUz{ӧ:KIF:7EuYtի'ub󹬰Qzdox=zdݸc OXI7nSmsg`+l!:h8{'Uw}ZP.D'!S Lkj~'y~׊ Y+Y׺^Q v Hf^}:ow_);{o7a;tøXG;w#;yZQSbHPKdÉ[[*ټPrzy."0"xd%?!yVcqgt ?ˌ1 {`;"'֖Q[`- Pđ[)j֠*鴺; N"Gu&܊iOs:Z\\$oʚ܍y˚|Ż̜vE|ˆeD]"zӤ|ؚ\GAt[н33KOBX^ \"x÷,œCC:0TTQ|}o/xPYcȊR;jx3ܝOڏ}߻cm;Н:ښhnw*]r[=mGü\#wϝ\]a>Ǿ<)jg]9 LJ` ԕU5{ռF͏W꺒Zmq4XZQMH!0PUξ w.;ȝB2=tGZg,>^˾ʻ }~}~U~[Ϟ|8ݟ <;K9e7⸬>䴾ĎQ+Ӽ St$7J5?SVջ`qgX=nY ju~\mkq!Y6j~Z\[(;/%_^ \^ɵj~ѿ^Bsd;1 #a 3Fp4 c\7 PH4" !hj&1P p ųq\T1:w 9@A2&$O$!)(,^DOE /ťApP0 E),x|n0\c<f2 XUcfbf<3pl.KQv"3ALBz<24w@E aؘF2,kD<͛]ʢs0G{oe:|~my1H|G8jQ.Awĉz4/OhU}`& a| GkH\9]׏.|B/ΘO94m`1nFb(eΉM}ԔN2$Ol?h˥hލ1N?KS^Cqti 5!t L9IXnBjmpgdnGt *ʨ6 <4xh5Y/u`"Wt9u]~ٖ%zIUeVkKuX U7v޳5˟)9>Mhf2x3xVz&?7c&'>ԓe4}}g %7%Fm<slfH%.xO p fء /eZ#B $cv&mst[ARh͔œۋS47P(r 3HnɬO9sN<_+05]N y2&MbS3gy@ x[ 7qHp,"2 cQPA%')_obJ$!)iPi`[lrhU./Y&TP^tM0&<{'J'䌞4=/0 5^= Nrӟ>Ǐ>'[s"F\h4M[q$nXLbJ' $IpUM8:pr|2cZteGhH#/#G d5 ?\]a$߄hᤏiHt]ѳK7LBT OAR7+ȇO2>tLiM\H4{xjӟ3ǯaLZh>* 3勚o -3G_'އvaTԍ:ִqy/mV CMna4LZHC7h7z1>FI(qnHzrK"jLjJ)h[}Ϯ5un=Q0\Zխ43S _03KpsE ry.lQ\o݈Vo 9Ro:K:^|0v"sTLQ'S^+U&%0;[*o0/e-4)oL+cle5+|c㩉>6[NʢLylpD[jYls[?&̼v:{5Ѡ 1[TDIE+YH{LD\EHùDԍV<>EI+e \#ItW욱YIZ_Hp0b,+D*̧|eJ4i,t[JԥBB㡡#l;#̔> r4):(7C0063aGr9xpGG{D Qۖm5zjLbD*ȌCHAlһ4GH+;> /ProcSet 2 0 R >> /Contents 267 0 R >> endobj 270 0 obj << /Length 271 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NAps$mT)ca"9 f CZGl!J+ &;n,217 : #x x:+JN+/g!SA0:Lji"El8!  ) `l @KN[IRZ_5iQ%fH54*6604GYS\oj>^TQ ~' $T" h@( BJ|-Z:K5 > iPppm=q (gRk_\n LoK8!WqTu" `jpk8̿YF^[Qd. 6 ]f[5ؤX":7!3/Gvra!Ëb)۪" b.18^>:1' #m @sM:+5҆oZt#Ҹ]*YBb94ڵI@e/m/9ȩUNw]D*lp2eg줁rƎl5 ~FxIo*>`3W1*(B` O PpD')tvR O L3}PE _ d-fwe&Ji'PhǑ.v$wS1Uf&w[TXUz%$~1Bj $1bVO` I))^wrtITy"$IXsNi;SS :['0fn=u2`,$Nhɦj&}_" QRdk mNR$ѤY-s-ˆHQ$.oy. )-qP/0l$ 1~SÐO2!(;THc/;U"53M"ͨkd B!65 7. vGE%V2r!&Rt  B|GNtKIqM*}& `LsgkCbP:|m@|Ro--s45Pc7t$nYA42/Bʱ.[DtA6D0,CmBܔ_B 3)C0 5%"PU{77H"-ǔHEBI}#ͱ$H$OGt7bR[%*kT0Mq$Ӱ`;҄R Vn 1pQ_4?γO1H  N GS..DQBȒSBg=tӥMsNwNIl @ ؖҀ(qH[PpQFSU>'S>U 3QR4S@a@F~V)"!T-5qK..zCZTwhtHt=imI0MhW jֈ(vF07E\!s;ZQl4cOuk(vXtwnms[k67un գoKO*TlD.[ Z_!` v `a`aCcP|o<)4yfmA@ﶾ6BMz{1ջ6C_|VoWM{,k}uE}V2Wl~Ymw!SK7GH\7 X-I2-q89]5%59^)&(rRMtsl`, l ;=Ng< &*ve7'~*6b,z;ү2)Md94g}uA. YnˁiwCX#l}sj//k8~$SY93EۀPuq9u:a4MY6#Sqo/k9EJ)wWq9K"r QNMX2U:.Q__s]Q5;@uGulkVx8=[4V;طhu,눘y4Z2+CXѐQܡ(yqӓMU\)gSmVE~ZvX  בmlՔ:Au:>m[X 7)7E\v͔QGzWnq.Js3$@rG3U˘$S9ur+r瘆`њ68hٚ{*G2Ye˫yr[5{TsGYsJ~jmͯ7z{5P簛F ZY; lE97'NJPvs{=8GVǛO[pJWpFeEWD8L0c?}_t=aYn'9` ڪe8٭yh)R9+9_dH+%ԝz면d+4!WI9U[3;M;/UWeZ "#["<1 {8Ş$xSê%L=ot\EiW\M{gļOKnC{w&^g6'y࿩PR\z`PswwثxϬsϽUc)VÜ{.؀zo3μu]\] E9Iׯ##Ͻ#mXY!ӜI&ݤp}zM]:ޗ kqO{L"&-YsTߨRyZ WOabWaWdpacnDscHrejHyzu|6QXXÝɍVл9MݫퟜU] L=~=E1M=,UW=?W?yI1Ŭ m#%Y}`^fVY&܉\`ܔ`=ɼO"ٝE)!ٯڵ<U9ۚ,}iՂrgӱGU^ќ<#>Dc}>}6X9;M>&>A?)9?*ޕ`SFRAA$BeF$Dl^q:y}\>v ȗ]uz ,=Z=ŻԱ`P dQ.R~@m:-Bi7g[}>]?ϑq?b5 FBd8 x) 3FAp4ApXd: bqQ mAx&C DQ1%A3g.3τc)FCav HR0ZuB$&@h6*U^GWieLh0\kUe4;ClLmѵ0f0F!B/# Ѓ=U``9Jq| #D!n1H&qa:MwK3I)Q F#_ n,eOh܇ K@f;8\#O`b4sn)*ƫ .A3kcRr(d)jIeJB( $;Rdȡl1h1ԡsf2Ȏ&ܫLR|xMMq$Kr3$E5Lsܐ/AS$L%tBZHD8;>C$+_TUbs_UW?UVq>Wmɲ(Jдm(\ō3֊Bض[rݷᷮ39p0C(/#$.DU6񼱋{ϴ.8$;.$!g` 70h* A:5%1f80" /&* bHݢhMdSh=FO32L.w4:ʃQ{[-'ZN; @蛕 "~O)elw/9q[(d&YlŁth/Jn|jk Lj_&s?i[}o5%]i-˼=mw}1gOs{D\68+t^OwgX{XtGzڝ0o?E#zJ)gRi+A'@]͹g Q endstream endobj 271 0 obj 6383 endobj 269 0 obj << /Type /Page /Parent 252 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 270 0 R >> endobj 274 0 obj << /Length 275 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1}jkL$CaI=@+}%nZ tB' qD tVkEu,H,;Nl!e4]jKB;>q |hja`AD:uN.E08}UfS.H+#.<AG"tIGR:b!crErT瓠r&S9[#0 \˛s}=ZB{anɡP+kmvet3"^gʂF$ 0%TtO&Plg0in9Y瘀rc͕׊Ƙ0fKf>y{/zRS i2*|&`2~h"l o:X{` DGfm7N7\vݗ:$fKQc,QHV]{t, 0՚SbeVV3YI;)Y]wNI47)ɜof Zb!PpiN.4h+jg"L<Ǚ- /erҦ@hm疬PRr]FeS-r\Si oQDGI"Vjg|E: Y#Sa.l9:QL5 ޞJanƛIBŭXHvhrokd NpYнü R+plMmB8ګ)Li~.׊(i^]zHiEkdcK`NbAY`@k4a;'tST 0|"FnH !B)cܨ\s/[n6RRG q8KqHa 6f86L9 Ky$G)2VfVlXUȴ9+Kv!7DM1ҚlCQ!$zyH$*i *Otvtu3\wDiMD4ݘ%LdhRve'CW߆gZdY$n5DY{`FJUJحg`6*&ٞ3jm*|Fp>CQeaxAXM8l1Ef(Ϗ+IG$mR~8f}ɂwSq,n_0g䜣2#T%oDo \{ʑ; ߲u5/rr`952*ꗄW?G s >AO n=a7T =f_q# /"|bݑvnRTܹT#j2ʰ9Ųt0a_m^vbCZ珄> /Contents 274 0 R >> endobj 276 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 278 0 obj << /Length 279 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1WeٸⶎqARvy@i|7}j!;҆jᖳ@e|Xk.{ƍ!nf0;>3jw$QaJhmznA(V͕%BFˆ!e r!J;AV~ץg'fIʊubn}q On8hЮx+~JC&?`ʥ! >o(ClrD%+&6@@0@Cpt: hRCM#x~]&b`\ݼglGf,"iJRx&6~9V UЌ)P TiYI3L? EGCUZk%4\+fĚ)fɫs*D)9{qM ` rL8 ^QDm*>wETq/ DC(+qє$(q"@+boyE$8;0U a 9ξ\Թ3eS:EA&TT3`1(YmW"B3N|Rf`90Q֒FVoI΁Wj"P’WK 2Rh#VR+(̛@Cs<3[`@z-!<&Cpd !2q"ÐmY#֡3KPs S @M)Q8V"$\A" agm6Wm/-C.4K]yTT'SjUuv``v3H>*T/E@52AUT0g-ȌrY%HL2R@vt+e[pQEB?R"^f Tr?y֡q}4av1U4cyj,.޻5k gTZ\Hc +9^zqځ9Cn=ǹӪ<ކsaoz ߵӜB9*Nsdq1t9,kT\!6|r.H_'՗#3P9fyTg\8o2#_c :puyvIvߺ }˺wnF-?g~`T{ vUXO_ |Ä@dΠ)b ov.|6Kn譠] /m~` ؇8 TQ0 ӭz . j#" MM` `'r܍F4L0"l&$lωoJ b endstream endobj 279 0 obj 2494 endobj 277 0 obj << /Type /Page /Parent 273 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 278 0 R >> endobj 281 0 obj << /Length 282 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1̴r`lq Lϸfp:4h:s=OAFT+MAut~cZEo㮛t"FNuM#YW; cٽY}7#8]ł[d:s lyJQOe,m1`\SlԤtΚVgɩC : #OOo?Ք:kK(v\I0hm@ Hb;iTUn૕3vXc[t2vhl>x2`k~,t<&HtS3//TXlʂ1R8 A 0iA RDl2&Qti贄I4nT`B E,!^OԉHDp8AA0 dWJZoqp4s.U:UhzDF`Km*ϴ;L%;Ly_J%r2I:`Z RѸS@*N|!Wδ6RHnJh伌ȰSP9k smNst9֪n}JwU)5nͽ6[>xB7;p]:93?p^<<\-zFV$;o+vL};B{.].{\3/?d~?xqh)k9 y(n;"̘ ,/Er.d>lYS!a,C H+ ʠMVz)=(z ftU ҙM St࣫B$Hl, qhef͙t GτCC˜v!{lNFn<$oG&wg 1lZ Q%䱯  qR@ endstream endobj 282 0 obj 2811 endobj 280 0 obj << /Type /Page /Parent 273 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 281 0 R >> endobj 284 0 obj << /Length 285 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1ֽ7>&ioF\""-TPìa[Ǖg~n0R[!|9W4F`FTkzl:& +94 \*Fi pH\0w叾[ !W@l|W^l)'D*G]PLB.ė33FQ(=7ڠmIy a:@R%%a0b ҂n[c~q( YE$*`GzLe>W.b$ 6ea|cJgZ.0B4Jz! o/"9gpi]Py( U|@s%lrl19?4<Ҋк0P)`RMgQBPi΄ TJ +H5"LGhOT:)4nY3SyC;tsT Jb` @Ȩ2(e?` [I8 MrqtzkR"7S\qn{\k@lDc^H T;Ha} RzE}lWCWݸSOm:igګK:*q5H5A< 2P5C,2R8prg씁bx$v1<Q@XMS!M6]k\jUQ+^.h';m6 tJ{r:+jD˩Xc [T$lJLY)èЩuCȬIxZLӛgFՆ%e >Ma̧limB1ٚ52ˣVMENm˖_M uvs6g,e1' o$6}=͐Ѡ lHs 7xMڭ7jD'\/]:,RZjbuzƻ*똬gex*Tz7jv^`;m?fA Kq+DyjV3c!45bCxnrW֝=uKŲ5Z$KE5*xr vBX8>Jg\f :Rͯ3J\mmW&p1)N$5)ю2P(rP#$ B _-%pPBXg.. `@f`I dFz ` 9ek (孓 A2xo"rd;+ҭ3sDs3"r34..y*5r'p>Hr7s[7H~S3z|LNŭ8k01

J9>5W+4H!S,Q,Nw.[-hR"7ABPv"FeC@@YY T&M𽋠'@ Q < 1OY҈W==3s=ԉ> hI1q0BCTZTskK43s.s*WLKO ,HHht>tAMWf$ti~HfQA OQP&A,9kQT.iQh36C>=nPn MA>$$nV>`0 3fIc-Tu_T)eQD %X1 RE(7cz7RD2K@ YtS*JdW5l9])Y;[` )Zb A0h Mo`6 p @ @ v  `(^`40PN$A(y'@Q @ lr" @ `fp> /ProcSet 2 0 R >> /Contents 284 0 R >> endobj 287 0 obj << /Length 288 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb10biݓ\\!#^^ ҃G؂[G@ |q$x;Ol=خqFzqP+ 펁V@} ҋK"ȸ &q#^ݳ‡$&bKy=E>I&K&]>a'p(?G)&R@R/r%Au~S AX+43WTlYT;IY5Dr"EXQ\X\n1FOF.OE0Σ?]ʯ^ԍ0XIQl21I4JʭM4SabTib4Ycia <4MZ~MJ0 p?0"@ҴHӫ<0 ^tC=6t{LT,{){sU@Z D |x6Ue?i%?okim`E>BpM;AQ)B3Y8"&qGZ_ [vl7+.uς]w/GaC0_52iQsq)F+Q Uv̲WZ%qxc13fxW3=xX+0\f!lWf!:ƭ;g< V)2P)\$XH k}} ?kV҅;);8G8KYS*!0+m=t3o,I+]pepXRjK] }r9Pe6-WwIPovL51ۋDbϜRSwPLuVFhēmL03vT"t @:z%{/(roFV|U9x `& KV>+'/kK8AQ  UqZѿqτ52ys-9٨gC]uՙYܫcr9KHZѫXrqHEIG쯰ʼnR[ع8r٩w Nӌg C rւuMqoz1o^״ {t)2D:\.zKPv֌T֠=ZgV:j0zsiXEͭ=yYBo4o+0'݆i[]9['z95,ݻH1}`($f6›X E2 Õ sû)<,Xvu9Gƿ/B+׀/|qc?xZ[X[+C\=tqLWb|Ċ(|*iJAƥö4>\%͜ o9q\vtPk=8AczL+"V$*Ip f$> /ProcSet 2 0 R >> /Contents 287 0 R >> endobj 290 0 obj << /Length 291 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1òզJ f A'I fM4KB87 6-2!72A.%#1(aB], Ê@91H7S>4HNC 4CU41Uu#p 5PAV<"503 2*0?LY6QnGg03x40\AhdQoN8Z0#2>`TÈ"@B/a \,PHβ (B,Bx** BH*+8CLGBn4m=aYV' 6ۏ߲B0(i~A[sEa@<ٵqI#&A@)@#Řٛ8Z$'Y 1Pk]g@3OHo:g#t9Ӄ}9߀ %M,@L1P..;At>"Vuq :j`;A<@"ahJ@RuԚ6H"#!D"(h!|dh |' |Gy jA7rpB"Jhq?hS]q6lJi/P#Q G_B2Egr I7@ARA{(i.GCRq.@ `p %2e!T$olT/JBHلb%64{e)32|ńB UF0ԨGo'%ʘ2HȢ2!0X0RDgE2dJy$ Ny !9G~I6vtke` ePm ]Kg`hSC0ts%Gfbh\K&z:!|:_AKGi)6 -9?SXI}3"ƞm+&6Q:CaTCR$ĺSj5~2}/It֢5ղY$uNVcSi_k+I$%Tĝr 4](ΣT- fsnÉ4AmAUmgŪ/3N/Oe cڋc4m՜&ڛlS^Y-k!ݶ`?uy@jE˺G(-&uNSH yLYta kΩ;EqP PL@<+ ¨<7A_ }P8ulREĐxJzP%#g8DxVQ* qLҭ~(|uNǰZL>a#liV%ĉ~9b8[ٗ "x#qnayGٖ:˳VeK ciD$3ZmlfL?E۸X.Tei&3ViO}&HmuwT 8y|h$.sޒK{|\G}BGJ/6hHQqI`aftۆ滼7Ic&h9jN]#|_|(qH&oV< o _(ʼnY&Y+@D·kD8}h^/kIZS[~/?kTIh$(pz N.BBάӎˆL8̝P,K-B`뒰psOI-ZhcbQp?C\p 8~y+*D-DXjr8' `FۯPPox/gFD ^GN/F 062*/Mq0 ouǎ\ 1 gFʑ4D..d3 3J&yqNf1K: " ;,_"kfhѰ mNw1M8/ ˢl/(mv gh{DTX` 8ʠPKuн q),T;H 3Q$t<⥯$T%Gb P!RhYoov(*O0.&쒌8.I*1)rl!+P(*i u+,ˬfL&8"l9 2ٰY0i_0011,$1u23˱^pO Xf`8$؅.'MbD'$tCPȣ H "rB42+fFtmE&Glq;PG<8o(1.BI'+o23??RANKAB4BS?jC5)L+5g@50Wv4,ug-.yU3BsCT/XCbVt?'VtFPl9(M5[\.Եȵ]c]pguܶUGu3B^@ s[i-_#4rt5IE!e&\#+REb4#K#JoJwT( /N! _C>a|}н@X$H MD8 6t'T쾪/!T9Y!@ga'3dg ޞX/*铿k+SR}<r T\lam VG,vWWVrn6X2}AAn'p2ΰ%kZr//MOH<ĐQ)l1N->7G*KGQwSFuE_WAcu3C`dЖM$?]C hX `%vQ #LeJ3wf~tg3g-gӣhF `#ogP9›u5PЛQ$Q[(h}'7l7?m,?8 ()7IȲ6مUXao8M7WW5,;A 9Bp.T/5M-]k3GJPh%pG]XҐؗ?<%럙F懟Uߠj؟\X\X\C_LMuxy x ID[Ƅ\'?)J)xxzvc7Y 7"9C92v W'RD0</JdR$JяqzkYf:o9m9y0ͭyPvV*ɎI9^: VϰAqy![Y#ל!s N&phgp;͘Էy۴¬!U)dAvaSoqJ Ƿ,غ Qt~T[NOjYۤ۝{*˻m/[ǻv5*;ƸӴ=̪/ 'ە#{ '?;0#'wE $de`HfT@;㾣Q@`'H#Jİ/9ļI<`V(\v\^%dŨOdOBPXL I >Xny] 온>ȾŘ~ )j.CJ~{ Q?ޑ{‘]ۣI[,S H_M1vC+XRV0GK1R?wVPh<:ܛ`*hK^ %cd8 @cQ5X 6 f.wfKWq{ f  bA0)!l8 !EB$l\0A@Zn"j6c8(f5C9t'΂d@hg0CLb. ؀eApeUFCr\z.V+ЂcZ߮vATlTi/F1C}Cz=qޢ3Z-]] y+^G-ۆz_Yqj#}qv 1A__= ^\j2|YMP F #7nº)n@IH(J%&3~4>7`ҥnDm5d>`=!Y<:.m*sjn4qb>޳eϋ=̎`/".aő{EfMBi? Fo')jwq\.<72<3#)JңdIIS*w-K1̡4SR'6M܆=ܩtO3? P5!Dg4BL|֝N'i=,J !3(] 0QAy]Od'pv 0o[Xf ф!< ](+왇ơ|"+uC6pK0DzO ir gP#3MӘ''!>Ңa_i%Xigf l4g,ƸghHS\ >U!,Td`EoBΙј}!d{R(ҡ,QsUˢY'u RmXQUI4xǬV :b}܄t]?[Z ypT39Y+dm 1^cAFȪ/-} Z%8U+YT'0`ia1^ O}t@8O8 &SHA L$O3=_.BlA'>2QM_,sW&q¶ U3Tx -wnUi)J/me>|鋔ѝ{.DK8d F&ʬ!}mLk8A7ap& l(݊`j2zHbm` o/׊,/rҷSq䊨 {w~J+> /ProcSet 2 0 R >> /Contents 290 0 R >> endobj 294 0 obj << /Length 295 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1DF$*03 @: h1H7K53 YEpY]U! R풢R;7 : #H9ѕ*D!ZPd{FH-^+Z3%+(uXM,v~>oWTÈcx64@"(k2 ,)+LK Dw'` JeXeZBbj0 8PvXn+Lo /W M$M ;Ɉ0¤i lFаC 2̰p@0l0o lGo  $׏K0q4/0Bk] ^o֯Q`ϑY%w:̀Z&Q"2 iÆB bY-¾P.0J)@Q ҭ XTKGcZ/M(왠w.Ÿ,>m&А|%lS ! y"P,w W Y2;! 0(pRGAFC?$H$N!1 %q'] y#&{*b3S*)QҨReO'{%r(rR+QnR*R׫-JOB.bI0f T*+p29 M$ā0*)0.%mдc +3>)%p2H)m- @|DXǬ F t1A1F(k) 3 3QUI/䉀1 ;*22$҆0"3*R))QG*3p==_ 2>?(Se)R= '-E&3i%R@?R|r(3BB)/{)1)ҀR*R.>t_FrХFm/kF4oUH2qHd/}H~$t5}0dt S.9e2g`V@Q2€c3eV!03@ꍄ J+O)35FHM?X)7E;7 ӍKKB9@HMEL;E+<_EC,=}T!>0+tt+#oIUs>Pӕa@2PW W\uBǕB0sVַwVC-(W=(լBEWDД}*] Ho Y 4{+sH _4Z^mM]l5]`RJJB nujlnMS \# i@ $ d`Rq-S*u6?43@HCT@pi2 +Lv{S\3 @;mI0qE 4MX* <0!k5]eZp!Ue#Uim5SXWmLX_HQYp- %`6 YUmձ"`[mD`kƞsI{Es_,$U ^O_OAHG^H_a2[wg,/B>U J)+J`AQ_3 `I z vWRe!:MDB3S5drP" * gvoOepA֟$v֯\mCW66l3][6]рMmsmoR,o?9uXVиoUp'#2_YwMl˄8!'KOWCwlXEs>$UriI\܌Auۇu6ۈX\t́l2ar#?+rMa{w2؂()T1& {@}zJ7P55z1{-:8}|-ڃN!H}oM{uOZ ..᭨ .)2#n;L>0z\:`suYmEpo` Rc^sZ.}4̀C+E z@7qXdןeHL3!qw:k$K[{vk05N)LIhhF6g֏s8&X߫AsҺMkȗsx-U1>>+Y>Zo3 7Z wUpMq̻eRl;E .[si[Es<{T{ @/؃=Zͯ[HǛ$-9)+p姛ikUws[ǽ-OaS$Ys‹xE2Se:szai:a'c@b )1k^)_n j{"Q8ӟRJJ-щy,_lm_p4FQ3l0 BP@1CaX<& G"2.JeqD*KI1@Ѩ%EHn=fRY<:6h qZ>EØF'^T+3KfӦ endstream endobj 295 0 obj 5554 endobj 292 0 obj << /Type /Page /Parent 293 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 294 0 R >> endobj 297 0 obj << /Length 298 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb18ӕ->nȡ$'B; #pRd(ꗓ{٫{kk* !6 H}:<j06WXAqlXYD'6de^0U9R+4WBǝFЍAR,cXAD2l ɱW A߂/< WټXPEPv'ĻqN ʪ؍ `sA^`k4=6sFKq=H⎑$l+=d[q=;|E.^M*p y@0%~CT !tpdwiP?FXTWöL0j+Kn^'4w>9]@NCJrw L2?ORmJ$,r;FOS, !)EDv0Cr\yw-b oF,}U7-J܃KMUn6GS) XHRqLv 9Zcʘ8Z͕cJY.ax8EZj/S3=(x9ձR38zhe4$vfhLIő!(E%ݳenlY4V !:Yf[Vz_Fp].b]3pjS&)"UL|:Ks{ L9K[$]EU8xBs":A떦pm6]lQqvLJ/{ou![aO>;a\Ԣθ?p_66'x&W%&} (ȵ0+s׋{VhTLV:エK%gҬw1mԡy'X]A^b;u izd sgL|MM5VX$k2[]5)C~ tBM,˳Sk&BҼ+۳}4-[&ۘn;wbk*}WG|j3< 80g[|ģ\U>T7`<@cߩWZ-)Jl.pvφpsAkrvP ܊ 趟hOЂl[ 3e Nfhl *p[҄0~qHL.'p%l\wHa 'nOOҡPi@l *k P. pv Ѓ (il  phĩ ȵ %s &Ѱ1q=b,k<=x~+*%"X% 9XA'; )~N>$'>.9ˆNdɾQX{P~c /1: @@ PL* mz:kzDU`>rh'` @qsq>?d(ւ+`o O`ZO0deT*CXV+&bK? q T @.-)"*P[HQHV:IJ @JdJ$tL$3EoDNN'"tOQkFQ~Qho'e.S%6T%@DTM+Ue[ N%l`i-W%vW~X%X_%YZ,R[='\]]h]f^1^eMsu1Eg3-:lLuTTS@eT&2b16>~|6&4V!BtW Y@ΔhQඏ"on@b .kO*Q)@POMjx dID0M< WF@ J+C&s.j pl›4SNmFHNnNƉ;o7 PScSRS Q9HTRQE @o;iAu%Pj;9<("q+UNAc#5sPf "W+?X̯NY TNS[ZUUCe^Ff´!hcD0Y 4UTeUhkbnj$RT{1!͎*O`Q%UTHc8CFV`}KL%_AL tS4F,Wci fgP&G ᕕZ1hh 0LER֋0jwZӏhǖsHHB mJʱ;VjԨm WvkگV|!W5Y4p)Y^Wuzqv# nW7sW7pm"8bjU$liE`XY@v`ScB0o׉G#tT}H c*2r2"T鲍Saz5'.OvUMT MdEfmPẂW zl&Z9c֎~]iN!jLRl.0UkkV̦&w5XP4$nj\KWtOOlru_nqꡆ9 Ն2uX}0sU!uuDd$@ܵ g_WxoW}_xku`aO3IUJ&yPb׬UNJr^'E *; _}eo9~qy5j/!pɀ<ͬj{v‡x"O(y`>V߇l8?pGNYO3[ uyZ9x9\xyrNUsYt qB}mucK-5^b_"%|WwFxWG(*w'>nx㠏TI(BtZ@ kTJ+G@k+0kI Y{ڕmWkh nGVY vrCƎZwe[k_67 2xZq Z:xGp>kX]SdB͚ZPZ5Z_5WWE<$UoX / "D @ @L+WЕw ?tEuKR 3uV3c{0lfg8B Cp·3K}u ~V௠94+gڜϹSۭPj}Myiڿn:ÿڃ1YӜ6k5I{'>ٝqH}4lc٭[AGUn\UBdM50ؚo涝 Y @ @Z @@ ;E0 _xykxf7I'价 bCkV/2Z)tTW,@QzKeN zF,[f4B;Z :$cJ+wevYA`դbq#%"_vT Ig|L<@Aճ- m#cVYWN՝ Zk =Cv+]`#Y ` eU].N.@%BHVKc "< , 16o}<;}95l/vH ڌgoY^!8U+oڎ^+h*m\S>-mfYxt+\!=ɞteM 繟HH&m^uG[~~&j%6A0^F[!t^xu:V=ݏkǼUJ ɸ[]'\\ + Y ̩zuqͥi o*&-i꣔bX T|s|,2[9iR_?6Eb׋Xd f%3qk@GK/Zr~09m2f c!1џ#i7gF6(e2 Bp̞=ƤL@3 DWJA{T>(`C@|*W8H2')Y.c"{)ΚiK!XF4Fi#rv3, :z8_ُjH JBNDHxdK0˔H̢QrK6_TJPZ$keIhA+ bqă4b('[ù$T"ɎX endstream endobj 298 0 obj 6633 endobj 296 0 obj << /Type /Page /Parent 293 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 297 0 R >> endobj 300 0 obj << /Length 301 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1%d8^H f*Ə3VAƎcMdYo4YaaoGcP>X.ezA {)lD3Phl^n z,QJas-L9xÅ4Ti/ @ 7#Ґd3*"?FYpTWp%08j WqtMzA(d O+ԭdˢuA =!›- Ɣ}9Q$_vbHm6N#Dw4^sM*LΨޢyڦF^2;ފB1iX҇yO$SrS'N]ZdNIBhzi:U 7TjeUL bcPj;׹Moˠ'3&@d+cJ^ 8i 5ڷԇ\兮1Xa匯تa4B&!"" Z0Td gK|fi Մxd8! sLJi Á&rmE3Q}=pi9|PPvbz ^^B0 TL1\n 9Pz k;uo饦rW~H:(țUuW:[h4X ,о_p+e mk:MSh/N 0&("1T>qKvB`SQ̥ E0N}1ǡ[DdQVIRګ>2샐):25'H5/PeEzДưt]HlΔM b'ir!Jma27Ohʁ`!5ԴC'.frYh1voX-Nb&L5m_Uk Augzm2id}:I+ zrk7VܶM٤z5'xM۽#$y#kVZ=ը.;$&ŇRRL :~ݚR1^m -p&gs]P]keTUUsv]L \YiHe:&'<ȼ4NP̢ol/0C2]l:Mȉ-Ev;hoZ:H6=.߿rn4Yh<,[J#f)F˴ZWtnsuea֓sX}Y[6ctձ>c;Z%fھ,k{#q} b pIfھ$?{z?x(gs}O Kez5cd/rRNBFZ 3,bn?U˪cM)./ E`PNCK36޿$*Pxh0Vq G-&v &MPB%cRVЮVPm ̓P Ц*Ю OIM(`n5bNK1 4'oΚM(,p"3f-H qIMδO!d h%ctD00r ɢ KP:)0>IfQKN%\ RCM*E(I-) ω Q - (15 Y OE MR*Q /rvߒ/#w)?i?mP}%;7B&1Lr;v^r{!u%"r-(J'i?$ҕM&`Ҁnri1`Vk!\Lh*20ष v  $XE Qk^Ikpv]S5`d%_C4*ftBMW(oqqVuXXRlnHw,Cl;+u-.̀-HR+rDt0Q NI%)KP]#],0Tv LV(?JVI3{%d ncf9AAӚݖK:e30bPW*2ae5 Me;Re3$+hr{-e5Ch$|(O Y2!V7j k2(vZtv)ylUgk{6l;TOmV+Vm#wpV%w z 4pY A-%-iz56'@Z.QM3/[0^3 O_U_S_2c8?ŶLbL.@ : rua۶ O!Q /yh/9lE:VxpzzbM!VViVy5:c{f*{e}z6}wh4zw>P}wi YwTa6V@* Q:2}Y/Ce}X%)=|6 |,XFiep0lp'@@ =`h` B-b#\F²I[UyXz8@e(c:iԄY~$ֺ蚊w֍~{*ްq>O>Xt8}U❜^]i>+]s>מ=V+KN $p@G=q s.$ e]8irp1=^$I/鵮f+Fl)ɔXTWd~\ 43F `evw?lcYоccەk߹5b_C:^~fcޑQGo?"6C\7FQ5Bal.A0D*Dca^3E~ FqnQ@ƒf52'e泺D} S m*9Q5]l]ODCxJVhb2U;=֣w[a'WQH5?Z4).KSm5ٙhmL *A1# &  C)o9j&àTjFp[y ܩv"'GmbO@Fn]Qي(ppڿMЈ)˻F Mc"L,%;d@1p޷Ⱦ:9ތ` (slq,˲* Ka(!n7 0+2/,J!ҏLڷ3dƐKtNaͳ/Ot0Id@R˔T{Bɘfǒ,槭,t SqVLkTsG**~ɳʐq]'qª׫XV Ŧs eI5ή \R # l،P֍ĺZ{/siu+D'sܸ2ZвB/#t7K)c:5 +ZslD8A<AT!B : llH-c]2G!-h*uvʩk2 %k5Si 6:&3|Gj:M>֨횞K6FOo캕KP0TՅ'h O-%q#TkmXpl3ZW @hח&`)O"6utm\vA)}ԗjw5=u'dO?nj7a8?p 0@8C |Z7؃by1bٌ03zB&DΙ!TΓeE2 ƍ!nshQucF$`@C,}d8@n (;ࠍm !20ʒ;0FZA "A 8'R5Fܣ8PUqh6G$bNm%҉dmkn2֛o"!;hq+>5ۄ!$']D!k7 &I:YH%VU1S) $Ic#e3vKڭ%j^ʐH 2\[W}Mic6%E7&Ϝ6sMu3\I ^ /\"-(~Pxl $0U_LR.#;xڠ|@lxP&T^TpuAf% gKj <"`AM /43Ԣ\,nsdtLw$.JՒKK%eLtI/$zN GU# &x#u۴ծkpUUjU51ǐYl_1fFJkq;S/X9bW6/1f._a3G*D/!-,rQ)Ԭң{T'%ʪ,U\ A[-Y\N^5s0"ʭ-ꑺҴ3n]Hvvm5ߝfӍʶ-K2@|L $e%!Q6Oe ŧn6mVr.3jL驕 zn%!] ;Ư'.)&@غ endstream endobj 301 0 obj 7321 endobj 299 0 obj << /Type /Page /Parent 293 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 300 0 R >> endobj 303 0 obj << /Length 304 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1'[Y&}y\'>r|!x灯FKmz6 MsM6b` 9y:: CApE@f([ceΥ:m} ~Lvv5Agj!4تC Vt(V[䐼?N0:&gc_ }!zE)7B_bjV?J SB\OOK. 2[.C,S~_!0,^Oyu/_ 2xc?;MȲ@=pgY|H)(j?;[PAIE"y;if.4( )Qo2=&P[QV@jM*$V4GS$<Ҫ6FP)5fħV=P:V^.4jK\F0+V$tE֩9HHLt~ Wb[w|靣 kjPSYzd:ٗg#=Vj,:4ci,޴ N;3M2ҍ,YlI&. O;ْ/Lm`h2YO'x~KŲE8)ƹ c䍒G>R4 *g3<_[p\Cu}=sɺQڻ6Un]jNiݛwL8zg*̢~'fECt Ur a=`MbASrlPn{LY8檃jAM)7+ DC|"UZ{  4xgߵqb\iȱ5ǐ_/]ut w.$\ncˮ͍Ba Ng*JEbmJromZаC RL1KS*(`p. jQgC0*n$/^[c>)+Oo'R (I@QAAOBR2oؤmbX6hX"(u~ϴJ3YYloD[g[†TvU0YՊ!LԀz3mHHxC^1Ư]KJ N+4JYN낖YX#F43mb‡c_68`-88:V4Ε$&nWeӮ %Y@YNu-Q1b oNM*)S 5BsHuP(KZ8y0Wgkv MТ] ~%zd $g#~II$bt .:$fd>d[d/%0E rin)ouX1[XfaeaWF1D66O.60u"M[tfmZFwF'be_w]7</J7WwHlxU2t2y_${6 `( tFt|.5Jnt}t(} dHe|OҶN}ȓ-nfPWEdž HO~RjvwA6c,nџ>8G8F3A!'GWMn_G$8dQ}x(XHXm}-MX}D iY{Hd|~k~~Š/-0q@ؐK>$'HK9 6٤ bOXH#H%'@:@ ZNk L*>Q8'C fACP% MqPBv@(D AG>i<18Wu8rOrlFbW\kْWy ` k RU U l(V٥BIJ`@JK"XKMgvLl%Lkdڢdg,kQD_EPeY*mE%P,E6e@ITTUZ88Vֹ,@imXŐYZZ&Eu.]e=^%ny9(_d'O_8wfJ!媌A?s2d?!-c8 $gφꋅmX&F/z嫈hz τB|: ];(ʣ); ٲwXp_ %+>!ki[5KhXb W0؁KhWUɶwӷ0^ycx#] sz=0~.+x=>~Xk%߀ dW>J@ҞQRMjWR &W߀-'2v/IhP֍kUVzogVM*sUWKkѿeМC` txtz"c.B0wfaƆ7YHDgw.NZ8SLpIBs&ZʔyG#^l*33|[@[;Q&AHdt8mys1H϶wνM\x#|cΕ";Ϝ/d8R2k$;PrX ;ߠ q! A о>i2qrxU¢; V|e,ڐ=$0uI?2o USܩϟ2? Bw(]i[^^/i5[_<) 矽4}7ݚ0D[F-ZY3T1P  3Ia:B91EÁ5* b5@@`8I",T QE$$q1!c1e?TAcHhn.T41 #AeApl d$+ Kfsqm2V[1 Gf2FN[pv}#91Msj n;|KAɬ N}'dN䯆a\ßp3A/B@aAN $kD"+8ľ<'(8L^6 hBPe40#Hx2q!1,x2N̕ɲܹ&Ȫ jИ'֫ X-BԶ2-h-s~NZf4(O*Ǎ}t[ 8@x#q/k{P\ >wKDž3o\e`X51]Y=L0c(aM.cpS:,]#*֝;97ӎ#;k,>kQ{dl7&o{>Nέ;D <1 BRir(EDт@9 21#`@) (;eW4SUJLxnU}m[ jiE] `PBֆ*w"L5  x 2[ZFjκ{/4տ6?]Z ~}gIbyـ@[mҖ lY2Ps=ٙښ\21a#?h(yCјN+op_%&y6ؚّQMEVɁD"qF%͡ 7և)ԌR1]K#Ħ 8 *Gcv#"</9g0]ϙs;= e~EtiF̄gbqD:;8,N/&D]t+7û=:N'Lu5r&څ KL}7sR\#{$ asJi՚BVtҖTc+J jےb 'dL%QJV<tU+:eK^%ZWf|֘hi\TL+s.jǞNWXi-$[_c~ֈjq ~ m-ۛl鑺bDzk.UK>J=bTF\JZBq+Z %EKQU%- ScN t򐮬!k:)"7$h2(zfN83V pj*X7vȏɔ*:U| @R9lM ڕG 7c^Se|,%rγj,fUDdJ&H&DUdo.kxje8̙3Xy8ve*٣C7uηqThE^Ԗ|r?M/ѰGgg[9~cjM7s0éÆwqy5[0Η_Ⱥ2[a|pL 4kQgd?a{q 7<-fts =b51rosNq:D6SW r ,w`K]1 4q'f%g)3)x 􃁙 X5`u9m]}uI3s|)0+,t' %(/5\2) Z 44ú,/›6A(#p4@ h endstream endobj 304 0 obj 7962 endobj 302 0 obj << /Type /Page /Parent 293 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 303 0 R >> endobj 306 0 obj << /Length 307 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1u2nV !#_ZRSbXA0 Ho0 TR *6~# ]C@dSM¢w/"Sc .J \5i 'HОIZ 1Vx154̲Xi:4 SiӫakѾ@7x/:1:ZDbA >G|uŠ F&֋.Ӽ(riTuQ}YߥJ8R֞B6"6i$mRGCtnSeES[#u+MؔR#yaȇ:( ݍiO x5Hmryձ뻈khGS[͂ X_bmTG9X:l-UŽ*B!i >NMfԙxZQYO*e\}Im*ұ[0LgS!Ka3Wиi g5fm9fSiyڶgI0<=K:C#sOVXM#&c%' R8%<#pc5L* L' Fל/fj@3CwF)i6n Bb,>_8[qX;}•+giS˵0XW7Uo34e/Vqm! g΀5lBR@$bfҠ7`i *,-B oZ7 MIr-¹SJj"tHuӝ 8Yy _M'Ju/ ޛ3m-M N 4/ Ht  4 [1}9wo3QEfVf 1+=ei\#9~ٗי#qj(sxO6psK+g'NG >W/eJL 3l܇!e3p(n~Y;Q\L\MRgSơD/8>c2'H&qZ!/Xe\H|kzv6{_ЏwAV4w韼Esx'+.U HH< W_?tsL;O/ď&Ǯ Z`g v.PoP$O.CnFT@iΞ/,LaNh/pK +/pgǝhjhʰ0w m P> RdqiDOn- C 4 ѫr+xB<@M ӌFMbP lɴ%xօopMώZD0Ѡ ڰPIO+vZ^fNjDbfRSf/}Js1rNeppqqn/ Q{ PD&ADa>oFr0pQ Wl oѾ QjrL $[([F6%*[ Zk`\idOOf*#Bt e\ifU@  .  Y/xdROmO&)+bE;lbtܾE-īPALJ/,1-@qR21,)5Ѽ e/i,3 jp o11o!S 03#r.:z0-..-,ɩ!11rF;SP3e5)5dqP67i3C?8 &7UR((`p 5#P?:@8ыc$khm/'ooZ+^ kr(rҌz/^ (Kď}~S JϦ_sN$@,qb@ GS2iB) 2b8"l9D3lCDя0D +A.7-CM3Ƒ4]2uM4.w,sLVZ,s/*# h0ঞX@@ @Z5#Re=+0.k AUN8dzYYqC?-)1jtZTNfv!UY4F4[5[V[[_1!\f1rxZuvuA'C4\UH #_H4IDKsYaJ8)7q6381E;8#MVC7,GK!U!ic6"x4jƦ=s6B3iIPB /RU+?51'BKSDϗQ+em65U^ YiJb; aӝakmnt19[/o oJ/5pOTKqqTqCw^_-`HWG+8w;ptaGHCINkSYv.eKaJmqVwMK 3}_JvWGדw H׃vk/NjBh'@}g WB eZrPm'@_ U+|'di$@eijjBt6Nfk>ϑ? qxؔ|NNn%AX8hA5)xobM/(xM\ 6_sՅNrnxroPkysΙHXMtQX[<$؟uWMu4dv^ (xbbcSa6cIXdH_dMXeLaXˋdN=[pM7{B SuhrtMSNAF)L!I>)=63&^+?/@jăz%wt!iZ9y75ӆ'_*L4Iw/!42w)EaFsLY7tmUG9`8 UNQGwŞ_TCFoYx6Ú{ xSty:VNZyXzVajAi#"Hq}$@ Ħ@RiѠ@3Q&󦉅`~i_ -!RU(1'5/;גSy+/(3(GKk@QUXV6K+EYR\Ռub\g8<&+FrQ&.Vy28ci6 lCxyQ Fw vz;sծ9ٱDn?U.{YB;6?g; x?͎bMƧNL!Vߣ%IIzKIW8J0"T)\Y"SɯIS*>M>z>_a*+0ug:Ygn7-9u\灅 CC);d$1_R.|+pK+Ü3[05cHH-Fqߟ䋡|P|N2Sʜypa{9ecYP8QiXzUUI?sSwzAz9'Uo=g$^q45=<Ǚq0WGL|R$%9չ4<4<9֯e\Ea[˝?Q\eKme[u}u6kV[n܎6p˸lQ=Ӎeʝێ\=;AۼGz}gM ;w{ <~S=hh#i+Mj)Ӏˀ% v%[*sڼ ׬=9/o<_Y=_]c<-^sɞ!du~>DPs#_ʡ ޞi3oiڋ1so5{SF_>pVG>L^A{e{ ;z0SWwkth}7~~3'(VV^-^0Z~48M=9IVI>Ue[];G_oqHٙ]ɟKU{s9t|ܡ Ϫ)tE>B8ca\7Cap r2 ѐ@hD*"QH4FpX<& bqQb.cE4 c;/LrXa鄆NFp1[lB*V(\8 @l `1 rT;o2DɦS n3nmsqB$1J(. *8e`4. rL`KgMi^>- G=OFv==1Iy&Q'΂dmuiŲuzj(2dR9@C0$ ˔ $Ⱥ2p4A Q]B0ḑqTT]±J'/ \vq oЛMR@(2BQDW(|ˊz1̭®͌f+1FLB1o4Ʊ*Ъ+TʐQyIGO?j-C&QR4U ARQ=PL-r+9!Ns„H: 2H7 :: #鳁jaemR* #h Ľp;mSX }np7WKv߸,].S k/CC߻{#tl@USu.5<4mE6EIĆQVD.A>tQ 7c \m {~MY8 g-|sI0ED>Nn3xpATj(vivD%ۄvN0s&2yITSwj?HI'uyҎHi@ZO&&6TA'/Ɖ~EZam-żye,@+_%`3l-ACNN%N@1@ ȉ%]Ц2 Y|0f0̑xVԣ_ƙy*щu18"$aHE*6 )ڥItMPgLbO*q†Kw)HgJ,CQ76h\?UɠeraZO.AMh*jS C?&VvS5kF䜬ڵXNPUD)o[WFVj't̺Z֓#5^VZzW+Av"B]J+ nMVu.:bCL9K`콽qKrꥺ'ܤ"mv ];k\.ۑUz5g 4TU7a%K1LEߣv Y6 }pM.T'+E# 6>Z 0CHs /iXȲrڜ* ̲ <]7ed LBLI72Xfb80E9.ok),n nt:F Չ/rVC$гExT s2 P)BE=sZ mR|3a!: 80Ht9yŘ A<ҿy r}0&+&0v:h9vhrud<1=c"f#72ћAl`5aRtz0c APăs4- Է/wn/$h0Ik<ȁ6K߲ل2F@UuSGW9:P=tSsp̯$WH rR;ֺ.7pѥS*;Jؐnm_Qߢx6:غ2m%$.SH_m2!x.86 Մ{O+2h+?A0ЯzA띫StUŇεN>:"S?+<7U}xݾׄvm$?j([nc =ż.;<=%;% x endstream endobj 307 0 obj 7785 endobj 305 0 obj << /Type /Page /Parent 293 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 306 0 R >> endobj 309 0 obj << /Length 310 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb19@c)+@6 8#P@:CH/P67z@7J(Rάh=3]hB 8$:5Jt_\RKMSd8;OpbD4Z & ;гuK':,,[' p9%^Xl>P]j#u$COgOb!;M@fx^VaКzKWck߿{xybu(iySzҔ>#Z6lȞ2ga?x%=(Ι~jvHKhAWXSiOp#D9:(_n&޴'A͠CXZ]#|(| ;eqDwhc̉D-b;J'<lZAz.fYZr9szQJiU+ܗMr֨8'xs L~pٓ[RAF\NM9RH U V%^U3U>#A#~5)p]I06 -"o%Lz̗18{V#4+^1 C~>$G8,F̈%SA8L ;'h:&b46e_"\(<% A.}V =GqRDpوV8)LM%SH3f1FQeI`=4&i"xhЎ "hH<,R*1@L5ZMq0! oI8i%$sVImoT$)2.bJġZR:؂fmR)%GWFEꋻgfQwb*`jL YPD&,ABo\tckS5Lw2jV?'>iǯBOfэU \4>7Q v`Pp 4*T~c[ 0?w`' `{^va}NjA'u4T\T 84F BLI8)$g q[ NV ̈N ⷔP("Ʌ^]}P&U+a Kr9WA]xM}kWF^;|/]ݣTfU9&-OG8[qIpRmx|h|n.*%ڼt~WO=`Au¹NE2IѺ~!E}֙L = p5-lI~t9'ֆ7ikb#:L`QqD`Chi q6 1gL]4vrX +Y8IT{+e"6T1oݾXmVJKM}Nn{f)EteٛжOe Ky}jkD 54],C+4 F#щ6ZvuFz}:CC]^=ٹ:T7@G}𴿛ɹFwŔ7v?%[nr<4)3fKz?S6oc]?lKGKgFKډ^vIu5 })*Aw|H}'Ÿc߬D$y?ǥxgk^-k{{ob xЎ.*c#SC/%*(P^09 #BHN%',(sH@2[2>Hq,@   V @ h傒6Ղ;ƎY$zGHBHcp0 T-Jİdnk,~( JM c.F b g * CGQ P% ($n&`. psM1J6#SXY^,bqC `0Fp$Кp}P l` 0JQ  # <3[DPQqG0=D,@ -Q>bq60MR;tCp DVI"`ʬqИ% y 1}TCXPG c A"R(,I,h<`@ r"&@rr6<{x ( `40nnBM@O&rw"r+-ΒErU%1A"!&wQ80A %PXbhrlR.2{'=$lp1u1y/VLs_5c+ҌuJ@f ` $2<V @+ @ 0#@#rV`U+8 @` @ ',Lv;RQ9R;`V ES8|l04bi%*%3Z5 rbNǵ 50! |>K!v@(D A Y2YEAQ;As'@ZP.~fs*Xx>( B'` :P+@C0j @@.* ':A*Q@ecŴ8<"p`5DEKJ6*)$ߴat= @P P%$A% ÌQ%&^RU 0Y0wL3LeJ1,Y%<&d3buRN @$I(E5T MTBttfRrus7s{7Ȣ*%&b%>TqtV’LG[[z^/[>0kvHJ CՕξ]^گ`}@m[u]z/f,W]a)4\"1_UaiA h49cv> /v" IdV0`6p3uf jxyFEd_sdV{\6'gjbk-k@xfMlvkvdz26mJ=QT@R JlDǤ $XMYke\QP(qO82:3OiL+uF| pLk|\ e&].bujvf5IQxCVb@_uv`x5) h(W[wxwa{v$)Qwwvvx׶O&|D)|wȠWOf6Wm7{Ufn|lj7du]6XR/*JC$:ʷ(j8$8(\x0Ƴx>xB߂7X2_8Zx#xfOdcMܪ6l^ dVWRLrNW)W.Esjs6 2nO̰ PRVVsu#r Lg:BvX=wzvOX#X-{ǐ*;͘;,&axko(>Y8xSg=6[[Xk ,hLsw6`ijKyWJ [jљ|J&iٽI9ӇU=g>%'X73qZP9}OǧÒy:}V(¹y@AN/ͨCQ.:yU}.kn%9qiw]dd,/YzYEzz9YzS_6X^~MiR>hKLqn7jMLoۢ@ˣW,6Z>@B̚I[:M]\S(z$f W; @r$w; [8%∁WI},;~#\:ٹk{WWC6љix_&Sn\*A|Uaك\C޵@^mQf{qv*@r)pX+3;@+^l@3v]qU n $:}T=sut8tn8Q{wX; ʖ{yz:w >f ^5mZc>;w^RѩJj5<EaZ_ΨL> /ProcSet 2 0 R >> /Contents 309 0 R >> endobj 313 0 obj << /Length 314 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1V4fEމNP/xD8Od3] )Z7ݾ=\#)jY:! "FSo7sXeIzk>a:~ Ճ›-껝g5ΗUڥor' 54P'IFt#!llq×A!MS=PTU%MZl)^jj`QScjZ۳); 8SI{!L?fM ~O/f;Vƪ`[Ԣ%" le?2ס}+h&( B,j #hg!x@Vе⬄B&a7W1՛MG*ȮLuE¿bxm1b6snb.FK~ 02KoG8rIQXHms}к0@Ccu)ix^,LY3֛Jkwa( Ku{dBCctz$YY:R^*)M(|jQS8u_D aE#ҿz5?u6Nj/N"\AmN)\IVBfZ!4,>Ź (yN MTֆ5lPY,VPMhl1ODhL~pyZS4\{cG&AY >1@rch/G* G:j3.->jAi\L^AF$1$gԈ0!@4ʔa 48u b*j)5VNk,aݑȰ c#PU `7$PyxWI_sQF0@ɘt ctOLy Hfg'!{׹`<8!!„@MEz3MOz`ZxeKz`rd 礲O `  "FhBL* ASg$6Gd:OXT )$8+PLJX/Dxcy% xcؘCm'@U% rk to@M GFh"8xyBfvM7 M ndS NTpO+`!"`/FHUeR:$SޙNl(G^m&¬V!Q`e l/ƬqYD9^3kA qkf]섌n†_ІO-e!L2#p2+EvF&yG'Mj#]#59'ro(e'6[=%lQ)f`Rҭ"+%"c)&?$%S++n-RÄ3)*RWVjHFJ/ M>-rx# IJPv ?\NGBPdy/ 0OPkSJJ2{g|H)i[(Tx\f"i"aJΤ(k9%>K+9`_3+M4$Ӡ]:r;9Q,27/Sa$-3)$=󮫍L&=|2a;&βq.%'敬>̡(o? "1NB%+s>gʽ#;@8O/TOCj+42p %[FlA9EhG@)$"0/401@snTKIDIS8$>pgi`IngxbO.$s4_A1P zsM sqBTb}d
wCnC mH@nx< NhbbhcvMKv>2L&goT$'@ 'QJT| tS|\p.}`XiRWEMv kvȔ=&Sl$ȞTkӑW+EUvTDoXYXvR?ogYqXoqqer,)_ ruX 2CCj ԼlUՊM/xx_xw̑uVэ9xX/ 0Oy1tמzB ࠒC41Lwv7|PvD4K}Ny}C@ '*Wts,I&yc\6^'[XAstiql(]5ĘSm7x\xϙً>8moc8חY $cqwywuCYӉXR^S즸 VY9]YUDC]W]5,.XZwy{}4nF ْ)zT` G}iB'c#6*.S/924ҵ>PyHmS+EMus,7"q8w)S!ɘ Z6_zUu: qWQuZӜ6k_|2˜}qp+ [$[ rz3q73.r^x ?HճGkAL;!'5Z.l7k[\mE)W8W;Hr4sJ@mzzy{5cdFY6Ny="u}e~8WMɏye;KyfIkY7W5exSd;;AVa;_+<+j`64wķ[U;/iEA׼W>9ػ1ƾAё۾G8xӴK`]_i[|k잺[Uo>8ut[e^~7K~1{xܶIKvsiUü}#T\}l QU6c?u_ ?a0y>ğu?uX_q9b38W. "Lc7_?2 @r2 јq G#!p0 PH& #PqR-F8, FcX QIt~c !&cx4q- iP+*㸀P>)LXQe2 d򀀆l0b$)\8 or%h`1j^(. $#. oEK 2ai|}:(o< AE﬇Ai{ .`Z$\5EE)<},PfcIԟοQΔd1E>F7Lsd)50MsFIR|aFMR}:*ױorҪ*B6(jŲ IJ,*е-p@$Ø0֍ MfanƸ 8 4,(2jͷRa)R^V#kp-ˈO67N2g.'l9&:R~"p`\OcDŽM,/~zIT|!?S~}.0d/cT7ø@y6AgK~Rn4] I4Dw=a>eB]#9~R1-~|hzwGb NWN[vѸ{jX'i>q͆U)6C˸n[ Q97A:Q_:O/L"3 ;L/ҰؼO1sGQ@Bm.@bmtǕnћy4x#EeȾ|__Uy 7qH]=Dæ~Y!g`Q endstream endobj 314 0 obj 6361 endobj 311 0 obj << /Type /Page /Parent 312 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 313 0 R >> endobj 316 0 obj << /Length 317 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*N #PP2!9qA(r@+9q*>D$;Èc;7B+c,pT̯XB@f\"h" @ ! ^i+5? bScU(08{l 0-YڵCŁ@<{9ue@3[W2Sug  Bɵkeg OpmEH# R^J1dѤ{ Oy`Ih QVƢFe1)Js%R'.EI a*I3bFٛ4f\͔r[M!A GPӊ:_5/Dbߗ\ )UTTD]+`iMD!DžPl@ 1 S`9j | ?G(%SyT[efenbVpr̻/6hLb拱36(!%uҚ:{ZcJs-v|ӺBzՠPZ-Tu555DkY ~DO]j5r5[5Kh sbN[r~mq ۹fn{NNYIú.["-ٸ$ޕsP: (LR"p_.7F +んHz O'204KI !ዤaëSXMHƁTfYЀî n aH zCsUe& Io!r HEupKE/{1 4xyb)Р[: J<%.2DK' A3.,%㶳ߛ z_*A@B |;I4RV_/Ү|}7Bŝ4SGx~i_??$꿽!,FIԙt8(m " փ $/h^ ^(zlv: + M"JPN!DMpe0lvj?_iFzbLHdC]o0.|@ =n ֢+7 RKnpAO= 솦(7ZgPPڢ PJ8BÑ Cil\if~ G,JdpbL~u @z !jP=/7qPI*Ѓ+( Ҧc+;.ڈ.qrQ +.)-,7 /Pg-,+qw<(s9 k*"⸳K!2 /B5sE"6N-2i q3 zdC"eiFi0##FX2Sp#@#%:ĐCL$(/ I T'`H(q )8; 32?{+,PW@#AB T÷As #q//D [,0Q92 t,פcD7_Bp4272?+)^7E5{ؠ*2]|J(Yˉlۏ==5NAˎ\݀{wH_ 7۾(5{F1m7]3_Dv#Q]- ^i=:~Q9T;=P&!73p2Gޘ]aG5+:~S"3U쾜< f$t|h}< Ux]w/Wތ3ա1ߒbK6ZՎ^Qq;Y}:C>fZOBOrӞ{]h1umomwe쟗ѹWm-m,@H^ވ2vÛ^J{9waͬ_0 #af2 d  c@fFcQp#C"B%"i$#Ⱥ *j4Όp ބ v &#)S:5F#)P0Nkcةtj0<+)+caea!֪h[ge}xaA0r , 9Sd`SQ2"3)a:MqR/*|c d8p ѢjB1f6 N6w9h|G֗d~?;5<~^l #H@\ jK>/c8űd 0šP0^apŐ3#QxGȐm;DZM(Cl'ZR='R&>XdhǰМ=s+("sSFs|%3Hl3N3ͬ;SsK9%rGP4-Fɭ4R3\&I"@Ң(ɲб@2*.7 c( !2#o]3J꾔Tc^ϊ޸kl/,%{AjlBBT ;,)-3܍ k::@QէY x 3j3D˶jzJ/Q>l/]"⓬ݑtG̙!5gԳ>CꓚDSC]G*áK!Q^7FʙN7$0%'c%ҜK#r!bzlC$Γ=k%Mre^iN|4$+E4WhOEWR'-:?j}*J%Bv|ʙ R wV*h}ZVu^ bxn 9"HeB]Bށ!e/WE%4ϭǰSځ@$:2_,V(ka )]A7:-.D8;u2Ml80[-nBXZ43P13E9&xL̡1j6ՎMȡARLuQ^ b m}e`9NpS)pi:F"a\Q73:g;6D|j1ѝ#MtVEHVp;tˊ Uŭ2A-+Uny$nUe3E. 7|2=вa{ŚX?@-o>`~+*7j}ƍ?}7+prx+ 1#!GU59%( g<5DAs[>؂^Ӥ3 $A1*?FNMĆ#21HA"|V1F͊06Zr#HƘ7'S|1#r˵Eq&ڨ_Ԗur(Z&#cu¡b]]Ձ$4{jk'b0 !$NNѿGH$쿁7`Y~X # CYq4[[Ⱥ8bV\OLc0HPT~/dlDA AȂq+;O(-T#R*V >QWtETb;b!ѼcZ.Ὠ.&{K<9gO]6ku_OYei2/zz,U50y)EA#G74Ŏ+}u4Gkq-rP2ŌD) o^w%輀"XD9:˹ڀ2H_l}uY̙/,悺Pp-$,nfe;q 9R諮RRӌk5RWyْb1FP.fiܾ#Dbj_V&T'+SxF,V0F`4#&\Aњ! rcvc#Kn˱ [̈5ɯF±;a:Ur}}$`@[6A|:F=l za6F/a "%@=,BA aCia|:Oޛ>qn/qK3!dci(tpAE(1p/E(&a 4"0@YJ@  endstream endobj 317 0 obj 7531 endobj 315 0 obj << /Type /Page /Parent 312 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 316 0 R >> endobj 319 0 obj << /Length 320 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1R6$4vIR1ӴCQQfCXh hVɧM:ǩ6/]{_e{ejv;l^i#Bנn)Q:]{ºW[~p 8W`A3QH;F۹bB A|6}PH @|A@(#\ 2 "~sVaz0 C 2 6D@CpL4Pp93 e5Oh<@ꮖTI f6:@i} S.: endstream endobj 320 0 obj 1123 endobj 318 0 obj << /Type /Page /Parent 312 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> /ProcSet 2 0 R >> /Contents 319 0 R >> endobj 322 0 obj << /Length 323 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1EsS:lBLϼOK&(P4@li% ŰK܍n[@Z զABJ#AP&cQaB+,x*ށ* BH*+88d* EBn<WW՛' #l/(v/(]- ҆njԂQ+ XcAջ#38x3Iha# 0YV:P@. &]qSJ3m <:>>t`C1% [H9cKSRPJsB"w_T:G]sw<3MAC4.l| |)Hb~ʓ}O䊢#)/@7Y~{/Zo ?w Ko % eDK@ h!B@ 0}oAMnu H8A#% 8"G P98""[R Ĩ V1EXHњ.@@ "_( L*20.RG"[K)5fP(tk" /v"r<Y*5xƔ&;aMh6dHd ڃ4:G&6F |o*E9}a](Qhn_DH4vfr)щsH}:HS$:ΌIIq0͝KZiP=>@JM* "qbURMP(8\tEWajX4KJMv9"$UED!aVj, E&_ju!嚱rJhk唴Y8j|H2 &syCxf\o 'l?y)r nF})4PBɻ T0e (E,#}Q>*Ѳ~cm 9Qj?/ =f%JDX9 TZpYK!{n$ CX$M9ba!ƄBOo(xO"DPx ʩY1FE;% `p4g)>fxSkH6;7b%df<3@Ի2grO6gksůζʖs0xp-ǬW*\sDRUWk=H}2;U .RlD'p^\H,/aZF7ɨLբy_uڬ8} dەb^tNgѸf~۟k,Dx7e@"bD7&wϖ/~c !Aۿ~2m!9/-EO)w6O*˜gn^\^Y}:"`~c>uݱߛK;MFȐJOAO0N%PV(XpSb#Ьy$DL!ӠP "I< <:@@kFn@қ' cQ Lf5l ʙ&58@aola:MEIoĈ% d@ t@zN SFP $f0y v Ff)`JhbEiw EF(Zg |w)6 6sj 08 z" #" 3 </-#s 1?w%@.1@Pk2oEK@SAlKS5AT?g$_J(u_AFzXt1B_X' NuJ`.w^27_È4,dV[aNu$]\-_TcIdea/\V$*l%vLAievRhhu@ NS Y9c:Eq yY-r eYR endstream endobj 323 0 obj 4294 endobj 321 0 obj << /Type /Page /Parent 312 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F10 48 0 R >> /ProcSet 2 0 R >> /Contents 322 0 R >> endobj 325 0 obj << /Length 326 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb16UN#[4UaV.V2#`Z+#e'CUAeV2ݗ@1a'sJZ!pfG%8aJNJ鳈po0k(;DQϼ pqB&(P4BJ fŰKHkKjSV fjz>lh@e'ku=i8$B3N]\0 ! Ux9n&؊O{|i{-*k!ݺUWL, n dw`2A1Y8SpVCDv w)Ŕ'UY\ܩ6ؗb򵍜*'"Yiܳ[by"}((fSp[iz#S[#W^˸|)lAR:|·";L5N8?Cر8&&S}aC_?_7u䲉^Ț$Zd Zc^_nrNvl  ĭ`d肒,(CGŮ$H'Di羰 +d8H< 8:zĎ rN $unh EPzώ?aq"neI10(r !DR;"B+$bPزQ#P’QЩ20\"qov[ j!Q+% ~lX)1;/L9$R1O%/^`i,2i,Ҳ+Qx(`dţ+HE" QLhjpJ,/1.~)|*@PΊr)2>rC\r̋DCR$b;2(x d |폎)+q!82".9 !c"[9:70@ױG:ӧ$|(s%1K Q)3ȗa<2d]<2oAhr'($oc>rR<|OICҟ)pP ޺4h8&J ! ![-m42Kd @A2/ET(ˀSEi+cF+MqG4kGTQhskAtAF.IAly JșRiC,Q[,,2I(TBiH}LliM \kF-؜LIB m|D S*ܷ2ML[RnF#S [@S G&gRe0 P D{cTUH Š$ 2*ꊆs/QjX ҭd!]*5,2sS k4AQ (U}XXF`Qr.tb"$-UX__|YWhεL#/?i/@h`O> /ProcSet 2 0 R >> /Contents 325 0 R >> endobj 328 0 obj << /Length 329 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1c`,ۅxT)2 #`7<9(TZ=bHc.)BH @'6 # J:b.({:=ab0`2[HBZbK}KG1|D"BCƐ/̒*1[ePQ[;"H9#p4QBUe-K(\1r4 endstream endobj 329 0 obj 564 endobj 327 0 obj << /Type /Page /Parent 312 0 R /Resources << /Font << /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 328 0 R >> endobj 332 0 obj << /Length 333 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*Nd(Z# XHr,$!I#?jps4 8@( BJ|# ΫH$B,ƑncHI(D PdoզRM,J )CМ,El89 #:<W@$(: #01H7VpZ ҺtÓ4N8Y]BCVZR"@BPGKHILgK`aj@ZoS c@i25j?P(4]%Q\8Jw<>69 f^*L<7qAr&"츻-Ig6k 8@]g^|5X":3،!h Y BP.bꈂ`Lh^VQ*Kkμ X1@' #hxmAjjƌ* #;JC׼X gȫn+ XCP+V 6\ߎUWҼm2Up=ۨATr!L2xК`? SהѕZUYEjUxa6WD02C-ЖLC~`d02DTzI=ˌd}æĎC;q<GkMtxa|q6GhXrNHidDB5B'^"VOj_)Zt Ag'4ZWC&ZHHh'/@jS_J #"^qR"xO'0$B#Ēʃ#(*p0GZT$Zp2> (jTگT+T:VXĖִ0'I( SO Nx% ՗v-KFǽްUt47@/VaDYA[J\[M1B!heiu9uZbzdUI)/ P;.s8kܜ:TzhK&\ɣӀPmmgF3 (u}l-:2Iգ3q H e[OZt[ԋ65K3Գ[#\uOL)syZt9 ^pO#a*NuN&'Or! V/`bGaU(L&f))*5* 29AKAFFT``mUUX^nV aVE:\H1(fX=-GYZ1j+3bGG3N5kyl肰lF6↴n^m6m_HcnM_L`,cihv1# rԾ7/Yw*g(v|"tC3Gsgu-#4%WGrV9;uW%2vkft15 rrLjw}2tw]y7]ywAc&y(x5xLWqS9rr7zw/}w:fAMd-yeVADRoTJr1voj?R&d8{.**<Bn::YFzLx]\UrfdXAp5jho1dF0_3TFiT1V&'M"ιO C1f}'€5 X)Ci)h O:fȅd &*HXn¾f/0c.`e X8k`7pUs)١j%%[3Rz=<0XZM!2 zDI0׼{mZuѵuVtM0vE%owiٻZQP( &=Rv]*ұS+Bg Ƽh$hRD@4Exc[3EytYjh6͹|9y\Bmeu vWwʹ G xk÷Ti%p;kKzp\tȼ|ż|c{:h\Z҅b ż5}\zoD;5dG߫-S@4@@,.sTkTRχ6sK< .:v`eOYvCOC&R%Wb+YMEsoM3\SZTB[m2SͥHWsgwU_Ɉ+ y{ }zطo !]Or5r}$49%kL$@F:1Stq}G]d)ּw]_Ɲշx~ 6ǜ< c=}ٻ}dyL]3^G|d:V~ r}uz&4Qw,]%viD'Nid8#gPj6YG뾉$Edq.-mNiwFY9@8fIy?.¾*x]3f0YI9*/XdDy_j%flt՗o[ +uJ/py4*)9Ր&9sC?xYNU/ﰵ0CPT< )3D Fob885 %q!X( bj9FShb4F a7̰X\6 "QkEAc ,*2D" .^ovd7mCK 6K`vm"3,|E+z#QpJЂp]_dcyik@h0e78|cFAf-l iv\'GF-?ubƒwsHWKf>’;S$֮,Ib"-c<npd/@S°k*;'Q4Eę;A@D ` 4Dd"ѦH$TKȫ_́uNSR΢?ЖJy.]BhNH27HԃvjcϧqTUjLkTWTYx/VieZqZ_ڏ0|z0ܖ_-2i+mT"xШh0@E㢢'N0cGi!+'RHGA]1i3BiΉrZy'<˘1o\|ܖk]੶bߊ/j>iW=V/ `(F Jk9.ev>goT>|*>3Wً*] cƐNGƵHd|UfLc+<;dʘBa,sqN@?.bYSΆU6\eU\`PȇvfiŝΙ0|Uu endstream endobj 333 0 obj 7233 endobj 330 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 332 0 R >> endobj 335 0 obj << /Length 336 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 LMv21:DI" 2`h<0F԰TVʔU\T+UJmG\C͒B`VEwjV6+Z]sZaoFZum=YoQfܗiް|zegV _E}xUpjٗ"X61(XVX=vqG9[puz^٦}h9uWUx2!Moxk9~W=:!>6 JEml:45N-4{/.(74d͔4BPrX0qBKt{?Q`P*4.@c3365MfG} e5Qk$_pusuz^=bXvg6Ł֫{W6|\0^ܷV"꒖џ .F J[1Hc%7SzBY&-x@ҙg &&m%֝`J C8u k}ՊTi,/$ٔsneMP߁ph 8C`_ ]:e^ vH %\'6NA?:( bM`OL'fgC-vư6#3UCxkLˈ,IOSdu BkL9Q0t_ d;*PLy5K{38M g4՗~e? ښ3N)S9&L)pNtMg jq̙)^5Q $L ׅ1jLVBbD"YO D#EgZQF sQƥ O9։+REqPwсCpA!evBEɹX^#s"j9TG(#$dvNM:bI'2QRAtWQZIpegb>6&$NU3͋oķ %ZmW7Վj^V~u?ilrՒiedE^?&U>;ph{> eƔ ?siI;pڭ׻fD=I)2#tnuڦf !:S(.*#N$EF HUIx&SUfw2};}ob;Vۊ͸[؟f#X{rN7 I2}c#P,/&?qO^YKCuX-XymU.F$&w~cKќ%ʡ<]IctL`Dgn;N6b BF 2| 8m/ip!`P0q:%-c x4*1InEv\cѵKVLCk;NQ"0nc%sUc'dG]f 7p Ӷr}{IeLtvVpUmQϿ|xfwiCzo{e񝟤KF#BgJ- G|(J:9h^IR4n:EIb 95Y08[$ ErH!pOGiQp٘wgI'v%0mte |{s&~ r,}_w9k 秚Y+a˳n)o8}H伅k|kہ4o}&a^כ^ 4E sEEѩ{!b G;u u7\knÝ;>MdL$ `PLbt’@f HO(Zi,!N6rB p@Di/6ȏPTߧFЬ/>  *bTLX␈j  /~ ϑ A Nlq @H L& vk*F5F(&&*(PMF  ' X hp3֨(NϮƼ8Q O4P M (Ү\~:l pPL Nݪ)htf@^10zUpu L!0&roг fe1q p1Lb^$d=mIK+԰N䌀ʿ)1N.0qD9P%GnQBؑ/970"'Q@ڪ ʧf$ے1jlu(yrۭ(iM)2pm+ 2P|rr*4⚗)2/ 00oP1r1G #5 C!I:!&Um3"0*o#f q%B Orb;r' "ٲvxQ 2~XTcNp1)Җβ:`3' 3ӿ-3,<,;I:eG= 3OS>..>&/(O ؓ 43l:IA/1T%@ 2RCnlҰBk3 +PgS445 _Xh*3ls%& 9h'RlNӇj8'r T(eJ%NrJ;PKM ϱa 6) 5 5 ABQRo.9 tA )F(u&l$h@ qCc5΅$H1*3pD7ga'gӂTty7DYZW5P[C:[nk;2\!*SuO-U<'RUi,$B^5-[^ -B ) qbq6-15+c53OUE;; b 4sTԽɀ@Er(o_V5fWh}Ff`XcZ}YUYS~uT8T1G(l+I9*/[ek\T l*V°v*t-VN3 lO/ 6Nv `lo@iL;EGq qlq2rQB r-5SsA 6DDj!eϾTX`ʍ5\ VUigS iT$v\Y%Y5*6&N8IKI(`slIY){n K׷L6s.l׷m)|ڳnf`^7K6=~ Q{w a /i07>0ہq 3S,W99t6LlQKtRhUucvdivl qgUtuy60w4{xYoIZSzS[q7G{x*,t"94}x^Pa8}_x+uxow`NjX pBWHo!X~X ə(#B( ??6җDB/xHXOeTf #ubp Kul zbvhG7Q37yYכ78xxz0{YJKw,]dő+}ْ͛n"Voyݏ/S82y/(QU !Y': qG873 *`T?V΋f^` ϕ:Wg)HsN8GYuyIyRzBrZ@حqt]ū%z8]ѡ(z͎)笺)͟!/y/b8)s b!5׀[ +T"!͎RSTS7["yX ]lXQ5BZiFw6UFTwX+ $ۨsj5:*.qS|[ǸW麹}[;x,;QݻۻsZxEQ8e)[ ۿ{ss:4f RtSUSKf5gq eFy`cuw:Oہw@9[yغ(||ǿ;s;əs<9Y2<ÀZC qx[ӂ|;3<rNXL]B,fyS5Y #Y1F֘q$p$CׇWiYY:xJsjfS~Z/ +u~u]q{և{I=lK5 o]FǰM^ ^܋%] ?hLU muKFQX[Y;c>"x=:TԺSi> [Ց<{~Y-~aڻk[C][_]]Eu5{aݫ?PߊKe }P^ȇ|? s'/L?S (ҿe8pumzmyp3 A@ *рb2%CPS& aFhr.Xd" cHf.d: 5btE*f)h. O52R)TȐ\2TfJ4d6]6dj!>mDifno`n$k$Sv ˍpN*P;%ez<4h0gryb6g[ p،Eƍ<7eqcmz-!fHIOAzsX]]|L⭯?ضj@`/ ;BPc A肎bL&`0#214#``/c"ZA&Z!Z P.z.2M+%Aց 4X萤rOs[N ĴЩ\ >갰i h65NiS+M,5+W *sV4LҴ}U\Լ T]-݈:d >SGc}pX֛՛2Rq"N@}tÏ\+mu*Wc56 %jx-׃ t_/.$DvϳԤ!Rk1zG4 /cx8*4t)JJm,2Թ# L8o4shi7: h-s-?Q@0%Qiy>"\׺*u[9-[wDUkv^'\g^L|ҏ]Xt\p a;8Ns%ӄgqb=0evדB%)x7~W._VAiyW) r_ODŽDAJPad#xnHI#3VnC+;g4SFKlę3 D56@(Ne:Q_%I(Ք+jR [BnB)DLArD8ĜS1.*תE& [u.ŨL2Qb r|,8˶zDVqVaD :ic\zG b 0uzv_uv5cj}=%՚ ӭMMD:T2Vc0'R>1pN&OiѶ1K> M#5Qk2ZS{R^UQ,[ ϭot)3Ϳ{iEqmܾLvv-H.̬ B.+)I;n9)o_ аW]s?2)&X9CsLJ3ZsޑCoWN#{WS2q`e7MPCaVIqb2icd6#ܔcaP?veUѶtrJƇE~nxvkyK< 580@Ȓ<*J=>O8=ҹA$,2rW32zY˜BZB5-[,i= ¼C6Z4p7C36x2& &;,#6q,' ,w+7#,sƛ^+5D6Oq=ÓdRergL> 5CD?ѡ'J ;:ӹHi7;ɬ!S$3DH-#)!E h+, 1c,4WtųI3\ɴ^tqRH +Id(lZE*\  Jl#\5q G)K$t+D[ȉɏkpʶ2c+{`762|2:H C{셁ýȔHL,Ԍ+*Dxx()?t7*|`"c JJ4ӕ#"ʹ<M\I͉_̝J0*sdb9:Bdt1niʼC$4<iO4*%LOOv>!K:-:p6x;/,Âh4DAH#C6ǑxL̓ēQI\ TDJDєҕU5DMzѣV͜X9Ks"OEQ\e$.4%(ARO&ʈN"4*@ JS,SK瞔STN]6,ӴO<[iDŽ?JG 8ZjuLJnLZo;U ̢P Q,LMM#-(m"Q-:mBsպR 8WU\Nʌ]R]NU)d=fL0֬ElβW\Q%r*TӅutNx=oWUs:˫1B:9G39@1:H} LRLeJкqTȂvL&ĻPCU3"Ud,>:"EXեrل%5DY}bMY75R]R,WUNjZunZYrxRʽ>ճG =K>uw΍Ɲ0ۥz(+7o:G}=Th2ؙH IXJPT5Hrÿ-@T@&'b"\\#&p0p0l@rmQ:pDM \RX!7y5٬B`a9*{(؂ ݅ўgJ]uڕ}FM#u^i]aN^ZN!JU+au$JbT(ta)b,I-b0>bc+\ <6 %6T|4@1u Лj,DT 6!VEG``' 9.>(J" %buePM{e=V4dUU 4&Z8i~[ֺ-FUbf[j e hfd[`7_lf^m4`f4fhb:\ec{݆;P<ԎAud2R5\N\ÝBy``N['f|YoPhRRJO\Ƒ$i &ViPiuz齩#iN~bۑDӕжJ%jYPj⢈9#jiCI~f ޴k4ִf\@%:4&eDk_jRlZκjkk\k'F( endstream endobj 336 0 obj 9888 endobj 334 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 335 0 R >> endobj 338 0 obj << /Length 339 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 LMv21:DI"!>PlTaK с!ZZRVAsRCbGmT)VV oWՐB`T5VKnY]ZkٶͶ Eg^7-siސr[J%s߁{6 VVE|WV` naiHkQ*UzbX]ua ];iV"!XV_3EÛG9nu/9z]fVS[`6)i:K;1owyfS{v|&?U߿~1橝H Шhl3Bl`׃uj> a\1x`!#3a(q k V l c}?d BneMPp./v :s8aMh8N%\0QD:2aOM(Kn8;9zyq:y&!j[ ^"<MpVR5xHD`AC} |V_t*LJFLU1:hMi0š50s p75KWSnKIǠÂV$*??9@4¹& 4:5Cb5 T^јXzKG'q_6(KKm">u,"xрc3pak$6"ܬ|a @jw,WJMtuNCa!$v;Nٜ:P cҝjIdr7kWWj/* Y3y^:qm6Ad;'U쭊~Fah:r ,ͦ<[NiI4N{`,l GTw_;rgj9Ea#WZ9{n^" XQ/%S[|9Ha n;PܒFZ6QӖszR!Fȩ7#)R~EKX]MU!?S);ݪːq1Rub%{, M6Fvַ%Pp37*\ rn5^`pej-9ƖnlnW^=W;f+ynSo=y}ٙf3wbhMwN9H&oId.$ۯqj~ѡ8G iVUabꌥIs PVUCZ*I/;*b|wXƶk,ye}ԗ'2겒=a6Q%K.$s|o\ǣw~i]m3NfT4tp:>WӒt4&|k{' n?-<ga!kPT"ԿREMM6 !7PHd |7%}`Rj&q+ qR#ۘ҂2}hָœHĈ ,RA#Ք>Aڐ!^'Tǃ˖χȞ%|nEϊ6/(eOk}=r-\^o˿O26_!xO~4W{4COņ4{hv]v|+_wH JEM j.tjf@a /nHQͶVt.HsN$m$m4JdJNm-lOd/2 koL,FPXXk=P叩Oz~` N"O?|n:Tp(XNGb"ʺl ] n5F\@&CTnnK+l N ,`qևLN(^ ;JDLMZ*0I!,KbtŨ `EB>qbHO' p-%LGhOK }ϧ~ PP )@*P A Kֻ pP*C ykf%FK<"fbԯތ E8 @^ mr"1*sHq4͎)NFK&u'qJ; +1n2R!ZQ2Lr+ вĚr01,1Rj-,,:;ȵϷ- /3 .MN)!o-R#1 2 &3q!F:PrmA"dnH"]#r;#, L"T"BRb/r&nFRs{S1 >6p2'QZ jBTr3Ѓ;B/13,4)˂>1.=i>.9>S00? h'4`:st#C'-6S+pC HQD'&>$e!Kj]O#R9#$ps% [H8q.22ttwM~O&s8ͧ˄2S¸L2m +1iL>MN>̟-.˳OTֶM@PPQM)R&S*+8;13 K>u;uT1Q5IBsT!(!c)!MC,+G3cGsh 鮞/=qyH!8mIgPej.q1('htNpW7t\ WL[^TAN_1uSN-wp^,P/_QPuO__QU5Rpk@T)% bM>v5Bq ;UhwQ6B) KYE t`le&HFl6Ty$o /Hq7o%`kYSY791?:ҐZ :,ńTԽ)f*Q*Ak̯**/]Rko]c6NlOn>nKGnv`2o ea6av0eF;OCb7 v(͗# 3r)c;rY.!t3= M45Zl" !sF*r(IVi#ag8RC$`$ӓuJU%cI1&֑jT':rpQvĔ1yU}[/[ji󲭷k{)a{mL3!WnDq}IG~32Ɓ~dwr/wQ*B U/|q6Xh6.<872>8?t-x1EsEܒsN=e'5mGFnp tLw7q7։BDMt8yӔ֟'קQz6}Ll7x% PQ<ر=0]]^wV,Jܚx_ӌSX36/LݎX K!BW&!W3y;9'j/DtQ=W_&T`ckvquGHXyJx`q-w"rudχjmƑRGU1isq%PoMY%3~y"tVˍ9ۏ79;YyŐnkByU<1н: X==Z!dVOt'd1""V/VquW|hkhN59yZyYyzNzQ:.(Q[wB0“ ' ǛƘڗyϠ:_wzoBYŮK'Dϊ#}x58z(x-1"!ae?e6k#5s6zQgoՂH3HhsjTt"uiRmVj9SsV;_qRǚ{/znNײ"SZ}M(X睛K9íI;5B!{ɠ(i[9XI(/4쵅's_N N 1R+j,a՗&Ms'jyҊQx@ϯ6)i{wǚr }Rtɻɺ~=Z8;VBZC#{~|ۄS3凵ւ^iW֘s{e>)~]H/@fƳ[϶eZ0VTY=>r= ]ߜJDKe|c- z /6<~H"~(/2*E]MIתzҖYȶILv񜹚+_Rz~m_]~ ZMO|*#]p>vW迟ۨuKe = 追?{lױ_ϲ}va(UhN_ " *!\0  p#2T;@@9 FB1pR)A!t1"⁜ ˆI@[#xԦLAcpm?F4hf.Tpj8㔩X4Tkb  L.uÑ@fS}ADW)}z *#tÈ.(}OpjFΆu9r-kQx<.GS'ьiL5Ѡ:xۍmph4\ -l{Yo F:mF+Uh*BlkCҼ4&1 @l#OK DDE1 ! ϛEq% 2"`(Ȳ"r4(@ 7 k1ˁp7(/cx.`6#! ڤIȆH:)% [#.RB&:v'ꄢJjB̜{AEϩ8O( ±E0-+[)eg/Y0+2Ua0;'Ղ3ԍzBZ>M#`Z sf'P\v#⽖E^ZN{\XVѺ>6ۗSy]\-Tr#q,1 GkJ-z}nbT^E= ǑG*X{ )--(,3?ĵ.K133\73::3ܟOO@$I#C,@iE&TkGS)1E JM*P85&XH dN[0W_afa USAbKq_wx-s\]sl\C_Bw]al?k:s_SrvtnZrd]|/7Os}mf$_lh3TrRR$T*$V`+@Pђ[K}0\`c 2Bo 9VNZˎkƈGlɴ`Z&Fӈoq8( nU8\NH4r1>rQ>#GEذOtJ32$bk1:f Z7KGDlcӱ~젿G~NHgt>n.#$rq>v`#ܫ}W`_3oH)HҢW@G$$ @> AfZ\N 7$!nk}Ol[*+Sq)'e*[ڏ)'AeTÕh(ԑL.PdيsPJ.fK%;1iG?FQqKoi죎weK$\F[TjѮvu>?dO!YIU)UHKY͠4jHH=3霟 EJm+vkOj!7&p r٭Έe lIaSGϦªfĨAC :ҸZcVc򴕳0%l*tr5Qn]"tr[uV92>>ri դ&SRS-]*[2mm T5 zbcTYl|љ^˸]tdNy{inϙ#o$C^埤[й ~-p6Qlv`sIu uSOe՚^MJֺbJ{k@e1lha h,-{cyّ*zC!krYq TBK|&*`s7UShIA1TuSs?AU]Ǽ-s3~pggg~8cH8Oʳr<#/,^0r'EyfzÚq p=awyEzs}3\uu8dv%hv^ 5LMvלN {gD *nޣS0Mbz̸6ql'5-.p3]qGot rnk77ծg~O:K>,; ͎J 1! 7[k1!, ; 'y s;"ŭ w7y3 5bV4L3;!=0t!= #>#ޣ%>'9K28!>jL)AN4:2SA3:)!]:R?0CSR: C:;ӣʈX1Jfa*j? ;0!s[["zx 7" @M#u.JjFL*JQ? suK|DŽ5KFzķKۣ:`#?۳˵DT5p:0:0:!l" Ȓ @rw@!Iu@ .?' <+ :\A4WACK`0[ό0\3zS ʛ l_4J9CN!$qEt9#$0\N;Ka(7.#O7P ]#sJA( 6Ф!b)IJyP:eQ9,&P=3[Ƙ|4PtQPG˭!9Rd endstream endobj 339 0 obj 8635 endobj 337 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 338 0 R >> endobj 341 0 obj << /Length 342 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1uz4Bt 4C%sK#k ҆oъԃ# Ni+ VKT{ 5`BYD{pTӔ;"^4xM <&Je5:4vF))@#)ыn$8@2RMB́H@i65 ` '0(/ l`͆M A/\@`j!d.(@DvL qTY 5?H@$jZ&@F{ m6C) 1(R^L( 4pBd M8dL,HDBJi ($Ģ /G֓Q!-\íSyO`3L HdCgyX^i'NPMPr CsE@`,l]5 TYҡ S\KqqղgofaG&JR?A/ |&>ںşP?d}p*PW[+4";C\| Pk*/ld*Wv«p Xl j޺/{%T 9RH:ak /jZ]Acp3RA+ݍͣuH52}ǰҺd s$VY;$؜HY*Л 2Vˠ6nMdQ+#\lxMv_KE0(xOVe߫\r`jl B-YâTpWL\[:2\Ցs睑qN긷;?5۠v8XM%} YX;H&H﮺_ada]\ʤjeaF6n! k=c:j匏ƘqnYt8͍d%W\X{7NQ{_FM({}|rC ?\%[ij7YmÝuU8gYhÀa2ZL97em41(lktjsIb% =&;ztIOs>f~2#[7MJW~}[8'wj 2 jͮB_[))[n\I){b1r?|Vk?4kx '\+wC8Ns1u'%"M[gd xZ^`  #OD2ѣ/@s@K9P 0 cp1j"Lˣ48ըp0I +0③Mp/|Q̈0 n pO+qХqQ !oL,oAI,?2%NR/L 7?!'!2?" ,n k(#+6<~q2 \ wQLʯMbOб\be=4}C\PRg C.\C^|q"ћs"D!$D nRЬRS po0 Q>s꜍9,3S I ,;>W@;5PU@2TBrT1oUE V`ɔFWDo1qEWuCs0\3n$isB!UiG5FGl s5V(b RH[5U[ԗ7[J]QK]5\L!Dg9lrd!:'dl jv@OOr0[3@MQd;S¤ 7W/OBfUg$=C|ih!W1YVXpsJ6jf[Y]ki*Fք թk5_U^Tn1\A~vIoT2To0p4p^fP^uv M\ ' | ibo-2 Q%&u`)RWSR>θ@|p_G!5gpHw67vzqo{YI\0zj}5}pjE(wm2K{oiZABK4ngo}x]9Sͥ\Zx=Osos@ qJ1Oub2r_*S*vQv<qQ֝w+?V׊|wS|lIBMzgjJh+a3{Tt;ViɌ&֗#nj}|J8k}48Zxn ^Vy6엁DCJבJ-39#6w,% w5M  @dtywUbT)C8vEێO)1O8wd'dx3nbay !gΒذVzw>ߘXVViy͍ GlYۍjEZ79V̽͞9f|=uHUS[g 7/}z17/cy5h7(c3&_ ul @ $ mTiOs vivr`vD2,Ե@U7Z]u]9cgA2Üo˝uݢQ|ٞЙڳk(zЯeVZtSyxV{]\#-?J[9CLĮ9BV)0NR)pR Fw<Nn ޴\+TaY)* wVFX5DZ䍈ewy? EX%}g4!}U;ŋ}{9ѳԹYϭ7X箖Iώv:J~^;IĮ(5aJ?*eE63/\;5K2T`pIerTm|_s4۵Ɇ 9d Yar~s{~;}>=O= g?¿?#>QӅ1Dl;嚦%2b;Yrrl @ nLR Mlmܓ=>l+u3꽳˧޵$>,~Q^ߵ:ҿvmh} ?Ӱ}?1V "3n.pFQ5B8m #ApCFCa*bRI4)LpYE3l_3j$V4 guj %QcU$uV5V:m]Hb2P paP"8PG"&CcFi\a1K#Is:NGAM, yD0 9 .(. 8Cȳ&6#;r!eoa@ϭh/nï-"()<4y}c ~Q!ž8hʾ *&-|(d j^#,d/"kx0ʳhLGgE,R`ȩj$1ts&:޹*JRz?L%ɳ3$s"LhMr@2r:4+08i'(K:EpgEbޭ 0ul.N-0㊌=m`3YK43D43P66#8 7 dݼnD{"9.[q4y#=OU`/ ʺiAC@,A0]&i܇ǨP1$@RTJ2WF~po,#S΁!,لe5g,Mzz jWg4.UMRַ.dND;>>ungd3|gREUWJ>p(To.5Tǜ-X\Z MqW_+ al+'c]/e3:ϴ-JӅgp6uݨ=3|فF"`xlB`zP">)}@b2chPD AEO!|jq7_YvhEXFATt@q%kb(S !ZFk)jܹbpy$= 598TZJO y'6Bߑ:JAX [q-]9;3S`Dq͕U`rT8#ltjԺR=+٘u_b!8I2x ]᭷@_ 6!˹/s&9p,dk]tcyI}1'?OCz@ *;paeށ)Ƚ!C>@FK c6 '+uw0fv*2Х& N~NoOjxRڝ?'8!L)c@ZqE(CH(\CsP$̭|1-¡S{3p\Q7Ei.XT]3jkwvjm/FޛzUתHK}/@HRF!vZmc *$`VxB ^|߮5vn + ͅLB_=F}؅{&_Sq0D@;ggmpΫMFތ-Kndn3-J!fEj$6lr.PW<ˡq&e-+e IHzim&Ww;=A~VԷCSFJ4<$ݬ8D`mZQSh@i`U+K辁//ھLlq$Fc>|}aؕ#9 dhlݲ&M S2gF$gz6o=#a4p lN~Y߉p8КȋG8P/ AۢB9X4/'9jȗQ B.3˜'0184.Q5B;/8BC6 +:Ȍ8.߫ ԃ16hץ@:yx1;i1T@Z6y!p@&; u {v2CòSx  -EʙE4 \ ,'5 8YZڨCTcFa-g;;. l'=tjF,ٛo);GE3L6h¥4TwǪztE|l! ,!jєT{FuH8CFTL6,4LlgDŽ>JĊkȤlҞ,ȋDzHCt[ T/tsLGRC*$x endstream endobj 342 0 obj 8241 endobj 340 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 341 0 R >> endobj 344 0 obj << /Length 345 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1(r@+9kAA $p?h@k3 @ 7 o(6#8)@& 6 +Z;Ьx@8Q-##%1cTq&(P4SS)$-I:Ll #IAkKhUzզAD*wE*0e B@ej6D cN!qL^>xçZmDorDY BMDWOt%}P7 x*Ձ) )b.`nƪVWaKcI=qNR cWz~Y ["K.`YB0ZDfRer}ImqLj_>v:"#-. kHmLK`b_h@f["崹 b,ꈂ`Lf;?Qb6iЂ:1' #m @N+[=)lJC5 *#w;_O6H)XnM4RNpP+8ׇCOqK*U3:^4yФlBCǐdݐIR#))Lы$Ά@BRMCpVJFDku$`iB(/ `| Y /\@`j! 0@X 4-!Q DPhbL$C_bq4(BD"3S1rWKEHg')DGBe! m=4;B8"!DN H!]RKH3Ag:>I O@5CD)JTσUq "WS0m'D|1tvj/`i $2NcTjȂa7,$@֞vR Y /&#0XdP.|@nL5 CFQ% 1kaC4T.Q'GA !tvj-]% .HDd'S ELF2SO>qF8u"4Ri QVi9Htt!9CdwP* /e<P։?MhLW$>U`p2׺ZeA֕eI52{BM*?Pr;Aps :py&lϚ3M 墱p4T<9)Kg3->4kӾw!gW$juoG|Zܦa2&j+V+j5k h߯W\_?Le4CHop$; 55  %(0!˟l1dXøE^b 4A&$bv&Uc'yw#d d+7ɹ",7P) #UqTXj0!y;0VUe39Y&$O[[V"N hylUrKt/<B"hq-Eu Wm> x Mfca,mCYր '5[DubyEvfT4h`kay[xVNdͅqD{sm}._ڸEla54c2 77eǤ}boc3ߎ,i_|g[-Qq\ݟ~rL ]Φ(nv߈9ko:@p0nmr{niQ)gmL'N%ՄԻ`Խ* Nn&NLN@QzȲ*R90 qk/)0/s-0Vړ+9 a+3o;q,2;-P=)sްRߏL:kҼCC+.̉@"H0;E@/`4A B1-C4†DO+ LEC3Fr ęྵ` D w5m:$cLhlstk{$+rq|SF9QV;ׇHp Ա(ԳKAIL?L= ;*oNs,+OBʍO˴1OQt7Q" t?R&3N SB>21T!Toq07UCbi`]*blNfTS4 PFO TYG3aH`eH82IXK.mF)sHyc8C?= hqKRD" Lt ]n Lk]=MmAuBNuSKNNUNTV <6u a3>a5Ua@lLCNM!dduMU(Âe.dT5?vgg;2"G DQ37V3ﺵ P GX*qIY4#sbgYԡ%$)IIS|a4&@V !^0@Vp,HAw sWROwaU'qsNW/O1r.6!j{ 6ut"^kt;VG@U_аv1NmTeuT1w4CwA,g‡%"? pB3p֖` V{}k5&v5lQKmBu%T|TAfrSn$TUa'~o_g*^͹;1tPQ,qr4ۀq偶&wPBatm'667SKhR% S8wxqwmwu8}x"4)cPQϤ]*Ky[)y!V\ {.YYWTHm%7Z~3|ӇnυExC9xW-4яHQoxqs r=TېۃW=92PyKAy%9T=xAɔsTe0)tT7wkMig9W8\>g!"XQ o3T 鍋{k$Ci9$W$Y7d}V%J2a}dERk~XZ]")sL* R8qCy߂6N%#OzOg3b/f >I=B8@&9M@7UM?U.'NG75yG CzUg$ޗ ui9 G[H {lxnyIWmV'WGtt!:ŁH%^_PUqρ! ozt6Mb@UI*S2$`Щ5uS"˶ej`;A zdZh Ei:Z5ع#KdK3IZ}Q3٪JJqIf3)Rۥs_{K۹;O麵18$\ѯQ&;߰.=[ɮ6{#Mg[[K\^hyŴ\ S*oQz)UXF.aٍEVij`jir8:bJT<}ޞBo4w pZ>37ɒ`)YYש^V$luײO|ۅٍɿ_W}po}Kg*&'XS=|g EySo"şo=;Ee±1!eBL\l"  bb4`ECPG"&Cc2 & S2n/S1c4N2a(44 A6 0E`9 F!pS6*N1pq?iP8(gZ U񥈩A3" F&Z9mp^ּ7n" qFR7.φcBϠ16f +~w? u,P ؃a4M>r7m^:\]z|^#x:euoοVkz=] 򻯣!;kPbAo =0*@H7`@J|F d1@ CHQ"L [&i IQܙTKyJ.@0ֱx`s"H,#H<$I 8 ؔKt1j:b̸)r*/Z. :қhl0TsR; T-\.,V3lrXEj[Vcgvj)δ1\uO%p8$' =o׬^}޷}'x g^Br!pT9y'u36/~45|=!JYb}H gfajߙdҤ+@0Yn K:FWfdIZ oMYK֭4Ņ!&6 FQPF V:z_GH:\qóϊjSP-1IHQcuJǮ'c-k$ȅyc/'9*=02Mg(=58^B\}-oE^Pgywx%gЯ.߳sw-xY5wx Lݨݯ˓+/G QOZU.dh%,Ahbj>(>ji'!Epp SmP-B75TZ 9FH9lpާiS* Ȭw& 0`Zv ̾+ep0-,% MB{ilG^MqB=ťλhMe_x 8?]lɸLx};7!˽!2`6 `:0`/H2n:l "!G=#:E=(> J>*>+B6 9!+B911' 9,[A;C= >&cD9C6!Cm y;A<$$up9$T *@ʄ'(hɝ0A4.Y6سLݮ<AA& q2:b$e+$dg,&KFJi+jtlگ+c Fo?|q s«Qv44DŽ6#GA;"Ft:[Hl.yލdC^GȋHj$;&SD'M7"6\ @ K;)*TrV3ELZ["]89TeFAٽ1#>cT3fCSĴG[K:3ӕã;d;L\Ǻ@̔+̬L̋4FKL+#06p5\ $fKxIƲ̝At iJ!4^JcEF AٚޥNe"FtƊ3BOK4{N̶t0m4ˣNTOc AY &P ?,PKDP5 ̱= Lѭ?L23p $/(2 /4x6Iqc:@[,"z .W-Al<ڒE{l:磄M;U."CםD0 DLwl6S,Ӆ2 SSf 6 P@ʽ-(0?Y8+H4:JTkTFDCTOdX9M4:oQsrX:Z x9G-p;@ܛ!R3 A T/ +|;.Uf3xa>JS>up1sOCuOCU9:wOW}z>w{˿}TKuuT*:+J5j0؊ؚJجTXN]JpY fY9u`EW;̓2h4P:mMyبcdńŕkA4D\iT^tNVҷGP*ۄ5`Hm3f+ Bॵ K6`mx`̂gah`к=faHx"paufK+A endstream endobj 345 0 obj 9406 endobj 343 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 344 0 R >> endobj 347 0 obj << /Length 348 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1>i*NBn,4pÈ#9 #:/* #;JCt8K,fMpc P+ M(fO4 LMv21:DI" 2`h<0F԰TVʔU\T+UJmG\C͒B`VEwjV6+Z]sZaoFZum=YoQfܗiް|zegV _E}xUpjٗ"X61(XVX=vqG9[puz^٦}h9uWUx2!Moxk9~W=:!>6"HM). @ 8 #l2c(C4#`1cx3 .?.4r"KJ- NcU=?F8eK}4[+=C*ӛ .j֬ 7}>{V)Si48Yc(ML[}o<8G HlJƸ8zCs@>t-s#:FArl5ie{C )$ᖑdta (-##zɪ ι߇9xO+(Z&C.ھyB^/rq bC2ޕKy̡ܼkrFf$n,܉LY5\fɒИOJNCEj7DhNԆQتBW艡"HnUt÷(ݣ;z ;qqSV ~0 $1t:E:u$%  S ]v&"lxfrTj**RQIrWugUe&.ēiatk,ً:T{٫ (66gO;]3cgu$66( 3cڇe-r&wH2ԙm>\SHߋFvE܈.DsUj]K6TޤjJfP$k/0CtE唵TD #AlHXe0n²I0`(9V\*(rP˳ڎśo݅f9H=S(͹o!kMefU9e02d[Ƚgv`vL^9KʙBdTrfV y.cyY{mгbEK}мL%4GSR@xPUFn9HnLuQB\( I8FU3J#.։2k\]".03QK=?7&s@/y9[>zO[Ŧlq?X8fq^n=启!?&3g@d/42;zcXZ7':UN?č%BYɁ;=Z(ҨҎ Ҏ` jۃ$MJ+.G8$@DFقt͠$L^wbb lC(bpl!O0o4&2p3Fb#>(+80.P ^ oopO po vʐKJ xHXfe0̣f A/jl5FⴏZα aP  xr0_00`:>Mu.5PRی8 TqlI/ƥF wOrQ P |!pGPР̝7вqpEGq-7Hݩvk YOM EQ.!q"2 <*B^$fথ 6&!V~^ ]`< "a .02E^QbL@nđs U\0`@c" y+Mrr+q-2,.Q .k .֘!R6 4yH 2% /2- 237 5-"4*CQ$5"a6T&&fH s2|R@jIͰmJ'PP*xM+M4<=%y-OH>?3M=?@*=@sQASS331Ia1ӻ2k :39!4=DI3NSA1EsZͳaDtX5F5:$e6rv6)H V7q77 &s*x/(Xӡ(W)3us3 PXK<1Oч`ҍ1D=OD4-=t- .:!AUNK+ACPrB-N+PC! b!1Tb2UJUbd)S4 VuI55Vs# 2mf H1HZ2`J l@JsG*SoKgKPCKLJlO[ңMRZvTN}?9%zT^n_TD]?R_Q`R_]  Qe`{Aa)4bU9/aS=F vG50vHd46]V f3I#Vi#]Wk'jE&t713I]&I՜IJ'[04:C+t)q\PO\?) S;sĔq=vX0mUn-o QZV1bapU# .1fw_QcVcUW>;S EGsLs1t9Et!EucY#/u5 !ǛGʡghUJk E֊N-̙iׅGU̚ʯ`qMf>>ڝjݮT+载=G]F55Y(l^E) gݼ%S߳l_ȵe-e|6 8@Qtm~-JA,-L>IMl>O\qֻ嘱޶0G>Ǔݫ0ūiv]_U}-GM~e"S hXb_{^ T>n|߮P_eț#߹>] A[i6u{I0 A\0FP2;@09 F1pR)qh4 ($@P3$1m*ADD0b7Lɜr.scGdzd >ZPR"*rq64xl" 9Dje*Cd8\nwPhmI/c mn!"zJt jՎp n5cpC;WR:}!-Em2Ѡ<oBԍ#4#[y'q$ŸeH$eRTbSb[b|4RFRYO$#cRKp8Lkup5dDa+/-BjG}% qr8(aT)k~d T8.-AО~@)vE4"ʕ1ĠZ an5h9TȠPV]] _`ڽK{nE%*kdBA9I6E L/ޒ ĒcnOc8q kpD F3 h80vTVxJ4GUR;LyiLq oX)Dcx`I:t&u9C!}![Ɛ WTe (IR8Od!RrM1Ͻhmw z-}γډv #LkuṢׂK{M}ڻKF6Lk*#8zJɶ#B<㆙nxќqlE&|wɹǖP;d`eAj],δ4>M}?!ъz7e'RCN%ҚL=N)MT03 dSSČ3S|N9΄ ;NS\adʌEb3Wd*q7/!4J@-\g|KLHkRA$u8DPWheL^XeuXk]RݷSpI彂fVKXCe`O&S(Flx endstream endobj 348 0 obj 9616 endobj 346 0 obj << /Type /Page /Parent 331 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 347 0 R >> endobj 351 0 obj << /Length 352 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1|çVmDnH3#3W`T7#B$KÕ([Y,> x*A) )R.ZKC6|X^IDP -Sv<mȭ69]xЇ> ,Y8ݴ>o۵ڳ\$AtEgWEt:"#-. khmKK˖)"5_)k )Ӫ" b.-(Z;?PRݴgP:1' #m@sLZ8~k ҆ot$ oR@)XNM1GA@]ӥ:Ssz^E/;rL Ghw`5@" y0 @rI212RMConAXaUЎ|``B(I,%H¸n!rPQ &P"3 0: l1'Ę) \(TBÔ# g$``HhR@x؎c|hJ98Qte1K V$5QR"$F1FH "V9HdNdzY!{7iO=>jZA'Gl !7 @&ad 聁p1*j@5FD`cbChB‘O}_s96! A, :㼫ZE wUeCYtV/1H< }E?)5 ᤓAD-( I%I8lqN)GZT3P4hO*!4ʪOc]gi=!51UJY!")-j2 &uIOl^*Ts^lъ׫Y\JM+NPٵvO >CeSBi=5؛J'58➉! .͜'AL()&PU? &'2P6×A)\g0][v urV]tk;7vK\) "^+rȊ~H|}d&9q߼xpdtY&>ôބFcpyyS;1UY\a`xdF7Wk#_vCCI3I&8jQc˹+ =4wa[}R#N4@ >n 2! kj9m`VX܋x5T*]$Vػ kȰOfw,y+ڶnժ\YvvJbNuUDTr.&YWX~uxO=k;;=u]&HKu?Tm\`jO6Tp\2Mo $&ھ7~Yf"8,{z,2|;#dܷ#!DЭEC[:Z4o a5ðeA4ݝ %=!rufݵzM4t=+BvMU<.3ok#KsUۨf*taIYbP{{b U{!NUgMPcw=X^w ĻN{x.;GuY;߯ p);Eb)ն꣮^@W]^679Ooux'j|QN}.)tӪ?s,Pk~i瓂0tse98W\-EvNNFg P؞oҥ dŪ.KDmP0 ynp*nnʰ@bl@pPJ$l+.zZ. TY90!hzNFNРXje jM O5L P p .g eB&$dG%@F Bl PoԚc牮  jvҏlBS/pkM( yn $dN~Dp$(0H% p<+Mw 1{NQl$~-oDw P\w(7(dzs@G #mlT em -  I@ʲ P;B`hf4LO@Ș`'u cN~.m%q*`gBD,sPҀR.DPdi'Pr$Qp'.ıtri)Q.R ])2*ѐRk)2ͪRqtq--LSp+-; ] 0n0sȱ, #0 1]0O 2&L MϬ<.N gd'x!.r1%o& u&IN58xlt#8y"HI`r|"Z˹;m_mLt4p!S//4" C,Bo ox4?D3.Dq2%EL;!BB?6 #O` w'Sq(C,k`xrTϓ}K8O9h/[Q8Y=K)m1&ӑT;s0TH]tR OZ52CPO%Pu ?Eo,QQ3-uSAU1RBuF,O2pn1SUPD+!U6!Uvɳ!WWAUyFVUWVF/"p*l%T?DO#H_I) hT L7V΄%дYfh) ڟ0Ҝ M34U.S3"_+[_=j5 >can>@ !U1+` mRva,6;b2)_;23Td"TX6SUTC3f =g3DVg-21TY"{C4%hu!Վ`=I*6J#uHr=I vCeµ"Kȶ& qNEL-^'T:1C^ :1JiN^*R*4=.po`:W(U=gw ]q?R$ճ?>8Qtq@tvO1QAWtv\EsW ) !fXg,C}wt;?WxuEW)yuv1VpDvddTգk5LkI[@[oDqVfU8z蜓`NWҕ0pFuK2Oa7)Uc1>bRc;t'wUAGA/yOq@vm1Aqp=2ӇHCcgSTWEh43BXiupFNG5$Me5< R l"Q$RZ~8on3nEn{_ML>RWp(iqՁ7pq Y6v1pA;%@C1IwuxaT9\Ri YbQ/SwvD3u2uAY⬳BYwib(;3 xÌxˌ6&%3{lMmu17q?nyM I_1gp2q (mZ1/C@30+bO'Q?12Gdyh9o!3yv٠s&r-^KmgM3gh3K Zov'T3dv})[GCu{5 Ę_knNwxJܶ~6W=y9$ `ӝmLD٬項KAUSsIr+UM%EG5xQmIJ[/s{EMYEDE ͚yfݗnIzxDw{6;qzSiis-w5J`b Qj5:@΀ q]tf͎ `ԹJY]SSrr^zPVIYN;qV{H{ͱA,(3W+][G<;UgPzSV8_Oлr$Xub[G׻x# |IK[7\٭]81)"lGC m#˜11;b3YYK?C;pud93Ñ_;JCy-7}ދ[[uJ6ͽ~{~5﮷ώoB{7I~OQ=qwGw}17Ɵ e ߁3Ct\< TLnBqlEFYqOic0GBC SW9ۙɡY>K^~i37LUgNè̹;ze4в=g{n?K< 6]k^|woQ WYpWoH~**Mظn$KVֵf[ ͷnè4{:]U.lr34!o bg O"O*|xЯÔegA`;&VFs21R5dp=gȅ3⤀l6 RmMAOh](_e8;spu)2&EZmp(c$~laGxaʔFj48LIVg%+VqN"9؞|Yr6I$)F@k1ѯ:fwy4֛ZۻpCj8a|:9K:;m>w|U'JLWK@ Ը7ԄIcƏ ?&A$?0/00NH>_6i&ƹ0DjIlxto 9&9y*tFgl92)M ~ +hMYxĨ'.ZEh >8:"x\㡛# -3떓Z5ސ7<Βϒ.8g={(S˙Ts┏IydS iv(1($OZ,ejA.EEP]A?X]4탉qW 6۝%Nb't$S=kE *Y$ꖃ19IUgjhpve셵R[hRt):' 3izvk^;no0xEᮩpuByU烚dYQUT^cgV`촪Jk5QVv.#S;& ҽ4p-:XZm0d,v/\9d>adcMŘFHm $XƺXJ ox)Cc%`C7AMU%N䌀TȪ#cfi,d".I(&at$ 3[!S$E~K%;d PndV~{ |-IQVȓ^˓8;Ƀa\DUE8u9si9`tדXe6S?(½~W3kr[69ژPr0YGUݿG" {ynCs)ӹUue'h9ުy;0`#1Y9x晎vIm%TޙCSfiUj+}7>Q|@n~{V9 s]o><kH5z'< m]OgCMKzkIǭbƬFN؂=IW{(UAW[Jk%w#YDZ~캿lTy8r:r̄i)77]6l~/r7:Q®={~ڊ 5P>hI|t@j @º@˾@!o@t 8ۃҮP*7)s9ʜ<.+Q3㍸k$#ڒ=92b=/> ڪ+,#d &<B+gB*:Y!&1k6C7kÓվ;?;Crw\3:2@CCB+m9.J@{D 2dCT <4M>Q?l*:SGs-H`r6(3(7 /07p:76*=3؞`#4 }b4\%G9H;I4:\l;cN˜ʬC7w:C" ?,NLqD? l [D㲾CyHȬ; ӡDMHMk:͌4ɄęP<$XZ+58Ѝ̡J$JDJ`9H=J<)O$F# B#*99 BMtjXI̍ˀӘܹ$Բ$.92 uOv Aǻ ̓.:ā s7HLAДˬt]+hDշeCQ ?|7:hlEN$I6\,1 EP/.8N$+E9\'<$Ƴac9Q;},.bЛ >3T7GQD{Tm?5A?Aԝ ?FTHAQ 7K@9ȡτ-eS\L}VUMpU ( լUTUG}^2˼N!N1hT䳅&m(})R7<̮|ʴ / ˌf¢RcFjS7“yϘSoPSLؓ{<$KVW|C X2(U}{+ )MŇTtŅL8حJ1TPMmR\BYE8Ym"랑h5eЍӫԺX]ѷ; ݣZh@ߠq^Ǣ CܬC ݌Hu\&1Yތ^%L( endstream endobj 352 0 obj 9249 endobj 349 0 obj << /Type /Page /Parent 350 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 351 0 R >> endobj 354 0 obj << /Length 355 0 R /Filter /LZWDecode >> stream Ѡd.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J!"-Hb1ď8DApoKUbXj#C8[x9&a)%=h'RKqd6PH_4%gBiw09瓢vO:C8ڗu*I:.iQ0\TwN:~h"Sga鰘JZ<#{p-<\jUU t~0[Cyhp3`,@bdc˧);i򁒐w Ж.iXKGC{sI7וӸJVYLჳ,>D3qNhq[?_<Ӿ#Щpޅ?Z>{g\zΚ/f%[LCaPQHg2m*T؉J!mѩ SfBGڌMTiS&Q@s,"g2JI$/r(rM|NqDRqa$ XT* !6!8t(C;Pyn7@CxdME|NM;V$Jz\deOp8AA0 dƑFgД7REm8k9Ak-RR=ۺ[PvqxH0$VXS >rzp.*5 :̐1 `.I)~b* P gD@nOV BIqc]/ Vw HB<KwLv ck X`X}uk¡x>9 m 0Y?& q ~jc $ݹǏr]+Y 1 IŤbpLSs5XK~ !LtY _T"b5 }^^&jЙ׽f i A:PCm UW+Нs @P0sKb+YqR@6W; slV JH].pV8I KEy\∖%&N3f|i M|CT.GϺR͆u"Fv78aJ'#x9׊#}+L|&heJPנ!HD{=hg2Br la LtZIHS?u$;Z$T))׹`ǔpnnZMXoӒiIGU(}Bz="oQ6zOr~*y'gd|y"#ϦdJRN)UcܦOҢ.}Jhl0j\00>iP&e;(CV>޿exp&a,,h²K( . 2 6 Jd O8dDNH$MNOt"QV&ۋz$P` /l%6oON l *崠ogj ,0:ojxjNJS 6Nb(&b1+ cB!h3Y )nniQpβtI|0@Qn)}J1\1nnfeju# "k+ \ q  0Pse{ >a?PU ) in,%|@ϥ )!HBoVۭ?=p|jD)IX$ {%r.;2Tp^K$!|hmr((Rs(nR[R[V2i )`B戬\e,Q,'G,h)2-0Y/0J_Hhs`r"g ȋQ :Όq!-& Rr  E$ݰQkƼ"%%"D3j7 e#$EȖby7Y2of+.9E&ӡ)oS*2ӫɄo;)x8<ӵ.5*/s|;*[R]? -/?(c?)"!@RyA!-2W pn+BdpqIp~q01ԳS%s//;3 dEмuHI!Gͥ!XÒ$a"25!7EI7$b;3VEJzSUtJS&SK`|z J)jOLQ;'jIM4=pCLiIJt(dDQAmL Ut RRn9RrRU2T5O/Ѳ`+2$`bCu1q40Ǎ`5kEqD!O ֍K`k8 XAΎ>Pp Pttj04( "t ojP\.Sb,TzNǀ@k$4 &JkXΫ_4K&v:6;`'aSuj;btژa!OQU61*[QPҵavGQUΞRR(v]'De@-ffMt`6y5Aggh6CAW֌Ui6aZVf:(V#FUik DDMx3đ1ЉXq  #Y-Z ;ZfpQճFI4uGGP!p]-]w]n>g^eI^{>^[urtt:QU7Iau6#ggN3fgN7u6;OWYd9O7s [*zWP6uY>(f*`7^WgyioU7gwv7{7UH(whiv7t+Ib)"NBCkXPDգ Zpo, `oopTRF>`/x ]6`PnxpK8G)os?S`g&&ST8\j7Z8+NXyvAoawTAxQQYOXyxuQA`x<5CSfx6@3Sx-NǍ|8ߍQ׌(dS0OEVēkDpD# rx ծȒr8 lQw1)33iy3#k78E XJߘO}bJ`d J3Ay};ח؃U9QV-a?c<󙙣Ny8Jc٣͚>ظNA-MTv7wA1aye7")' E X 0p9/6pM!0yqx:̄x7+x7eϖIaM3`kt9s{baSإz9V9u"5)-.ьͬÞӶGUt^GOیퟎ (skwD+!y2zClEglF23F\Z54 {v!8'R0+)MUzHB[_K'q q'zϮ;a*1z'aSkNqShI{9[\$+.ǻu-ՈŝҘF?[Թ߽:ټj%|^š*ƿi=~|kwv͑ ` %35/rI3{\S_[e!Gy:܂P;{bزu;؎ۗ݃ݠ<ݕ=J^ݻ'{]*zǽ{}ݷ@W{hPˉɎ߮ϥ3DHSsC1v| /3;# a=AL=1CQ?4PyYwwS݅&eu+~]跕[ݫ>˻R&U9>vh?zf᫦|ӛ5:~u|>>\~O @˿r빟!ۇ~_-PA38^ZTS?G endstream endobj 355 0 obj 5295 endobj 353 0 obj << /Type /Page /Parent 350 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 354 0 R >> endobj 357 0 obj << /Length 358 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1ǏR&Ot(03:Aq$rGqarF p (銁'*b7,^2A/3$ΤaʸLb@& TB5 $Kv6J q%Ӝ #WL:-@c#IroL,#+.l$oK\?50PSU#LbBaQ Xa5 84 #@94u8`#H44< 0ACt&Áp9 x# nAn2C2;

fKm\I/T#Q"Wd =]DXaナPjLH%DEW,L[)q.|5PaMWàaL:Pf[ˬM4z D)|粸J KցI!ӎY'AM0>H4hb+9ʮi4ߠ:qK@a9HH\*/=̞2HaKqRbM4i[-5 ^:70\J70Lf\#`͈E0S x 7ŽiZ<"7#]hsB\^9SZa <@nh"G2vꊰۑo¸[ڛrSv}W xKeuV@ap79Pcu endstream endobj 358 0 obj 2185 endobj 356 0 obj << /Type /Page /Parent 350 0 R /Resources << /Font 359 0 R /ProcSet 2 0 R >> /Contents 357 0 R >> endobj 359 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 361 0 obj << /Length 362 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1>+[:# ɒ,#3tGazؿ `co2 8. Bx7-| Kv x"p* !la1 z?!hۿGT݊b@(O# 4GB0o/IRI|,T芃C"à:!h2 t7Ø44#80@9l!+&RH{!M,LܹSUUpb47@$56v%$rUdܗa(P&%cmH x7c#L+qSp #`:ca@f44KO{K Mfvh|"sRnj4l,`0R*G,aHJ\ٶD4fn]&C;^;1 l2Zz4205%*%6 Ai4XHssQYkļMZItt.eM>xW !ͳ+Uj’$Xf˜28Ac\ڹ~jr0w.jԮEJ$C`NI,tP=ڜ%{ȠlM .y3_KYaC/ jX$2<b ;UC}ƾ,K~E5\n7Jw99W/@*w4P7%[ 7RhhbCFti48:Ay@ufϹ69T 0 C E-@qLQ BD 1:9 yczLg " 1l$r ʹL#;15|IxԃthE(rP79@9 nuY32J~dFSbb&sӊt[@bq/)(`S2>b)FcZV[ Q.`Ѭ{P,lT:#$X^%s,2Y_ 6BY#dkZyW> /ProcSet 2 0 R >> /Contents 361 0 R >> endobj 364 0 obj << /Length 365 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1׾h㭠i(:ٸdL"qYcBx":ۗm߇Ӊd("a o7'(@%#ԬĢEР@6 覬^ <3D{ U[g"ۘl5|>r\A AVēpn<G!W-ɼ="u;~&Aw8/H?_v^&'u$(o/ᆿ?u]eyz;uGiC3>y|N'_S4T l]sȇ'; l+~h1CHnu8bqDGlLP#GHGA6y2fBtCha NB)(<ĤeQҮ DlQ"k<Ъ7j,D%zw9׳*\Tn Z:GUOP N霤T0xmIr̶[tn:?Hh,@9 B(jT+uԌؾ-z)'I { kLX*aP VЧQIRZE a Nn˥! G_`LfT|p 03y57im7@   1p2(s-m͙#i.lS_0o@0Cca9A:D aGl5]e6fe!4J#XCZ(4xz1o#v,/E] TVo8DjAaa0ǚǎpѓfڙ˟a{6&k͙t< #upp!LKHFdҁ9m[Q acLÞ3cj=s Bm1\ ♣6O;:Gt\td;9t^Pp^xU3h{NWBy%.T9gT̜Kh^ջSE* `w09lo2:BِҿC0Ӿ,䳠kMmv(ԾgۭhH < .L@ ҩ8 endstream endobj 365 0 obj 2367 endobj 363 0 obj << /Type /Page /Parent 350 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 364 0 R >> endobj 367 0 obj << /Length 368 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1+ ;@0 0p9#r80: #(:Ȉ0c@@9<H #7rX24Z MhdwVBq5@t!])(.(VB S;j$ r oEjs"Ziqy(h^C/:gO 9n#!GHN(#+rMP.̇6yE4ФvI'c$!H Ľ'GP$@B ؤ;i|f),'/ ԩQ$// =\ՂoSu8[A𓵩Rg(~f- $0Ctâ2Q 8u(i IT+J޶Ȏ۫soIIQF*ڝGJ Rm.+uͶXЫu#ۋyy9)AG(^+vcw]:5w\N ߫z躨$`˺zYCҦ|էElENR2ReT 1J~t߼ E2^h1.WA,{~-8&]ۿNB@ݬo};/}p-AYw}Nb/+ kz$xN.@-x"<>~UB&@)Tϋ2 Q33ATm6*_!YCH"<-VmO@dj-r\/ΖW[eBDc;jkaݕY]ܲIכax"H7L2xULB*ˆz:C"+ϛi+2FݕЭkuu={i2ư\ze6!ȃem}w^xBv흧ƨ(âJҏnٰmfi-oq28bYcøEZdb:t7*ꨈjBF}w )Sfv~N"̗[vHy^3N|3>4aN-}>^]78n>[wI錕lC*Nnı0A|-  `fр= ɫ~1%@EeSʀVo` 3PEIV>> /Contents 367 0 R >> endobj 369 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 372 0 obj << /Length 373 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb19 ,|R4g)6 z1TGȅ>j>q8+c άSȆI5vOl> fuO||TUFIA΍x`$"!X} b`E0C86NB5tWlmes<슥Y9N~Yk'%}A9%`9CzmsqzU7w? }ŲgFoتvWΫʯ1=y mwnw*Fl_1q 轺ɷv>+ ۗ&Q7=OGb-緧~B޷xzb;3.6kH.cw"(/HB̢d訋@y@' -HgOhw0+}'},5L~*fodBiBI@l)Fd)L &^nll&n8~#4eh$VƎSl^ZKF %&O к(m O k (;Rj j, J>pJp.ʱܲ"lw0G/PmՌۯTJ$M18ZqR=-$cܓBX" VƵrc4h>d%#Bӝ3! D$G:*3{G 9H `Ry;" " Έȏ̑:QHs!L!?!QTs<&@Sܢ1?62; q%A@bCT '5fƹ1toRKJpRT5(F$4NBˆTPu# 0^/p ?@̣ n@ eF DsEauϸTŪG4[%[\%\`]]^@@^H@H%P Fa#:akE HhB&k6c?FdpeS,\\f]hfgtFxgƀ]3hƐh:ijP0,Nk kwO܀HGMyH(4N W' KgB,"u,hޭEd43v$ G‹ lj'B 0UM-5Rmv^(:yFWtMJ{ToGHc':0J}G~h~_\1x SPŤ(q|nGUE%r1G{xLRrn5NKc3O$V;_ Bz^&sV# \¶%s(q 6cabKԍOhd⎕607pdӜOhFy%>xՑ `]`KiR F5*LAk++y0mYϹ;VE,H S1@@F4 @i.c #gTkۛHHw rV[sUY? k1H~zS9ǧA9[NkZז:y( J =yٖ?W=Rk=:Gtٳ7YGY|"]ZW5zW]"ۑZ*@`F+Xg?'ǞIHxeuQXy#ZEYd;]8YuX}|Z[sc{f"UzISR'yY[ʛ[ &js;9{2u{{ "s$t:8.h ٲ#+ ѳU8[]UFHTO}ׂ8-#\5Zڑy?d=ĻP97ۙŴ ɼy9ӗwӻS:Gܡ |{ݖ<˰|liٱ&bi;H /`ݠ`@ #^y'< *CflgKXceZj z]EIyx/-œI}9EO{ʍ,}C3|8幽.ĝq[{C}Z˜}z,Q̂RvB\4d$`@^@1ޯIRx6!n,xF# [Fl1M2H?-Wյa25[w G~ۃՍY~"^;ǹ29Y9}q}>x/]%LO ̖MUm4hVwZ 7_K|%a!E+AC}cޮ65:]}yJ5o}5~cSnGΣ^}[s&E=jҷd3<^c=~kCuSdB7> /Contents 372 0 R >> endobj 374 0 obj << /F0 6 0 R /F1 7 0 R /F3 9 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 376 0 obj << /Length 377 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1>+kbn:1' #h K,03DďfB8JpZ!nH%It) 9rp: #x a2Hղju2M$t- aJ=jԤԪ !U=:U˺ԽLVMr!ujεT%!ն5Zep55gA(*#bŁj ܕEV(rwͭ^}RTBbT8]#Urޘ& -!aC.⸻Q7ωc,c ]^FemRB0vco" Ê 2àkh=N+*!X;QAbE KMd:́@TGP) 9 (HUT<5%eZ]cYڜ]b1+|VSWt6~;_}9a[ WIR<7T9?=]]hwX7^wgsw=g\j_uI7X^]Y߱{YT߁vcw9$w 9aU 4p]N]tt[3h ̛+Km"gPCzD3`O 95JHZrOzg.LqzA7'AnUAY{&ĆUEU 2xg7Y<E脭k7f {yEp6Q]G5ʹf1Y d#XXL~/e6"J0apu 2 @1mpA<s-4x"$ H2~QI_RYo\9:&'ǃP9r1Nv.nu$"(ٓSY>"{{X%t\1u;M>LV5Pƙ=a\tN8Њ1:bvz }I6d:t:P)]vSGբa{49%I5̺ɪ,xK!<6%$=*ƤkQ#aguJBH)CY`!uRcƧOJI%tyTVd|O6SA#DZigdGAQ +bK$ x nƣbJIeBCf~VL{EEvΕ ʁG% %1 )0 {OIckO 9:@yj $݂{uPT A&~HB ĺ\PbO!6<ևxI!"tRzQJiUt]͎dƙO 2mTܜF sD{D poJPɃPj~A_c吩CXr_\;9-XHN#ʏRw2@ʵ+e_k A05Jvn gћ=5`v^^Q:CU7^hи& قtfn5<3٤=/lHh6  D(Y[-arN vCdkad:kLֵvFvMPf!Rِa°a ۭv1E`VMA9-=+Ak|1*O^orRcnΓ;lr:Z@sh U]`wMUM-H䵗ItD~g!we(͔Ms/u(AP4@هZkV&u1. gfӷZkmUr}$IM`w顣ӏ8o A0#ɐΑI/Ei/6@Øo a((g^QF(]q\t$$>sʝ?O׮=&O~E ^7!@͔>a}Ah?ܯ/'125 q QWq+GÔJާ'oO[n/hWdC𻬺lML-o.V0&/g~ 4m(ϭ=oа c5bk`R`:+j5$F#BP,(lJ@ɜKK XO+c0গM~+*Dk6p"H&&F l j .  `&iIvFx ^1< CLN dL +p-t *Q@ !1 %P`ĖL T0&s f j1f >3i"@> NEk.kK2@ =++KJlR 'W͎lj o)+dV( `oxۃD '@D9Ipp1%0lJbصOq $yImk; ЎhncL,L|"@G&rPHJ rCFO&COˢ'C ƴT#AR_dn0&qnL )"NlRA,ut #L).1o#@&)fEk9 }%*Ā $l# (!A3$ch: O2E.BCN*'Cx7Àk4~>+5G:#301#7`x:$ I:2) ͺ…dK b5mm G;nHYp2r>O"vP,2P QG g)#((#BvehtgBg'CT/% DDj)D4!R$fcE݌FCAF$T#&`ttǐuE(tL=ZWttm**HχIԉLrZpd(W- BBıl CcP-pG{W" -QGKx q&tDoܤC=2r;5DS7BG gT;EGCJCU`td5u=TTp)oSLGW1H HoeI%BkTaHKM64YK=իJCKեTԉ['4K4$> g0"иjRi[O46)$dBPU =iPyQIQ5Eu $) t>>_S,BZ+'9L%b#W3b|^e 5OUĴG`O=Ԛ#o\%R)BS|csvIyx cItbuszqsucd"O`'kXCuIKOVFQhv8\(Fo6dl6ewVXPG8v 9k\R(jPHDxzڗ3 O3lĖ.`c~ȼƃ0Fvp(ZrVForBycK5G!f?xXSf#kcWo8y6qgכu7-u) Iգ79>L)Ò5v1TTD̊lPrp"NY&ɊA% %`tv~8ǎ8wmɜw񍉪)%u(_v'Bj>vXfKKLALxiyS>aX[Vhy_7Czu V)@xԷWKcZנ{ ÒEMZAe5/Lg+# FU×ۋ-n$E+ 7W򛙲EwLY9άEz3VGf8Wa8=GEfAZY9wW %.mjnq֘֜(N7KS['8Ӊ' $Cc-  yVJ mŧ_xŨ9=~qZ'ZIZl2sY۫g작0!"WI'Lb>)^C~)~[4\2rdl-ľTL~X@eJ]Cb2g:~{^*wR^]'RRe}uXWfu~K6/jNUsC/.??!ߦ*Dml=߿>P;)Lo5[X"4!`1qpl 9D`liFG!p؜V.2Ȇ0($edh[Pi c$CDB 4^FH 6,-t¡@(* ih\7TAF7`Հmh\0DaVSN EY a]C[)RW*ES (0cjr(遹G)nlFAlsmr?q4%yH@S"$Rhe ]5$o`BE+I :L'%BH'0 B(>LS "*:ZЫ;첦̼a<ɡF0à9 #:"p68)mZjK,"3r 8!#lJ!z"p8!AmBxIʼR`M( 4In O d;5Д"|%rQTؙRGIh:EQ2ѣ]MGT5XdSb =2&?X5|dN8@2)S|̆UcVMfZEUY3ݻ?4^"]٬Z)55D-7mP[WSִK$JN^d^ CGiSX-uTwF(چUjAwa+N'`]Wb67}c|WWBשRejfU|W{ endstream endobj 377 0 obj 6110 endobj 375 0 obj << /Type /Page /Parent 371 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 376 0 R >> endobj 379 0 obj << /Length 380 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1Br!LV=hdOMSnTuDNj@,F)! H6 d"34ВajMӄēO3L{s!BoCJ9 (w(Czx: #N;\3 9xqrhBP90xc8ߓiN:0Lḽr[4i]|A:MWv\NԤ͎Ն=n5q]o[7mgW nq5;'-bUհXvA3R&A!Ň!?{qm{q> dmGE] vK6D~MV]JĻ'/[5_ue[VJ\z)=W$C|Uڬ'^A˧}n6LS܁@^TY#ؒX^5X$'9 bdQIa8(D"G^XT0$6Ε[3.& G)z>5 (`Hs26 e ̸2Ǡ8k =4A!<\с+R|APxoi3F# /c++0oQv? 80@CpAяēl@@ 1{sϩ?p@[(AݡERNɃ)'\Ds}MW QH \B 7HgUDO Y0@Z;uSTӤ&Z'aQHD G]4ܯ̀z_Cb*XO лxU&53*XQ46Z;)I"bi*̛ʱQ"dr"%40h?ȧqHjgean5ކxm r'A$gp$1sh17gkt:UvA3кxoLl0'J~ͬrIbݱ`q\5 CR !:@ 9)Rrvl|v/@*; 8/S: OMxhJxsI ҘpTeZ#~5RzBp*UQ#]! …J؆,tq;/ r{7="hV *}MgK9NϹChbIl:?qi)K*.7;sFRU:kH r 2d=ZҚs&xU8yS-a8F̙>pxUrٟgxGo&kmA#\ hʕ.O]%x EG~꾀s蚫]txHWK=A.j&UaM`rO+NO 76X $"mF6vLk37M&;+QF®]g^[U L+[gB;qe91yC)/@ߪ }B}, /)`_ _q74UzE7>st;u뿉:];Kzw>[vЬY9["" n_j6܂`"UbHK2j0$Ģcf^O:!&Bum:$ӱ7ӱ iL!S9(0ZS6ӑ#ruYDZ8υ!ps?% KY h??=@=N= V͗1bX ;ĖI]dKͿK܆Me$/):y/N }G Q< 2ɓh=!H8G8q:*TJJ2VJ;gDGIN3;TXK>}ĂH)<+8 g:L' L5NU O [eqZWNĊbbtt><"FO6"+=I-R`FMnѴJ4bak*bHbcF8BH:e&Ve^bJ`f:fVBJghFhƢiF j HSt аIFo]Ihy[]2hq]D$*,tHr}uc4wcL=݇OGc{XK Kv)Oi d!=SL6]LPOg!6ge<h%eEjvcok]eR endstream endobj 380 0 obj 3633 endobj 378 0 obj << /Type /Page /Parent 371 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 379 0 R >> endobj 382 0 obj << /Length 383 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1p Bȹ⃮" b.3k)ꌓu2Bܦb,"a~+dϾMn(4B$ Pwn ¢ &'&yccġ(? FCJU!80r9eTtBa -w>kJSHAX)6ڬXӮA@&Sy<䤗 lQƀAx eM)`ć!Xjhnq<(ލ8r9XqB>EHwx HG( cB.$SQlF fGEI@dL2JĈC|>= $JF%hj~1]6\ٝLEfH> /ProcSet 2 0 R >> /Contents 382 0 R >> endobj 385 0 obj << /Length 386 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1 9 x6ar%: p~bx~_ /0u)m9^%?13WO+;Y$t{&1܌F H[Nq"L`l&jlZ)Al6uh/4[ N}^z [W=CGDMi,h{|N>fgCxԥjVy~:B$+,j3iht5zwXTM87ʤMSʁQ*@~ UЗ˱kQ2ggO;ufY9qIJ9^f%X6{7$P~5+#ZX_qSyQQ ~5; => 4@P9@apfdC!8P036&e#QU6 Dͧ 4fg+p`\v<[m }˞7{Ӭ[=&GeAk&Ip;kd P 4RLIL\3Μ[ZOC?5 2[k22s9Ѽ\t{Ȗ_ *,!{LEjX"ɜVz];뙁u<aM0fø6\sh+t w&d{c1XeExK9 W(VcuVttiM (DZ&mS7l)rpNIHأT`骐gI랤 $h5T 0pS $(mQ桚!Ywy^>_?~bm= 0}qYN3;g)3w͌wX.aDhm%pAxe}@KQ"GPMK*nEbYz =b_3|J@VapysPFQeh%5> /ProcSet 2 0 R >> /Contents 385 0 R >> endobj 388 0 obj << /Length 389 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1`ht3)$BòL8T ,R L2}&W"f@FAr?% ,k^o<'HEO CQ2Y2(beaY~hL"BY@L"$'@)8䐃,zI0:'\)PaTBCCoϬξW)!Z-Va:7H85 X>8 ,/M!{$2=#i NO x4&3%K唲8Efl:prEAJ#X5J9B3?=[P `P`jl#L)@l!!G|6J0du5,*PJ6mUmTeR#WJ*{к r4ڍDBU"ҳKA"R%+ +JGu$S dH50URy(R*JEj%bU+S)e>Ti}  E$(Ҙ;xbH-S*juag/(c=`C y:SJKZSvV׿P-7բ$i FUNֺfԓ QkF~JYAB흈"gN!yO=BdMg`B~r>xmE* wY*ubX8QQ׾`pk)5549 n3Xl?KtD=[5u3jx[$uƁNfiB#d1lJcpm1V*dOyblY'Wfn b(ʺwt* T|ܾa"TO 'gĎ:G6yHUr1 4;^WDOvA"F wI3UWF Zl0>L*tYHBa9-3噋&ɘiN*dx7rgו[7Cr:Ռq (8P fw QvtKn*wQC )A\\;/sӓ-X1zw H~l&`ؖP{oQl9f[ɵ7Fƒ+ ⶾ\[c v؂xtz&b-aWK im7\k樴^(ڡaAqXeUP=N\84Ar^1>56eb/'c[')< {FgYLySG4H4nQmT6fi=l`p "XAM6e#Q  8Q^삒b endstream endobj 389 0 obj 2392 endobj 387 0 obj << /Type /Page /Parent 371 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> /ProcSet 2 0 R >> /Contents 388 0 R >> endobj 392 0 obj << /Length 393 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1>++VEsP:A>,(r:03:Aq$vGS)c(4@"à:!6sX9 sMC2k>$+QHKbÊC4sl8s2HM@014;H4T0@:c(x@3Cx84¡԰C!H0E^ 9Cp:`3CW#""(YT7[u89TWA9փ(w` UpFtE>2!ul 8sHD%>lD\ J&4Y'E *0Mjs;O?Fļ#Qd;n JUu=PTU%MTUCMYWMՅeZS<+8 #s@Í"XtD> 83TpAimwb%Sp7ssuQWv0 456e˄Q6N8FQ,ɠ%'[7 8.X@'w sszX.joqՌMK[gŠɖ(R\H#3dOw" z6C΂!;<M1glHH#B^p w@TrM+P~? A3 H[ZfGifdPB /\zLd[!ruAKTV dF29Xr k ?4VqsB`V~RD? I d]ig !$B&0c)|'0tm ,:$*ȐO$}Cbh(o=9<'Q:%u;S B%tpCQP5JzQ^P D„fu :x ȹQޜV䊌#K-Nb^At!,#sT IVĦX#}1.Pddu>Ke K(:XdX c|<۵Iկjn-uv~ӽ풷fXijuMg;es.u40܋d܅ 'nQCL5*R:1vgg7f\hL ;]Vs@ 0A&&`˟h$w L.p~L<=v7MQD<)0]At-ޡXH܊DS sM~LA㴗_S o/\bBǸܨق%XZ1`Dv9;#Qa< n34ϗ?35Ӛ im!t'A{ =2yQkՆ&@r fZ#:q}UcgsqCs )4[hh*s){Q3[sBl}NxW#]}t٬o iޱg,qCZkLnCNŘ[k 3s#}55 4A78䙼{ڽ쒑]W5Lmm(rf`G0xZkZٕth7$@BxD%R+!Pu15΁0x8ֆ>tB udԯc-@%v)Ledy^CÛqEOl@'Q :GsN-pe7AD[_Q+LH7gܢKSPyJ*)FL_1qb-XF'm^趃W`&z'ڗuQ0~4dC.v6"Jpʊdf*X`%\uh\_$DsViBg4 H(fWE /n3aj $:  <pDGTCbZM(άopYG譆~ø#`'u o/'qc2dR!hFpKi꼟pB~0_<,dbiĂEJ(hlLCcRkޭiv*f)h. #~3~"`vhMjT ”0"{`Q P&*V(6D=6)".hV? bJ l,CdSJFrHF%â дΞU 'E0J5$Y,gTIv$A`N`]EB9$wyѕ>/̫!*5TQ1:*1dii M@ * ^5dgig&'#M'nd1&v0[gns "%qFQ½,'Rt)2k--y(ғ #Q*%̺'@yFPv\k2,nS ''( m(e2=.E2b1*qbSS+3W0ҾI"a6s#6ʽ2s26&'> /Contents 392 0 R >> endobj 394 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 396 0 obj << /Length 397 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1Jb,PDUH dbԹk>Dz$H(Êc(:p1H3zFM\##c2$,0 m\/J]"0tPK+3W5B:@& 6F* GA4& 8Q/Ij$Γ0'Z2ZL%]Q\#uP 0R,2溧5WFefю]V7|,KOcYֵ ! `KîBW^0MK y8( e OQ]wiPK˻Ix.0RL pЈ/F[MpIr.ӺvC\ ҸW 2u%-;MDtԙs4u6ߺ~v-ؓSjرSNWj6ٵ+dk7֡*SV2y$W¶]* q2Wz+UΓ\ړ3}H̤]_3zfZRmx v#bމ4-J}kC}LCMԎO?丫OM̦ΩҜlqw+ɲּreuɼt_5Q4Kl1WTŬufnT?o]C-mYAB_!QoR[YFoIx |,<.i128ۗ[sBzNhoVj7!\t?{C Aα<մ#{}N_}=/mMog]Pͯh/`%?7}Mjo7Sw2ma10f@ ˭$Dg4:,T )Pp J~tFb %P @hd\+\Mj_@ +=Mr;MD͑B1P52o4u^FIeQ1GWQNV_#E~5RҷWU}45uHFGa{TӞZI\dKG#_qVEd_$u^ŅD |MVg 9LwQYµ&vgkAHl~]+*x ]~^݇滭'`{({iZ29I7ɪD&~|ʿ:. c CPR @Ѹe 42.9 фj1aBy<*EE]"MdeHz1 I0g6bTp.P|A!hc8 U"4hd.!hb,|J( *|s[hcĮ @hl MEB7{> /ProcSet 2 0 R >> /Contents 396 0 R >> endobj 399 0 obj << /Length 400 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1>+kbn:1' #hdzB̳* #;L2H +&bϒT'J@" '7βn$[&So+Δґ!KЭ:T3rLBn55CAU%!U-4U4] WRS!l\Xa׶MO`aX֭LVA4EM`Vf2w]M]fH$r9tpR\z1y"\.VsT A:P Rq(` t5TcuZE9ʬ*nOi7؍kՅη:Loז??$]TOZ "|!. Չ dJʲI𪸬lr7F!Dcen4`zFHJS%iJ g) e6JxRi2`QQ wj"TS. #AVUݫ> fl*aWk[5to,_9^#W={-^sL0`'U,oECth8BP3YnGpX>ڥ #ɲyRBn :DңTz3J+|A Cb g+Z;KP-5%jT ڪs3 xKw.` (sVmYMB)k;Hk6(gyqn{s]9hmҷ_Xzwj 9,NOVDPja K&FpEONaɹ`2R:arlHSvH'**.6A:IzQM$(5ܤj]sΕu{ՙޫrI;o7jtqڼ,Y+`Y_yny+ػeiZDxؑdQ(haD46o6CQy+]L0[Rem6Ir-]v3h7K`۷=i;쪏>ѭ[zdgT;o @4!Dof̀Ҩަdk7;;2:݂ayh#&6d]`( qI8eo0fPj$l,om A WQXmޑbc c G Qs ~c S Td.Q0 <P6;P>H#j} /vQ*-9PX\ư0d0@c0nsʌ,˯К©E\K%hQ\! # %rR9NR9RCPR$ᱽ"k,þ^Gw 0nY1#'E>҄X2Pex!6"(2Oc0LA d6(gِ^lN {mƙC00J#/`bɜ&ZU [e(3"M'2ei3_,O4 &!K!&H]ŮwXmgZv5p'P!e5@&(slYEq8.30sg8etCW:3\t3$!)ӱ/]6Sưi9L4sw&p38'>q0+ 6!̩.DjrMcPwAE*^Ve $eGČU0i2{HN]d*UYV[aWS^U|UAGj& YrOkWukUi߂m5[[g1k,$9$,(@^bt=D DGԑCK@Q+/yIvD HF :[t<0OgtGʃt )(` w ׮ z -s>޼Q2_M$lFui~/yNe q2r >W??4{ }+p^U̦݄@lz`Q $lP)L ;Ht!2"Ņ뿅ZV_ˆGߙI>98IIs3t$_ 4*[>)q䐉&A.Ş@V?0 B\8FQ5@0f.FB C!P7qX c8 bA l:M1L  Q `蠸( Cen\ʔPP fqZgW')#(Z3w e:NF) "Ø?G($)cm^ pXndg ZD5#Av/S-L!k7:f 9o lrӯr?RK\~qacA0@ 3:3D@KRպlC (@kAG ;჆#?KCl5ჱE,$Ȭu2 }'2"AfF,rnJ$%[!B|Md!rғ-0@x.jv&hϋF) !x "h#Ćzҽ:#t`6oIV2p+`@ ʈ'b$p@# ŊBMyQX⥤! )!l>>U5Z ȅlTɝ0#9 #:!p6J\!kh]34^V%#Oabj7߬K%bKr#`ӣ1 4̮xn^gh3W*z" (ΐiY֮(Fk/HBcg:d6:M\T Y3Aneimt7P:rGmhaȶ\d[& o0D|o|tsQq& -ZQW(ub'd/נ:072p挣f; Jaʶѫd19+R|k}ev] T,8ۜ)< 3 Coa ;.@򸆪Kq. A [m93f[d"j]BӰHnж8Jࡃ_,~ROq C( "IpsBE* Rrqmc O#D*C ktEKgqOKgG#mr2G H\yf23|$#i#E endstream endobj 400 0 obj 6563 endobj 398 0 obj << /Type /Page /Parent 391 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 399 0 R >> endobj 402 0 obj << /Length 403 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1> /ProcSet 2 0 R >> /Contents 402 0 R >> endobj 405 0 obj << /Length 406 0 R /Filter /LZWDecode >> stream Ѩp.q2 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-"DHb1p Bȹ⃮" b.3fD z$o^JQrt)@)">5! oB PAR <*-Оbx!T$+GGCVP:*RNBpa &r9t@[|֕Rm=X> 3\1V 2LECyI/p1A=A"2l椓EJA1q<c XsFV^1+ (!"R".1EdD_ D#Q15&M eL1:3ş(3r9H-Kf@˸%FdKl[!3\ДeKx6&0 =@ɣx *g@VF~[ Lmpae]˨)FS BPT05XHC{:^X?)dTEm~%8/̜:mFR)L^tZUG-iGx $,"CU"]*T)* SFN)Ԛ43Ũh'Ц&3f$٨r'bg\%Ъ(YZe$o8)&3N'S|ϰ@ڡ 9" ZΙJhyLVEtu @si֤Ҿؿ5.bS:D+Ti rHt;g8d-l^7=t 4vJzv U5+zI{,)z-딖9CakmV*,DK% ҤֹN몯V#Y\WGhxf-5? J`$&+b(R Q^ɧpx\uNL>#XgXM. ү+㜳l9BLa|]sQ"t<  endstream endobj 406 0 obj 1792 endobj 404 0 obj << /Type /Page /Parent 391 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 405 0 R >> endobj 408 0 obj << /Length 409 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1=Tek* Hc(: #x|3 #-:  |xa" h0 6_ :C>\'';.2OɈ Ã!?v@4~"99^}ߣp{<*MJ%9#p< 7Ch~_12 @1ﳍ 1<OW/:ճ42.չv}m2h:CH:6tD<8_SqmFT9o :}98^ v;V}n2Y0Z(#Ztu+Uy,הʄ\5+5 JD`և \y[HJa*vTj&&d l]:ϟ d"- A6:רp5HI|ƍ&S OBW*hP<C(@LG@)"d UŢC 7Bʓ!5s+ҴS5>سVi4匲R)/,"Q03A"rn '4RS5\)W$?H`b"uDI/JkFE2vqxi{!՜&M@r &hne͹>Xkba=db"P APeyᎄ鷷 _2GY 5=舓=8]}3(M~tYf r(8ZnLYҔtu}ԕ*'*PG 97= -P;BF\,7:Ϊ^o9'0C9p1%R1@=]8?btZgA6@癠gq̀Ch XEm¦Hk.&&C9鳰fhȏ+ 4Zb 6jg IG3>ja6[3JQׅ}/Iڛ R15ls 0&6CӉ1C[{Ny鷶nKW~ 4͗b'un׻:lm>|B7i.Us`M鈞7[xt1xɤd9#S`JUʥ5\eM$F&!%x8rRgbPA|032(Ҵ ٔ :[{UU[^UEט;jn3(S4*jtA82&\s RrHV"NuC)黴v;TɗtI,A{1'Q䵜FFzYD!xh 8̺%4ԇ]~b!XDÕԔj. > Hnl2Ycڳgۺ&f1UJ*D:(j.8$'aE ܅0BZ`: Kd k`s7憜J~ ԮA`ua,m!=0ȡsuw0ffmU7ky %Χφsgcľu R͉u/򪓽P( fUɢBd6*^JO ު  > /Contents 408 0 R >> endobj 410 0 obj << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R /F11 55 0 R >> endobj 413 0 obj << /Length 414 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1I*N bo!o"$G ^q Hr(E?p9|o;x;`#wv솒2M^[m XX#|(SU!C@ B!7(щf'@ $?)"`:_EBKgEBׁbC{Ц?49  y@nwbI"C)j~eD7w"x4/ !H/i,r!;?3'Z6*R9!ɇ(T0B >J5/ZQ@ [GcVmriqŶ%F?=!{Ym R` sxo!tD4cHnxHiTT tƾ:Hf('ŬC`bΎͭj±!?Nc)0[ͽ}:GmBe9h_'A@6+`yfKU;pTc 9&U2$h Y$71 :[ V$eKPx΁@D uY04: a4wTiY)`ʙWa yl~ݏhunz\C+8 LIf81 m v ;qDqM,@|tT}9dQ}BzcII,/ !D\P: nO>P 7'&:b] ysƘXjbON/ Xa)dL)vt4R[V aEV@ c2`@bL8> endstream endobj 414 0 obj 2412 endobj 411 0 obj << /Type /Page /Parent 412 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 413 0 R >> endobj 416 0 obj << /Length 417 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1p&n;(R$1[A1(a wIMsqe6T⯂[c%4nPPK8[tqQyݻV$:b}P9`CrtBP !8#QI"^أ"d_BE3ה]'!{M/LI $"2$  Wln=1ޘ؂zoEBs{q[bGmE4 %`fB`އAsoTX\bl]\6(28@ʉ#L摰5Dh u = 34: 9(6|<7F-+ΑQhUk>M>k=5NvКgV\I*2$EG#-tD;?N{ Tk͍Gu"]nr &acQ0tW5j"$ߞr>:I7ӄ;{o3^yڜo~4*[Y#o3'bX.1C^}7źX:lpQ2M+<>8k b5`=ā${7="ʎ!] aѸΪg[ai=3f΋9M<}mth75(KgkjuEqKG+|Bm@X "l *s248À$5yݝC=fjg]t뾻X^wNͿX^!tx$wu;GvM=[ Q:!O9O!zSe@ ]"X,94~ GU4_o'~/H`noRCBapoNp%.4 2@./l~"8hH m@{lMEp\oLN{P;ϑixߐ*tr0VNuN r p? й-JK`,f(,%m&p$p4Ba.~7"` @(@  "KPPbhe t W%(ʠ@  tM R@̜:& %(9)xzGzN|޵PGHRE9HjJȜ*x\.^dx*xi^ZNWIZެXH!' ZK_1ϥZ NfLwNSVKW/I &hU ƄC܃ I"@ ''Q%9jLG TQΔѲ qHzg. V] a\"8DZ(N1E6B 'Jg~m endstream endobj 417 0 obj 2895 endobj 415 0 obj << /Type /Page /Parent 412 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 416 0 R >> endobj 419 0 obj << /Length 420 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1_^ة 8 4/DTcK r} 8A20bA ~Aё#w 5,CR<=&?Ob%'yr6@!ޘZstq`ջG709 (A:d8Xvc2![rBW"20@o~z+$03w2Hn[vL{8:bEf'7[CK7_ko8މ})}fܐZ]E&>PARmK!V! m;<<.ۿ'[y6,ė ?+߇I(-2HR3Wp^BY4A\ wҨˌD0XSy Gз3s*b/fcskNδ^a\9?*rv$r`/&M@!Lc 㷮HZk- {2YMpvfx hW-m}C2{Lm7O: n-GnyCCyX !,ͭiۅ/hmbz{!agMZr#pm|QdW4~3m[y*[7o F{hޙ|gt݉c hcW(tWzשI4%ds6rS~k’xf< [S7s#:+[ H:<]>+>ꅣ}?H7\^dqnPCHp]ܻXnuc穃` )٧Y4kO=rj|]Rۥ?%@ݝ-_>W~OdnD"/F,uʖp Bod"&l&/X{$JB'V1LY@ $vDDXOO&҉> >bҲ),K,qF Np(qFJhʭq/(ޭp!%UqOHθg;CDi#rFXYvհzmc1L=ʽ̂8ʱx*=PV1!P#bW 1бlh<.l!B!!n=R "0"kGfln,n  L2 Ѿj] {Rt/͈$ *W(2)(N"`pʏ;27h+g"qN+({-2›:a4."DR)&1xu+ .s2 s>T Fii>2 ?T ҽkIB#SAB K2r@ BSuQyC0דdBH4I7@PT*T[5t FsF`HB*<=nb$G,?DV@c#BBPL&(@fkKSdHgM.~7"r.s#t7| ŜsET 9f'@ 47'@P*HJ0DD㘃#&c  %`~z @VVrzWPF& N% @ nRžKeƌBc(C`c%ce`(b @M e\c@ Sd%vFHff33Cd8"qU%WO(Q%,QeV|XZLwcQ Q#P" `ج\U@Yv ]Lb ŀdƁR?cf\Ν9ծO K^@@ b*&pgFxR`g "L"V> /ProcSet 2 0 R >> /Contents 419 0 R >> endobj 422 0 obj << /Length 423 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1ls_XT|9γ@Ё6*&#;.d?ThYt* Hc(8͖m #4kC 4CAx\%m9N8%1c0#<*JA`زV 73Ow8_4 [5[S 70p9): "VSeeQz<.uu@<: #HkUw^]ꈎq}_  ( (93 dFPpɗ9kN(8HBXyTo5œ \')"I,ǩ8ysS)ʔT3$)5H%'X4 *S\L9~jMi6; vSM9Ct؜M8&T'sSC;%.}O#?gȡrKA J02R3ځP)Pn<A J LMI $кM< )bS=#~:AXulS jݥT 31x;t<'B2$J\)o޷[f>κLrҐӪH Nʡz\lU@vXUS Ysu`aRieVcp2 5P2Z!"ݜy69D "K#<ҜaHPd4l28HudxP ˍm9J|hQ:tA0aV Kk}.lSH ZVO=VDK}@;5z¸8P]Iu}w #IMjPp1( c +M9|WU<ƸԊ9LƨU,i? KBӟ΢T~WG,FjT|eax|Bs]d+?g&IЙ{8gP,] =Yy͡(ufG: -"H2r"S^:\6%w-ȗv/GkU L rK;wך(ѹ\*%{~:d6遨q): U2m݁6,Z D6m:}i loy I|TZp$0+<i$%rC$ʜr#fnT6o9}!|I _[st]Ҳ(xAMSJ> hxZZ^v42k݌Н?y3=*._zjjIp{ys@"uC=K\!ADN̻ꪛ).]Y):kzׯ*!!Ԁ`@1 iڽ[L^x A/,C's.u;wgs>*qә_@̀~jhKa'ok`ho@nX.\->.r{碜pL n*/ޫ)cO FNִPZN.4kp(pvpNc0n,Аlƫ\HGE-H"% ` RXiN9fG|o^ NW&6঻b$E eH$$iq j[0Ș-ZD*0;,pWo.QAqEOpdNQ*P `E 0 8[J`Qqq q.50=ѨN2ܯ2pP Ŕ`Qoqi2ѝ q!RpFHDl*@m# @ Nhm/ - $8"rk3BKT@Q@Tu@Rt ~2TBQC2l8,EY4O ZȣnZeMGG/)4{]4i[ NI[Jp]37]sBKt_14 长L5Ԟs`SbNܱ5P0G?!}8J5dt8e9a4e dv/ @#~(Ie[ &*"@Ҷ &m]R)To ~Rz3}rTIr}`6 AXMX?X05YbZ1Jn-TT[]oqW7E H7vEnqV^u4p1o*9_niew!s19pVI\)9o;aNpT2!:M,ܮ 5*q7svc%׀DZόIy2PS)GWuydf/f?jn$j9 iQ & j$G}k2k1T G},BJ3^+mIYT7no귊tED6XE4N5FL8;//)uuʢmr1݆ 1ŸwtUw>48 xp' xrEc.L.%"yM8LC̍{JWy7LۍTō6Sx89w "S*vx%1\}v}; ~ jOcO[Ua(e(ֻXxX_/iX)m/Yr'%_ 8$I[Ϙp;-F8gYV 7J\ԉ80mӝ!sٜTtzEv0?ǘr׸ 2Xnz9Ϣ!ywGZ19z;{ZCQԥ9G|6 &@@>?~FjUG~XYdkPQ}ΙT `)ٔJՆ>*s+9w9}z'DZHYHr tMsԧX[l䠹2wyr'E_ˬߏZq9zɤŃ'bFY 69<E,-[YW;_f2gzGQQo?|F%TBX;FMW1&5jUO3ukwOVJ# xT*yoR11nZw?598CMoyb7);嗿5ݿ/WB9^ ZS9›tevWÛ]yC{[M Ӥ^ɔ *_\sƸHQCljO? ֟ xIӏJʌ24<ܣNNʜxi7vʌO˜Io8ȑ?̌X,:Uˬn)dܱԞ] ȸʓ;SѼ̐<\,Mve9 9&tp 2qźB '+=fO#qS} s|eH (>Z?> /ProcSet 2 0 R >> /Contents 422 0 R >> endobj 425 0 obj << /Length 426 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1v"YB<2Yn@Z@F &A@e7xhP2Yd"Zpr0 l ࢠ) "⠼3wul>/PGFA G-ܩU3@CrY *od 0T `!ɞʢ>7 P$5nd5MUd^+R&Buc&m4#×|F)bL!iƩ唵?GWcl UmG}b+q^wXψblfYud3X9M!/bV͙>-\wl]Nٍ. 7Ynnd˛dQk$Q'lsӾ<N; / nQvdYZA"%b_S5nz9j(^MD7AM-"$REP`SN B գ`DO]ëɌ]W/\"w*/ڌFVI82Wlǔ92k $m!̑Jvm d _2=qeT2[;~{c@Y]RzL޳5 CџG#h8i,u~Y^BW{m T!ַ+Nkys+VI4\9a_/FR#@s_}рkm ۛ?ln c~ t3&^<^U JXN~ OC%5TlWP/ḣhN >r%u}5=GgI )H^ݎk3ի^ `v>{{qKwݲ;ХKn hgPo1綜gwE}o+z\ 3I C2xa!CtM.d_|b>v)$q"ApdlAz@' CwW-K*'G3)/em>"&-dnHϭ<Ʊ0쎦o8CL&g$>mp>(Oo'p*.NFVopP8 "dh# JPR(r(o#p5!8!$-~2Pqh@˞bh.F'`DO 0Q (TTōq:lh|Ry$ǯT]Ryh}c*% tzj5( RqqcPа//P|90PP\zpLC1A n%2?.SՐs1}%/%|Kpo33 QSS. ^(sv2,s 8 ! Ps%MO89qs"- :x-&"M&YRE*a -RҜbD(t+*TC lqH/pQT pW C挶b&BF~2 qqt.͍'g4Ds 1IC2pD񮿑9T;E͵e3|FSLGsE1EG5Oe5O$IL -EpT71;Sz~)lc:;Et23L:TLЉ<4N_N; %L(<L?w@nk,ϐD%I_$ ҔLbD/rNXzQE@ VOEOت0,/Wp2dHh58;lYtĦ L+<+p}CkjK —YpZE0CբF9\4f4v]!GH3?]2^t4\Ԝ[Mԯ5oe_ԸO"%0RvT!\b+ڔWbcV8=N/9d6.v62E6ObTek1e3;Pd6!e 6uZp^ʥUfl32jEDMNpV6;aCb[.ϐ\prI`ѱGCsU2!sS4Eut+_us3IprOvWJrYwtV{Pב 3yiMmW$gb]yqzO (7yCgFJe5ti"(| <]'u*# JK J'kl NSuI+MmuT5YV 2V XXcXiY50enV.fqEDɃ[0urJ1k#8H1"Wk#׵euoUF8w1u6 c6u| ɕxxLdupVԺ]8}8"јOl }xdVq$Y-fT`f}#>}*'t#A~ ` @` (DW>]SnlU?!+ x%A@4wI@P l wm4r%8>\CYXPGoC)ĺէ0jxsŇ_z964\ι2yؙGߠ9HXa3ԃK_v 7׷Ǎxqax!F]-IcY2:SW首;j!|ZuZcfy1c3j#XrI"ەj^$ >d >ϩ o"kMvĠk*Y,Iڪ51r)){X@rD VK ([ yͤ8e(1Jn;)rٞx}7h5ϡPvC539G;e#_+:&Œ+Ѵ7 WͳxU`觡v Y˥OJM&xk&V'yEMk'z;4+8ٻNf̶S[I۪* 4%EGKѼS}{:M6x!&k<&\{fk[kS%v ìs|X d " !Fk~("tU6;ʲUV&k.Vpo mʚ'}g*,Po}F$#@T۔ h w<z3{e+h*1 O8<>(FOY.J(ae`$bE}XyXȑthǻ3`v(31.?__Ĝfߟ_$m3d*+1kiu:5&Щ_VP-3 I#Uߨ}uU3<Bn=VXEhBt"d6NCI "L&sqi9DCP4` FBA1@("Nf37`hf1 @0F#Y4P\ )``83Xl.$R3FC1p;"*H* gG 1\6Z3(fXegWG!YFy fĽ od. 0o<ۻBuC d#>B9+ Qj}K!?oX]&MT (JOTܞJ/-D,5Bp`]Lr3˳[l>NsOLHM 'P宅ч+2~,r>.Lpr@P( (07h3pꧫ$(HTR)ꦪj/j4c]ȅPLe+$WV}=U2R:C1M2#d3Cxڱ6`7\)56.&6% [ gE5MWVÅT4Ճ1 #4ccʪ4-!OMC-#q,Ql|8u9OdcwzQ>ǥz%O7?oM## u,O5m^BiJe+:e ,k1cXRZB"܂2B ?V\`S/e4^Oa;(10Y a@k=YF֤`VQDsmCMo*;Zz*&⋨]κcQP Di4[xm< ,B̂1 2d+2n]"4QuHHԛ6Gx|c@}&77|풩m-%KXMR hꛥq-F]C )neUfeovN0䩕s_Z]ZߓMFEUC0fOXCHgjܜVJP, 2-b}A6K9AUV[EلAU6`hCøtg-!hsdSF,4z9 n05|-=;T̗u@9‹Hx/t8*Dp.UQ3dM[)MTcXkrLM&jު=J''\4t"z,'Qil HGƗMR17&.YZvҝ?oLȷfFk6ONZE$+hު`vZz孎O.Vm$oc\ -$ =Bv#br9:N4[fI9wDYW$. ̫&ZT{nb_HW1"<6 endstream endobj 426 0 obj 7077 endobj 424 0 obj << /Type /Page /Parent 412 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 425 0 R >> endobj 428 0 obj << /Length 429 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1Jb?A@1c0H78‹H6FCNp@.`  K<FX9Q )hdչObBa@$@*‰P+<4o, H9p:2PJnFbGCQT"!ST%.\uLFujWbUKaY%XTY zTuZR0ESU!mNS_^aj\VrSv\5Y_"-_l\v*l\v܉HdxFtݸTm$i~w 6+ޘ6= xZeoAhfsumC>2)$dh"zCbۑhxo8McykrgǺI>ôZ擉l{vVA>ȼ+Q[M5cʌj$oj'mJ.# 4ɲx64p.=A H: B!vlBl0LS$!MT6;sBS;QZu&:SA$+Tv]mOhW֧]rWU|Vn6SiH8S@S뎁-)nxe j(?`dkH`$a@S 1 ȇBxD<-pwI c7 aD\ Ĉ.ԈD'*P`228xILbpc C4:֔4d%J3 #t̪;LmZ:?AD$mY1\Y$J>X kA{'jWqE3ԉKAڣ, m kJf05fLb`fAǛ$cFr5+fdRcȦn흸7Wlo 1igr4 &=|#IarJt:?5| &/(F5 "u~7T@@Ca PCh 2PQ|YC@4t6$DB2?3Pm І亄%jEP%'+r<@i "H-nJңK+YB""O<@1thb=vve"~"Y1 7ړru"@jD9Bd9HYQKXz<]*h hW6Hn#N[@K-¨0mmf.[@xst$Bu#A@okܶD$û abUN tV^ }tYDK1cE>*I^5z֦bw*CkXrF #ĸ5Ԕ6IMN3LB\Xb:0XHfh-b%aӄi3\ t+ y<^B)o;!b ΰan~0C yL`# VczoXrb& %BL(HQ2Z93t QZ _ Ek &RycYpE E+ *@HB ĺ'R^!T=kD٨d7gӜ B}[f^"RKgKek`s>_&YlqS~ F#QCrG5kh)/+&~k?FšSAB2P{) 'm2_Hdf$^zy 4?"KM}}?73M yw :~ߙ{(*.׳7"@?:!/v谰 0l_5p)BoȚ3(lo^P48\qBJ JBD`tk~J$-6DOL@pd |a@Pv4.oO০EP$T|mv5&| ʀo o"S{)(HZvYfhGcCa`q ok/r]Wph ~^WbD6?qF51KϵB W fMiH) 0BqQOxQ3Fl$oa)]pKonf.}"(j/: f# x'xy{N'PF0L@ -vׇ'Xbf̲ѧ E .}'251ľO @` u*z m>HZqucK`b_Iad=@hM"N # (D颸bJD'@z-Ƨ@\r D22$c K&'@ '+%C$,ҿ 6} o'(<?1f蘏,s$[i% heo>\3N[WF_;O#6'(~KM5E/6v]"Psq8Ss:p\433ӆÐ$ӦC3/5\$$qtS23$Fb].S >p5a>3;S@@?> ;?4%wA >T.Xq™:⦺DTJ@ J .$mp/$)( rOe+P S0P 0)( ,HHXpLdSm@` YE~<(XĤ!g;3tʅf4(]L43NЋs]/>W6$Ou7 (s3A4:_S9kAMEQ3C77U"eS)TSOZj!BvfYQ vUcubdݱ.U(XNs[ aemIYt]VnT6VZu;iqTl[I oud]@fTкݗ*NTRu]a^ )$LwJS c24TnP-3v Ǽ(/ĭK '@ *ȯ<䶬0ˈ],IV_\N3W6lVzg|fNWes^—jg5}:cqfhGwh~wlO?P/}SXkST4)UPl3ln9mL)q5minՅ'po8IVh["jvӓq5IUշMCë#$cwKH !s"%0]FWAFtbK!#t{T~HXw' p"@ԙ]_WxxՊy z5z{Fԥ4e؇4τYxY/?ygVg)icEsakx5YeDY-VkH]G8o3k8=UZ% ۈL%]YY1VٛYI{`TYW qD{?#5B+O6C%t Ҁ # @ *u)G\N9s(d^$tDŋy$u8EXC2P$H Ҵx; `ZJ)aRv%b4ShyqC{.!YG|6U}T}yzVVy6FghskکsRzik:ٺQ; WlzZqXW/w涺 vt7zt$b DB8ԛ x2 Woa{vz\z`R/bicm>q{e Z}ds7 eS/}wS[VT[fQ{ӪGӔ ۫SӬS땚2j'\7Z:<6ɼU3)G|\( 5f=YG{O ;2zwAM7dg՚Z5:539~${Y?\EVOP)7h ];lݝ{jU|U<U'\VYfFo2C]UM?}׎O,&ztWd`9=n}}Wz~Q"\kQ϶`Xm3PCPBFl^^[d>NoX04>{~Jl]˕龧C>h" endstream endobj 429 0 obj 5167 endobj 427 0 obj << /Type /Page /Parent 412 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 428 0 R >> endobj 432 0 obj << /Length 433 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1[)SZ"9DҥHB3T/ƠC9Fxxdn-<(GPCs$"H!xHPIV&0ndØ^l64ld`OUsh1@"`fڥj FVhZcxS$ i@KUkuF$+CEUKנk`U$Ѣ`` lL;dQL[;I 3,d˛DIդ2LG(Vf`m#lѶ[Lw6^3Oh8/e2v\Էs֐ӷMd.򆸃Som/b&kרT+k}6^ȁ0El+k^pnSQPoooT/ܺsI6UY_COwrޤ7m[pર́8Py~a170@ri]S0fsZ~ 6R[3J"QD]8]3W@KFz&_h@RmN$ImOT"!UEԸv .};zaӮٴz^*]\}[A;4>Z=|6RGl?ٷ'a,3Ǥ߽uz5 >Yv{MPcz^ [fB[O"!5poa)ZD#%Yc܈+p75/l%f@2$Q)1'K4k91+=Iu\biҁ(*{5MI!Կҹ) ݒ$Sd/5R47kU$M*\ا5P92\%rsB~K+;F"̵B<%s$jE5>R YN_@/|TWHBк)!+oF)DjZ7AbEc>O044uF]4]GF3gHiE ItkJ4N<@ Kc#0Gc/21p@GX#BQ{&F rf@k6NB$> F (ɑ_-iC<6C;xpGU% Hnt.Od@Ϝŝ=UT1 k @I `]S.u4;#LE?ULOLqMMQTZbAZyZNf)2/OZHP^u(UQ* .lGSD6$:$nsLTDL5R?j\ aVFVK0Uxű^9XՐ 5YD*ac'TCA\vmTpCHT\U\QOF`P $l0QkP_v4ST6PJK$&hgmƢ N@ <#,-D@Ǝ_sj*hOnJ0'@R#   Q@ 1UN*10<HcPH# 8@k82LiJwt n3Чq`&q#P q(XLj Ê΄ (FHd߃u_5<9X<,ŸD #^76t@U WJ8G+B[,2 }noI~WO H,%)< lF'P)[B<$(k*+ =!_`/4SStb (T瑤øuIo V ;GМk Z s?٧+kA|yf:-\ *$ 5{;³[E|A@ltZL ju\o cet]܄QٜpE‚ɬGB[-txɬ ~Bl".|[.n+ӹ&qq13 u;BP:Y  鼜sgdzԓWǂ[e˻CrdBiZ%ͬJhVU ǝ|󈻆J]кWqbύͺ&qִs:ѝ<ԓܤҾ; QujŌy ^PMz[2!eA׾ ^؎߾Kٮ> c>붟 齱 E^%:I^uQ|{_.-PLE>R5,"/9!NqZe\"ﺂ<*$BEWѨqjdd -F?M 篩D iGgLZ]Z>?bT=oä}ܔ.e1d4 Fc1p$3X8 ƅÈR-@pT0f4 cHd5dhkItg@ͥCgOdCA\0ХU:V 2/<FwXcݶ&1;]oCQI,.O8 d6/7O7x csnMezLkK38c5ֻnAIP9Sc@ *jۢT1dž}d M3u`e8NGCit]QZ:B#!;a *( ;ja@!p*`.0'J p\qZ(fbp& fQ3Cn\zgF+,P' @*c(B/ q61c8&6j;53|+ OTJ2,B+ԃE7ԋ;6*I1:GtFUBY))='3X5e\X 2TR U@kqӴ-f24c$Sb :vی\?tԋ$#r$--G)^״8A_.}(%«8]ޖ` {aA8C7r lpC) :#g@& ЃŠAfEDӴSŸyHaDm!tu)yD!FD#:dI@j);+C`P9y2B3]?3 әK?5*,vYl5E$ԟ|Ȭjjv_GoR;lhrI%7}*<U"y m_`0 mee B7y8`Py%Arٜ~&!O*pe< "&@4+.iKƶ$zI$",ٷzDxh < .h-3t1ȶ1 !hs+ endstream endobj 433 0 obj 6531 endobj 430 0 obj << /Type /Page /Parent 431 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 432 0 R >> endobj 435 0 obj << /Length 436 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1J$ak!©Ј2H c(4#`@'h)8-X6 @` bE5K<$[&AD0\Š  {< ' O!N Z!@Ӝ' @*c(a(o -T{YUT]T .$< lw7 +z *&8n`pB`K5Pmw80Ga(q_Rp-8*T,L)U ڡW ٌCC*kV8[6hB%Qy],p[)勳(g1\z }= "r-PT 04Zcl ܾaõHq5M܌A% +P[6.EWroxpPu@M% U0JFrCWWQj;!aZRvB^$X Y+^J;r؎(Zvi4׫-ݱx4mp6%0$甠&%~:fRr.9s&wk z4$/8=Ѿ<^]F?>!y_^nw ߐgy$# kpdPc !'>UQ?(~Aj9!|R봕Z}߆|6%"ݤ.#8t: "l%憰_ b/ޗ9:c<@ŪApd e0Fl௾o҇bʵO00gh1jkЌ.l[.|>b=0GX;MDn5"ObX#Bd &K6@dz$H#6NF1@ Z'}@ܙ` @ E耊 ;&9DvG%4 CC X` "8{<,; @t1u`G{)QZKNh'@ `@IX@J@ ğn{]ZcF# Q /.I$V@_0 XxpkUǍQQU" +is$r~@i ĜGq^SJO~S DEf;h1EJ kt `%ю  I@ 2W@ V`*,qf~DشJT q&<{*N@ r O)D )!Fbr#y !C#'2z% CC @3R>G 21K2LNe2T)PlE22p1% 9 O1"F933qO5E"dDE# : -##MME- SW }(q4@8` @@..)3 N$4s501*o+$SKLÜ 3Q:T2MsXE@2Qn2U0E@ E=t1-ABt :hy~SF ,qABD,HBsPD 9%( Q@ @qT1nQzT@T6B{CR)0BWmgSC-fF1:0pւ>P5y Fz" #QjG2$^!qSQ(RMTPB";UMFWqOTwSh VpPXl!CW`o@fLJg)ՑQQW bg VUtWTE2VIt\s]eTcޖukV"M]φ5x5_uQ_J^ ȩ`6_\6]XX5(SH4=wrBCI RO.K$xPD$!NiOkTK4ZORSvc<,b5̄~jWqGRZUY&SkUAaU^,juFvc%avVlnul(i_VjVv'U)jYho5lWkZm.V1Or5%6nN .e5t*F]vt 7\N8_Oun7 ku.OZ7u$^7AsIYx.T͖,i{+cd2R Y$=M@T8$bifjR JVvR-/Ge8 *)̵QR ȵ\B E5V+UMt-8*B.v,MjX8͈g/H%wyx,č7rRezqNw&XśJja#C]l,{bwBK648YUcw`wz``'Xk yruzndP)1Z dL.h"?#MJ.nLvXB5D/~3 'mM+7E.NhՁt/y!3zxK-m9i抚"J;[k&(d/ZSo:/9bؕ_;;yg{Z {;mZ+wHe UlFձca5wM1{K  ()Co9|ڍd, 3LG;ڢb:s+׫$}V_@k Qwg<+_&VuuRzϗČ<٫JAJS]"WM{ǡUǚM8k܉Ĭʡɭ f/ݞ'{>/͚m>pey @qZ^zfM\'sDb+e!vVūXB=^Y]yN:Cd~bmRS:իΠzٜ[뙳eXi~ȼ}LG\V|>-/_ǼclE=^O x>=^,|1CDk^9 _3]^w12D$[稽)`ԝMڽeVL:ع>=lr]vMEDy87 ]k*^=[oܮ"9 FBk S@fapq\6= D"QA 2HX"C11Ldg 'R0\9Oq mEϤ2 68ʧbc(*Ѩ-_R5]Zt$ 4ixYG,leI(+MF* LF*C!Xef*rr㚎7e0=vgMjS|sY{LJ|q;|fkOe!b8-ctcރP>)LXye2 d@$Ø0(R @h`"AXb=T=@$@ p= @adRSA@ = >rM aA䈸;ÿ ID+….ЫȻ/A4s λ,D*򚫁rЯMJ j.S$G2->L k ̶MSG34&$b%6r7;&Ts5 xR7*!2#{Uu%_Z.u|/eR-e$ʫ֨a^%.DFMv7U &\;P\}Z^}޷Ekw9"麲33̃ A>ob9Ȁ2#HAEOL*CQtapFaiGͫ5q<rB/(M)Œ@+0{2 x "K`m)Khm1l)t26-Om{Z[hϔ/ HB|yeKrLt5: jt57/TS6/=`?Tr7]r7r@ݼeR5[/+۰{W% *مm|{X3 >zXi;&(Kd; Ŏ<}c!?0Csmu9HP{4Y(ьA6vY5"4DzC[AM%6[3E=,!qKA 5(h1`&poZ)bܞR5`Ǩ'.S\`JN㹓 ڐsYJF踶šQOttQx "n<Ɨ5\(߬[,+xLvID"F5$W<|~Q3i^WFZ <&}%V3>Ոe)v#(BFlS6CGAd ~@( Hl(U&44Yea -gѠ7љZJV}~"\6(1a1r㷣Xlp^V=<+n1> /ProcSet 2 0 R >> /Contents 435 0 R >> endobj 438 0 obj << /Length 439 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1&J ft<ǡ)D a19 72R)Tfho1J2 $HMm* Hc(: #xJ3 #-&:  :1D4H2<21C_3¡@/$#E62 s|X 5 M!0TŪ6\UTAs< ZFQ˕0t4Jr+#:!1Je{K Bb@䝂P _P#`0_P5Jodf5=/d1#ƈAnD~ ˁh7 P4! x*A* BH*+`8ҠJKQ"EVp6չW0x3q-ƫRx*4Zt'B|cF_0P+8g:ma= |1WPm]ĮjY}osok<1N7N&7ԗ8IyHiB^WD C:1..0` X4hd sTGL0v(Dy<h$ LFBRu L$t;%KW $Pd ٱ/40ZC@0"C3  4J0hJP;QJEpӘ4RJA`pCȾ^LjM=Xeaf r0yA9P~L.DA|7QvFD=$2v;Eʑ/CasD<2B+M nxK3H,)K@A/҂4J{&4.?G2 ʗ2"EMy/ RHJ$i6!RAN = p B[yzyC]AN48ɀ0&N^S'Oc(o?xY(,MRF ЀUO\r][k&sXP;mAN/G:@iԃs8d%d< H- bY#j)j= j(SkO S ZvXj5S6X3#\Ib 2`d!/DU}1åb-r~7J{ |(Yn&Msx/nٸSnL+LWFny]{s4 v\vW&.H;'䩑I"Pih)  'Pڱ ޶àsguRSAΣz2EI)'f0jK鋮Ҍ8쯍.Y1INRq @=@^N5ocˁFyvKM2?k/^ ^Un4쐅df;G3+``o AO))p *2ynw;_m첎:hfכbJ r&^2GBLb[#8t~h\s84 j QbsՑjhu&dT\ggOtT:=V}Cll6JE+&;^N)q>ܜ;zEnxӋl AJ޶smd} 7$ƓP3 &9; D /kWU}r4lQ?H`0E&56)2D5)1iSJLyHG]mOE﮳ Ёڿ7@BP<5\jHo{Ѝq:}F4=!D4ٗS ":s uUu%vG z!"M ޝAXR7d$DC28Г/3[t -S d26%Rrj"f2n>k`.D.PïtJ`qTgdl*vgD@8(iFzJ<ZjK}ko|^A0 61.4ӍZh(F̒϶݈!1Q+ ! h|0mؙt$|+ƴKuse15htOզJ ϣ νJ;N]̹mԍlѱ˩Qܜ1 1 !k*O+L " aǮj` 6J1 C @B&Q jd2Ǥ%К ^ ]p T(BzDLLz{ 9X抯јb)J/ы3)䎭:?*EvL m;,/ƲԱi1m!rܤrس.+JK /-r<ߐ{, =M*2˿ Q33UEIY4)c)35+K3Y3!S76*S!>KF_#@TE S5#.4N:Ž@=RPNED0vR['&r/u H%gnM(b  2V2֔6 Q{2 /+3se=43,4J{2B "gg1U5.av42DQ K@ot50E6]AL51g)#3mK4qt5 -$7J3g5J4m!o7-7$5*&9$ ŲFD0r*_D3=rgq[ BglPG\}KH ZDP+hbc \ 4`{xjh9QUUX5_V5gO lJ4%2> hbX/PgT7Zh \Xm)oCDS\@EIKϓ*t]AoG4$YJCq_-Dq^`Q]Eo614#Q]i!_6+_|ua-r;-5d۶VQN4eM.vgffufWKey!0hSOPt*fop ^'ub{okOLO>+W$@ӶS;ܢs2R&Ze*/(U5SSȫ:d LwJWWUR@\V W5oN6xSpVqjEo(PGlP@ n< v`  d:!2G"*1#*k}xy_1Rgx/^?r7t/g7wzT0h`Yg׫_Eaуh`wb@ؗa68$׫ci2kkd"U37MiqfBlhW~ vWJŁ6a77 u8Wj/ar&> 6U9[j@o@[wW PO s DQ uuNd:`qSuoc!c0Ɍ@ˌw)lR|}ĻHC%{z#@3@k%^T1ZM|;hZWa7zM'hgp`:Bg-u:#~\fLe Mek8%$ɣ:T~ZӬ1iߙ)D w\9ӒZWχVp~rG,*$@mUm=XQUn&t5-u%$iOToDL,w/U]sUd qtl8  a^STɻkX]s5E6 nf $[Iۑq[tzû i;>:)+}y5^z_&RzdϾOWӻ饆a{}:izqy s/x=|Ƭ;|67IiÐ]ĉ|C =hżSKUE"}gؑutGȽSE1Øw±Mͅqt :; bRQ u̩Bung'fCϜ(}R[ m{ѧ[G[yHg['Wӛu?h!E7gtc٨-iuQў7?zDey0^(۽P ~׫=`޼3R^whZ< 8s-; 㜸/PǚD<>~ |ᘏ=s^+OS[7e=hKy_gKu^ha)}(K-oc}/U(q;^Ψ+h{3J`(}>}ڻ^ 3Fcqb6 q(`.<$@r2Q C(ZCƣqs!R+F"4r3I"@ *؈kG b Hv &#)SJ5F#!ȸq8Tȕapb1:t(`0. m%KX@2]*kԋ,AN Gʉ:uAЂ*ϲRPE1>DƁlNT=&PmD'r>t3 KNx( ڊr覩r߮ @2*47 c( !2#c5kj޸5خp2Pl+7[VG@2L׷Sӊbl76pXo4 1Ȉd77 t:K k>|G%cdAt6_ =K JAh^3 q AFhR?^WQ~gGޓQ֙9XHz+YMkY싣IY=?,&6.vek.>Y7NA@3\]I܆19zġsMs3O#0_AR}#:ub~h[(H@pת}`XV%d bxn9bHk-˂p^W,|pYoLbݯb)8̓B $4 lM9~ V Xral5p+ Ęb5’pl & k`dЉ<'KgbRscPNZ(p 4D4xOlCi#54cqu ld`#6!1Eu%eldzBN"&hty$5ٺt.tsI3hΉ8G!"ҡ=֒Md̃uy3$"w%ٛ\WX+ j,uI Pj˛0D{k]$0^~=̖lA z<p}iZS>Y0bf<@a cdzVJ{)E`c єޙtB˜J5(DZ0IJJJԓ^.+Q&)[r]HƔTE؜$ooտH(0-SJVʁX'z8Ek<RVSejJRV4Ւ;E;\ 2qW@~+ܨ@\Sl* ."YK fxuul>5\kgWT0OEؗ|)S<;Ϣ?,( ?_O'ŎY'iRR@QUYog lV"eDeC\uͮ1cMWHX6#L^r\g4 e +3~MuBh8Azf|zOU")1 endstream endobj 439 0 obj 7272 endobj 437 0 obj << /Type /Page /Parent 431 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 438 0 R >> endobj 441 0 obj << /Length 442 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1l5(H6DJU(iabXS$SsV@@C v\4I@4ʙJ>A:f&rh2 @ymi!7s=3MT$|*u"7a6z٤ k@i " Ohm2?#aqITC!~*N̙@DYAJ*d=$)4(R]x;`Ȕ1WejLզNjyݣ}x6Ve EjlyCYZv.x%=gs("T'#9|2n ':d endstream endobj 442 0 obj 1552 endobj 440 0 obj << /Type /Page /Parent 431 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 441 0 R >> endobj 444 0 obj << /Length 445 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1>I*NTpmG)c ,+lB4y'GzH&FsC#p9 #`@& 6 ^HRƒnG|J=p4<(6x9γ4h0 : !9-  "x90pܻ/s X.kmȨpc Y&P% Br1-;PA6 1/T&5IR1MSmJ45BI `ء P1M2 07T# #9 #:O!4 #W 0A<9SZ ØR s\R<*MjI*Ps4Lt2 7nw ]IU#7z6) Z+#fh'CVļy6&ĭR C1U{i[W2Wxeip`K(po.xo{`,@!  ^I",!wo+^d& _Zr6?ՐM7)[WT A$!R]P_'xR` dLC)wpa x#'X>?@*v5t=pѿ K*#EPv8p<^y: *3F iiᱵ.!(S6 9śH\'?" L)$} PBy:d  BACTˏ( L1T$e#23OM ^"bcMy IL`,4D:XLk0,M>LvxL"CJױ?:THFl+xhE vQhhUxOHT"7@̙; 9RSF)Ty7\ ڂMhfጳBBfN3N:_R'"bI^$u Ѧe\~WYiiD)ƴWJJ Tl 0$AؕU !'T%u^%|ʣ<,9ID,f{%ۧBL]wYmc- ?pjԦꃼ IJ.TϪ5i@4ڪD*x \VA뿊4'fw W0 z߈0m:6Y<\iVDTk䊀j#!T̚ s rV<2Pt6P|ΙLsU̕Rr,cu)Ӎ]HG<2lhة Xb|@&tr?@dwTm/J7 .C-s}W]\'Wz13YuE.4KzH:TG'\ieho_:M 7 /{ͼg"g=ߘ27֯<7g}xe;?htcg)g%hӓmYnJBb6*VC'h~a߿<WELws2Si#ĚG"L{Йr4k>,~KM`_}S&堻 DYnѦmAC/{xn >`LVܙufC@aN A7- Y-jvW$ݾ T B52c [tlA#, ! ~Zx0.fdG+?H;*>^ӧq\OoB)o>NOnLV/Cnekb.z/nmBNOĎ(l.XNS͎юxn 0e --&yp| J)i=BX%9mickE,Cdl1DkOL)+8BlC˲+䬉`'|mKP, `CjTHRz(q6L xoźkm/Jnm1goQN0O b[ E-Q{pP;1YNΉ0B5p;BPKPfE}Nm sQJ‡ q Q  "poh9o\10Rb% ҇0o>QOB Fpm lF-<q1&b2PK#12̱1=>!̰^+1-+ ,,Ԡ/y щJ- Rϱ Z.09 + 0x3CS!1 >ԯ%LbP}R425U 6s^)bĮ7.Y7m38vI8$2(`l2{Na Q#n2DsPfY%/7'-h_&-x/2k&뤼H'b+Jܭmo~[qr H/2:'OLF#FCx d } ~N)0Z;#3Tx)A-ETI0QjFR/qFFJk 2S{GS 7`ԉ1 3i32ѣCI3 GDIJ2 ^nL" RB9MpL; ![NFTM1%O¢gLT8d O?QDlqO/5wS4jR ;W0j1QU6U4ʆ*AT{R8u45YVS`W|!,TW80HˮxOzKrvUGU.Z5!V"0M$@}JR\X뢻EK~WˆP%!_gNڶh T GGJ@R7AX@ :h `MT"@: V*M_bDF-L]nYc`k( ;!bH!aOW?fBf# bcvtq"GdNRjDbE-VgE+k  V*( 9b e%_ 9 an " XNHT ?Ml|"t> nT{ D֚1 l VvtVz~2{:9֥m6cD "Vee`Psoa;8t!IdyHBCN*BYuod_W_U< `]aa+O)QH&sf` E$,*~j Ԉw6y| }R "8Ms@htwJ endstream endobj 445 0 obj 4180 endobj 443 0 obj << /Type /Page /Parent 431 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 444 0 R >> endobj 447 0 obj << /Length 448 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb15 R3 e*to_QB7 7%q]<^]:ւ&$@I &VMFҔ Ll> /ProcSet 2 0 R >> /Contents 447 0 R >> endobj 451 0 obj << /Length 452 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1 p?A@aRe.F֤5MӅ-pݹyJPnKq.JyM95Ukpġ A@0d%*Xct":sȝ^y%`"P.b`L^ U70#U C\ 2syZZ r:XGȨ<F`+"jFPn<2 @2Upw}릁:x@)єp `C^B B L'`E X" ܐT*NABBa('2J!5D0 հR"ݢ5<)5.1tNu'di?8ȵ d=J/R#< T1 1c-xw(`+" 9xEGk&ǒ 03sHUIS c1Ty$h@aqT_TI\&˯ɰjAHb@jbǹvl[rF;\jL5+?Ϲ"J |i؛(Jj3F;*YPU#NX i2$]OA昷4<)yPUA.}VVɫtګ_kL㭲^Yo?`Zn •J)-ʞcfqÊuv:ğd1!9#$s[POMB Ŷ:M K1.;]F~޸3T_Z6z1lLBmtN,s9!=ϛ Be3WFH!,Y%t{23QO3&0Oؒ PSwP'5*HB@ǪKDTҞ UWK=+%,'_T/,9-T>c2i)PSCB4U/CO0geO(,3Y:3GkZTmHqǥHyO[Z䌅͛Ku\Sa%\ǥL Q5ح95]&\ڴ^M9_g3Z+amQh s<0wb$eb6_cU9c:!>/?1?)k)S!URBi,jlmVt=/2zbYL a5kXdC-00RF?mZB$bMGu$vY[VGlLK3a A\^RY5FԭL6]4c\s{onEl6pS`]Φ v6,bNW(]rGZ4h6Wrdno!ffĄloޕN(li}11(קfW*9 Va]l,lH@FZle8* Go.O9oFHdhFW/'D-yog#@9E2Pif)/bhknA-y2m&nnf9x́$JoT,'ﻐv rZoъPG's@tCSu9*Zdv%gTQDyF`yGUr/of>|V̰0ĐQ(~’$ˢ* U}l,!z5'ὡotu;?<}~9{ sW|7|~ZQ GF2t+xH R8tS#IEFkJŊz4 @-  T1'qa3`Ơhn.hD* Dbq*Gct &q -+SAW9#8.V&A1ޓC1Wr a7̰i|: "ֿbMC!4jj./W`a9 /cheHpxZl8cs-^CK-sN`6 ij&}v, t5dCJxf.:~# <(o6x_߲}r-뼻1KfІJD2K tmC 0 KBR Q, :UPqYFQm4!J0n'{ f!hxm* ȰP#δkh1Ap62*m%\ B!`b"RlJRZ]OJ6*P*4pSZDT@`3 :rQz,KД *cjF ;2ZU@4OE6Cf-O+woN qZw%'Eq.c=1Av gq7.'_v`@k݁Voct|8P%c,fPFwE ^aA٥ggk=YխhyL[8$k|+@m^V6/2BԶd1h0H6 2C7ӴID$1T% DF&ҫ-T޲ւKeܵN+jrxՠk[5 |U;#śI"eưM&wRkg3_|=_x3exgVmzUoǬ{=':|oЏWrw;Fw_je}4׊KFE5(ʚ'^@"uWU2s $b BzkOj)5fY\Q1ZqM3@L#snݼl\ @_F(|֞}J(e}Q@ M+*4jB<|i@@ endstream endobj 452 0 obj 6786 endobj 449 0 obj << /Type /Page /Parent 450 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 451 0 R >> endobj 454 0 obj << /Length 455 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1jjRcR @ 6t\9#p2 2x4p b bE.m*.$`" K7S2Ճm^=/kM1W- QmMy? !C Cxba4LUGk]k,B 1uF)E6PSG)B ! JWdo0O;x28ZJh~h|Zឝ pyhɬrͻd|;uy%jX}_#h~i 4E޾?hQދHf 4@f :HJ}zm%g׻l Ρaxt mMN*DTDT #()R06/m n:̲S6䯟[HRUoCCCy>pWI No4/Rjlޝ? Ypв* }  ( k*Jk$tqӪ>S koꋶ$hd;-l*~(׮ŽJ|*Pt ePAMqX>bFx)_P|m`p|!PrQ  4j b0!O\ c$sQrs 1qڎ4бOmq + rАrEܻ^<` Cq qΰHH!D,>OЌ$bK `OȉNʨ1B'MF,Ul ѣ0'oRtCm&q'ql!v})'SڏA*r*e R 1P-.  rgP Q/1-PgTmd.o K *U"Տ#`m# rBNm⢏N TuB4 3] beI 2QW8eGYsڑfP//(1{:&U)9ɑ;ѓ/1+8,Q;Ѳ'h9R}ӟt:rA8U;J*t=O9L4+F$t=Լ1N3 ™# t OrP 1Qpu n3$ `!Uu/2/;Zxz۔;Z c{{Ǟ!מ{ύ׺ٮ#(jEZ{{[ <#}Q%0挲=c=S;DX Dnj8_Z;hփ([q;uFm{2AWq)qoǛ <uk;ʛۗ{˙}||[]ܞ![O<%+P;Yw7#8 \|;#:8DW@Bu95ߴ;Gp5KT]e:o\F6a/GgƺwqQZƺה$&q6דJ[\:عŋ݁/'Ͻ˼ټ(}ڜ}NYټ{]( ܽϰf|N@)}!%ICю8CJ\Hs[;&t6I]=3MK3dY4QUUOgny}\-=_59xքN+Y^Tg}ܾWݸ-vќ}xX]!ݰ^wӞ]]}>u{|U;fIJY!PM}_^x! ^V$3JBJ|- Jv=/` DėVQ]E.$dZ{F[ej}1Ol_Tgɒhc|W_W?{IsQoCs}>Rی?a޿? J~B t2S H9BH?A` @ ? jCpd B$(\0F¡;@@9 F!pR)!pqpg**@e `oɓ6)Q85 .5QE6V "J$g?`؅e0ʁq\qet^/W]`Z.yqlF0u,5T fcx0֥  dܞvxC ㏯1h4u\ٗt7]=[ Ώ{ @7|h;Nks$pr!!NcȮT$0 ' 5+øJ 4$8;OtWQ|L=C Z`*ڌɶz J+H"; #pƽ cx2 = ! " FB,&rB̦+n*H(ȳ- Զ@@c ::AUZW Y1HVWp%z6ϵ<4NEUW9{skV/Z5\vkW]Wuyx ]G@8vDUGqFx 7˱v>DddxZ#82B%%(r 0LS A3M4#`0s :hS: <"h@$I dkM SK5SHMRm0kڬS쒾5˺TaNl_s {߸>_hV<b4=s[ kmRD^Gkh.I݋}SQ۹kTP_U]<`&^KzфpkOW 1Vm{Ib2HDH) mb&Chx3 _6F#STVYt1OűCh;tͺԷ"moT"?˘4#QdORyqcl04^:seXg:YuR)MЗj#!*Gx7D 3Y/z8؃D &0'ի 뛪5&WwWj<+F+ʹb-Gn`%Ĥ`X2& ʕpkMy`i VRd", 0l294Ùt)l +6tl-d9F /F~!r35tsh9R'o+|fSȸ8'b[i|w kN> o`˚gawBՆt}N7E=5u_޻u޳б $n„IDXK8"[hb@s|3],N ;4[w圡g;ȎDRk}. Zm f3sUagN%%'tuLgC;oL`i挭˚S뽾>LrW^\^_qe=kF惏[ud/1{bWb}RSLvhAczR̒bͻΡ#+-+'Jռ[+t-O7Ԩr( [{?9r*cԫA9z>.A"l>A(!=9똹14'$0઴:$-%:HҵsC 3>KB5Cj {?1(;(719(896'4Kp 0;ry#-ǡ7 (z@{2 0  3<4+;E[=Wc{ŤjE|!E'{E%LXEbCiF&F`cE|)<8F|*DkEo0 C#+t ]do4w#Y&?h{d+gf{,Jjl5;H2&˻.M,ۈI"}EJ@LE2RS3 A5[B[*ŬâG$E?I{J#ٯ|dɜF#J*;KJ#bk+JYP?l6X `:j&1H7t8/h7# "Cfȋ$H3&p@čjHHOÍC2t0A)XlYɋ8M¹Iی4Ms 8+@tN #N4/40DN||J^稱 @qͱ{Om1V_ܮ`xפeyW+m{:>wWxT#a0@+TBy@ 1{ 6x;ԽM(5T66MO ČUKvQlL CD՝% :.2 a Q1@ ]Vau{yU]U846ZI/eI^v:d ^g)^Ҁf>,W Dfn]X.d&1^>gl qog$ar6!pw-u~`(gqX[#j n6~gz h-ҶUN_-qhgp[ htNބAid endstream endobj 455 0 obj 9739 endobj 453 0 obj << /Type /Page /Parent 450 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 454 0 R >> endobj 457 0 obj << /Length 458 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1nC;4S:w@gx"M M5u;d!>hV "fYfj0lKFNDժ Bܻo.>_*'/ \pѥM)f+H2ZQ %Pz_L+΂)st B)0~X2;NAX5 htY5*)Bb4:DRMBKQCM+7p!}FCy)גH˒r5HriMI U6b;ZJ&,vΕHN4xF(=L5ѳ [=MbC6^(^heLz<͢3>W(eݝ2ǰZ1e,dXfm,]Ѽpe?{=&G39yN-b 2Sk,q?MxD>g񼥛ϣ3.*2h,O3< sHԾM4Eyn-%#EVoƨ :j[O> ZQRcBlnب,(-DTmBbڢPD*0/ҋoՍ\ Dp&d% O.qgb>ڎ$8A ()S],dU p|/- 1v1Z]I !Q߱ ˢbq, $q Ob JPoˎS 1 Q % nFb%FK"酪p ` (pg d ׮ȩb|ĤbK `LN`jͲL'@lw6TQҪ1Qc)h-J5iarznTfڲҷK)?Cq R^'+>)ȉ.P 1q 3Q0 P1ڷ2[rbj3:"N #gx'&Of2U&p 0u#OP`sy0GIq }) :r;:k*J"s0C>oO,./q)AR BAR+RPS; EDTGjXDH*bQo I* E: bG֨44Sy%  sJ3YuM 2)09 dSoLqL c]mM 5-+?/Q=5,_+V- PR`-QM#/6^0,RTcS+V=C/vG1qddV7BeUR1'UZBn$t\oW5wWJXIXNskHeY2핢Tٓ5N8JJ!I*/KEFzl*qcm]^ne+ND2, 6an vlu ѵn)2W L* 0j&7,&os7*;w)@=t8wO urUBW3b&JmgmF6WW6YQibEA6Jn P\IkT'HO`P4r@sml&ĉsy;PW|H}o~,p?bw}W 7NрqRr*/d '5,(f~gQT+t8377;EP"C0ϭM-2Q3ddb=N^Fw"#R9#Xq7GBHw{} &b-&o8s[W}{P҇3%/ }2]XƷ߃~1~ 9v8iwX0^ sejQϯ9@J!X69:zHQYA n9MSxR(yY2sbW`;Wha-E}wuw}4O5 5wJ-}yB}ZV8-j['>y9WK?0Vȕ9Hvrٜߝ~Yh ^3!YNy/y吹a>LuɷURz!}y:(z/5,(9-:7S_tKTw;5M~zYf& vq`?ue"X{5qe:UX Xmj}%YBy7Ĺ&k8[ [ٯ\ j˂mB:!Y3[WֲZ`X Z Œ۟Ztq6#W[z+9u &=5XEQUA2nBd6J`?vwY8 ҧAg+hsJW0ZEjm4p}R9ϭ;Z4S7z߻i3~{Ѯ{ɑAϟ[q6C. ׼{[ޗ71(O–Qs1ewv]u=Xd/ovO4'Fmp ؓi3ibۡDڐKW9[OLQ c]|ܟ?[zM"|5MѮܱ< ǿM<ӏü!̸w$%w%;-W;ϝ9OИaTU=7`N[E8p?xuwŶ:ؗSyl!ةjxZ['xM|8&$9؆O81 ٞty]~ݙ|Ιښͽ}Z!}INm މdoߝN`s= Yudlj2fꍗ ׵k4]AwځQ5XY/͉IS{Azk*낺/ oJ N!+ ɱ> r˲: =Ĝޡ.~bg=^qlǟ=^ MI^ϴ+O^߀YcH7%^6uUvd $NbEvqZL%)Ii7IZ͡u, tڸu^l9[wܩ97_Qkmd>eQ?ܿ߁,0dP:3l]xI,> N٦ Pc)Zjk ba:MHj#pl !H`1CJ:w r,2 bAǤc9(f2 9д`.FLFp-F 51oEkNdR4q"K4\oD#( FS5 Tbb 8{ `6ᨗqŜhu-cޒ57~W7 ,hߵmM{:σ,"4 AMB D,C{ShE ;BPADqL/E Qy ,i:"BԤibB(@ 7 k1p6# c(:*hb%hNJH% RX"%h2vB(@jjj*)8ԝ*+PQMKrB[LG!8,%SHUZ qa#:_5 mr ~5nËp%[1#:]ZGpFx ]7m@J {X fNk"xcn0ĉ1~/0f6n>?ȯ|W|))(}*)X҄$AD.KL07(/#01m=gUL'z2J JV59D/UWG&J tKtҦuPueRQ+ujW|tc#[b전˳/'r+$sXJYmOBb_Yafx7֝xݱYWvx;t>kg2_4&_>gL7" ߨ?y_cL6|dYI)ElcH gFri u/ƙCp.856[kq36uڔ4ͼ((nA*t[SVSnE*rx r, 0}'̄]tLSc!8u#:MZZ>%w˝q#83"Uj{ ;h^䏯v6( DV hX!eZkmin0:?ש-'FFRq+HgyfxYHhBҡ#M5&xiL!|6ҝa|JhPDB 4PG[Z%2'pxiOUc*̼F6G- B$ϨӔ&e^QG8Kl(,Z.{{2*³+un_Mb\Ag޸̴ NUzPtJ W<'<9㣻 C]a4f8xSAov$of?ߵk[6ylŦ's<v<v[x'6'Ɂ-C17˸ س -2-(C4=*߿ >(8Ұ0 /8=/L.޶S"ABK )B鿬!š9KRB9"L&B-?ی 4K56: BP:g:::C1 =s 38,jop76y24$ <0Cy3zk{<""8*I<zs.CX t/9ֽE>8_B bzJFd2(di/lkz m$/E0Ci-;?)Ga<3ANjòGd|p/L~?ä4sY{ELCE1tHD2JKD {ŭ QA<S(sЭU VErָ)Q Z:ż|]EⰉ:A`I78ԡGJZ+ܤB+CDhBkʜo FvlbFںG2GʛGry\GrZ˙G˻b4K\T%@SNBtLyIȄDk42ĺ<`M(Q8ɂ *DQErͼ<48TR,HI-_Ŭa3V%`,NIŭ(ueN*֚N˗]`:zkLmŬpS4OUpSkd֕n:YhOK׹.z}G~= EAPxZytCMKp(:0:3Yk X {UA/|(W,۸WR % =IhC;RUdZ=mZKҢ@] Rҟ/%m/Kk[ 4E0UW{U4*؍2%yϕL[cl5¨1$fU9\M+-Vҭ[{O\m0JҸC \s}WLܭPD4Uۜ]u۪ endstream endobj 458 0 obj 9193 endobj 456 0 obj << /Type /Page /Parent 450 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 457 0 R >> endobj 460 0 obj << /Length 461 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1&J ft$(G})Dp?Ibr# 0$2c( x:,2Z;GrC$Lμfz5tq&(P$ Ss Ji%' 1l2XRPP0f9Ru$.Pb״RR '5V+70$?XӨ]?ZD_Rr'[$mLpG(M 6(]^h>RD}P7 Px*Ձ) )b讒HZw:PL gTSXpM.pY .ux $M5wٶyҿ,4KNmp `᷒ފԆuY^dK3ӝ~̃f Jo7c* "2-ĤΥ[N/ ju*. "/Π/D!JS*(k[ME֝#9 #:CcTvEI # KuEFTB y41AW3Gwq%ÓR$L2H؛" r2 $$̒LRR-ƹ1z, cHLrO9#uN= o 0GrBv 9"KAH^P(srXHjd5 28dI( a!bʰIH VI̩dLSgX 6"k;~B8uQE /D 鯍0C E)$n?0"+SxWLb4D;$=ȭMi}9vR:!&d-Ũ).GwģVf*e$Ʒ7*<՞Vu2Jک`RvU Z$]WZOreV a/qNs'.a3@OњsVk@bwVg˙k9g80u% Už-yZw 'S~û( Iz (A uXbTA^bLMJ1.>FCԢ ^[n=E|g\/i)܏V]J=i0a LE15WsWH;7,UwL`xW{ dFLIXI~e_vW6&ul0̚g 8`Z7i- !/IKn)v տ%+IY7)bڎ1s")ufۋD7`ʋ`b*eLaFpմ))`G 2EJ>`e(ò6e}yH?[Ӎs;cfIlK5Pm-"[Y]sdG~ș.>dD/M)ṛp#|Jm["8O|Z hfwc簿jèrN۫ISt=Rti唖u9QK)˫D.*+5G*Cw#t[)oGlq-3WuygxӺ Gq"u=OHݷ{n W4e7k)ȟ<\`vOn9 Vp|oJm/@̏4HKא! L %u jp |i PڱP X`FP{bM 狜]0/Έ@M"D(/{Hdꮯj4 %v1'P p֊+ a-!(Qq^JaZhڌ%(ȅρQi:8ݱ,b 1 ȗ)w  пpu$/dJIp古1揾σ ɦ % .X}X@diҋ@ǢJY(Gr(ޣP4n)#G*x g.+/qJR-M G.P/f0E!PPs FF1l O1L0 .s)0s3!FZgFr"@V "DkJo $$MB$ka%)u2b%&ú KqZ )mR;g;+<ܦQ;1=,O>{>-?23S2/pO3*M[;M"BS0# ,sB1 y BS/CSŔ"Nԟ73CMU *U?tAp, TGQT ERR0$44'u]CsVtJ)_2R PmE(gN}bYMBl_"5)  7ɼK}J1"CS%ӪSD h:-'̗#XCء%;4ؖ.=_'NrU/)RoDT;caPMdU7 Lu>3eV4TڊW=vmgp>SggAXVoD/;1=T63ՃfFDAME555kIGO @ {қ$!7g\m.tT&S9rԔRfi(+XOi`xCRUuxKdu)SS;wmSKw5{1Xe0%WYO>oMZ7 ?uD!_~QX9C5Oa[ɱظ}=}.O@Εӏy㭾^:8$qQߍ<a$#Uxz7}I[7~|/ !h_cK *E[(^WU=WQ  !h.#(  "@2F C!oF as*E3HE@hg aP )c*PG"&CcM9e: 'C_1̦c1i4P4d1` B>0 :`. 2CȰb0 )S7$ -`1md*tb(#6ќg u@NJ5ZEZ0r5  CӾ35S=I0a{Y{Қlo#[9).Aa:O&t=at3Ð]@(+D'FAoFD: BDO!AqQ0nL4MJhar( I oe/2>ᴬ3 C1ȌDF:(I=sd쓥3>$t$@Q:#!SJꋰb!<ǫ ƲJֶ16.㓊βN5J3>Ї+JOaMYٰ3n{z7ZN8\sZNq[ۭ@%-=p,& /_#4`Lӆ{I ɷ+ JRJMymΖpn K!)EA|I!Dq|LZu꺆̡IT2WQ;lk:l5$R}J !ʶ N,B-rՍf4֣(޿W٪aQ[6؍dZH.]67 ӭl1.]ͫ wy;^i^:nPٸ%a_?֜my8|գA۷1*%OGHAEB@tDcύ>hzVN a M%V! lDVȓPe+nPÈ= .oPeኃLmU.=V95^啒V|7 ,ȃꂢu `W{'ш#Vbwi-s9DWZx xR۪미<ߞE_/ytIVQS >3J a  B0eI[$Y5G\8 h6VK0AjrH>%  )̘DfGԍg<6hDqIZkLÑ[puZ5JGW˨aY*UWIu'eE3RnjuPioXzybX$tv6{c f0y+6^ɓhyhl`(`Jb2Wڟrri򦡑EUZd8QFTeaR#BA&ҙ K]* r ,Sl@0 Mma*Kڏ5;{.fLQ\je1i D"*|0VXB'\ozca;gV9M/DrvlƿbJukϸgjǞu֨ VMqv[!ov/|9àhiQˑ hn] M\udS#~u!)]$6d \gC`1f%qIMG:WI`GGp&k Km;9DL-<:G WqFZUUb*w^8Ï*:gKr$ 7`M`6/;_O*o᳂t쪃STPt>R][˹|/m12^lΰ]m#]3ؐmJ{h1 iG^GOխɮ413g>zo+ZU~M\2ٜQh5~Z W0Zӕuz ߟ"'!!Rb`:={~1WmR1VƟ{ 66pDd?)PDKh 2嬹F޻|-Fׁh|LvSG{xr9qY@ݻ臱=x>c'o<^R/yDWϓ-%Z$$+>dBӡ&C >4!1S³lL9 3-:n?X:B?0D4*Fq>D2lFrBns+dGSp[G{ CX34q|}|;?G]8QF!rlHv;;Hõ<ĺ6x;eDf2r2:E")E3p7"6.,YA":PE7<:y`AFoAи+=,dQk pB)#HRBTkK l~ pKTqT-,l/DxT3ȺG¸ˢ|H!E$C-Kt=6HKdL̪;d' SLDnC@@FlPJQl2*6p5ɼE8̢hJD_J\ Ĩ3F@SFd!Dg }el=O*Q >4.O6KOm0ơ|Ov'b:TLH6 :%"KLА+P MfP6HJMyQIZItڲu/(2 x/4x6IwH.0Ţ< 6 \]._,Acžrƃ^e/tkS*'O:Lг_\SL87KS~ӱ ;O> /ProcSet 2 0 R >> /Contents 460 0 R >> endobj 463 0 obj << /Length 464 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1ЪSKE~*g)h"O`Ay0&&r:I_6ekc,e5| p'epbY\!Pc7 :9 WbsZ\~ҦAI+s)D2N`%'R[fLi=g HU.m Cp#'ID|RJF߈zB6RJ3JՊb&J9H&"c#z3 a 0CbOA|d檎cd(b$`"HdTtP47bʈ1vȨ)m8eK$PGG6QgЂS,% d\PW/"әX#bl6{@٬ZpثQ<VMT5$0ZGdES~o,(hWܙkl-bܻ5p =j+ (v0-JOImu\{i^跢dS JBiJF6DԺNqᚪUiVk$SuXdG:hlN`*䡝úFE;(%xUhMz6|ś+tfK=}E6q4,sm3YN{{ofHʴrAr-9R;};n*fLi9fxJUFwƗz&Չ$ޔvc=sB:V,_OjVL8F& s1QcP. !TThf'sՉфlXRXǩjk\kc |vd $a Mj1Ifad4*Y?C^ `!=T^P:g?%<˨EӀ[6\+_JRT_n^F]}ظc 9 Uܮٵu/9ͧAA\փƛpc+jXnWCN{#sşP\.inZY e2?ԛ1B!Ĵݳ\36Og ,;e57F\24:ݟ*'M#5M? 7Չw44/GJ OnοԼj?L#H$Q;pgdnƚL.` In.'B$@.EMdvnvNvd(u)-2x K TO2F)8-#nB0],p-9^0 |PDPж!e Ц欌ݯ- }( hoڜ\ +`A `l`j"֪ pR~n p +P ei:oѪv ܐ@F JCRȧݨ is `qN̸.&N0!'QIO)qQoOKKlPu*b1 o.n2 r .n!1%FKn&ʄ/ppq6 $’ '?㚏`qL-Vo j+L~ݬJv/V37QR1* Ҽ,+/_ 'E,Ѷ1+зnG-!r+nY-7'!Kn&CP #ݩi1S3r02r 303r#r&xdFE&%r6 w# &Ҫ`ڎ ls }iQJt_ҊzvPN'rj1pȏ'w/R;K*/Su+N=rcP/R^{R30N..? !T*Rz;>`h2T+( 63 ӄB3.T?BD 4X%"2.sST5Шb g6G$zd!$."B'a(T2ܱRs;'oEJH Zҧu@G(OWQ(z]TgguUg9~4"QX?G6rHc {U{0&/V55QیÍmi'ߍNjD뎭w]u>9) - 9'N;/AwGs@*aUE)8eY>({_ F"!+"H;TyiVW,> ݨ@tŤ ޸mKk}zŝ;w-SZT`7BX!㵰ս̻2 rQb?8-7]G){>ҺQ4x68V_MUJ(?WR f+x[hi <`qp N[[[ @U|l4|ǻT\Huἢ@M*Y)kGSpKpy4f[\ [iTB^'jY/4^!d87xD5}uٓ-piۋ. J Wx}u;=[a׍|'\ơD;fo܅KsR9 oxy]Ӟqͅ9 ϡ.w7 - ~񳌌D&ʶ">+__}S5lJȄDKmhnpy홤plK?ɘkYނ4pd #a 3FA h PH4" D"QA1@xL,] bcQSd &B*G#9$f5 pP <-΄PG"&CcM9e: &2_3MBd24P4d1Fڄ1+# u|P\ 3``C8RoFiMfR3a]SFȠLكFxv[[ߌxcZ5+p FH>: #&D >/ 5 HaKl(;q O> roК"s3$2BPD *kI**Ő5G1BOE4+ D/ *m1ďD.›'B?JD/r54L E6DOl3T"MrQІVSfYظvϺvӰׅ׽oa5{N+} bɿ@X4H@!- >{=}o8)K{{67S#k ٸbs 2C$=ؔۜAp(L`` GyQ7Rq90 ᴹuЙ3*XխZK Mo38.8G~sNzx}vvIs:@d_nN&UF>%9NX{ZO !g)~=eڗ`d\@| ̲pys1b— #OF} 4.R5257ʂLhX嘍qLd;"SmDVg=q1IKTXtʓ@CaĪ c9CkQޮ%} `0iAAH#hRֳ[2nH"xstyznR%!zC5D?6!j,ruT1Sش0}?wZjtɘ֟QR@g'5CavVϚP2zFt=Kd5ﰔe;Qb~2|,Gs>ډfcB ip nSCM2T;vqiEjU7,k*wz$FP_b Ue|>9/A*W<m\by͛0%paX߰/{xa ,)6!جO,x7{rw1,rwN7m1pa!ϥ5BߓPS"?['}9nwYǃ.!{WisFJY6db'y#l3 )~rɢ|t:uƙi/ J.{lTh/X%rDʠ4W(eueΎ$*\v9IN,^FzM0j Wr&8gKpC;Cl @ lS*v@ocj"x-!&5tQ*j]$PE/yGq/-vWF-lpi' S=db4=+5%|jX1 S`"`Zk6t¨R Ao!'¨  <"^R <*0hE P)@w#z쪌uu ×n6Y)=rmX;GjTή-; X~XĒ?^ּWtH"–$z-޵Fy_mṹ2dVPxfM aЈʭ#)Y)4(lrX 34:8p0S"=vx* p᠃j<߂1D4 B1 :ZRADJ3ɏC++ (@(!2"2+p#P7]!IЯ(p@pэ) [)")BE LsJ8㔯7x0h";rCK {t;D"H`BBH4d,D9C H䓛6´3Ԓ܇DY<ԁH{D4䑁CIDL&ĞGɜH(BlJ$BM}JR!ʲMELdŊ TJ}tFj&>ȅ%0?"6.l62p1m7j myaDo ?|qr.Ǫ0*j\)ʱJ=Ж Fd\͸23+Jݟ QT*NNNKAC4SNKZI:N0 TNMg1xI nJh,HT& E4,d4B]τH}KT8P= >K. /6ܿx6:@pm9H7qa&~AbQ.+#1JdMa7f#H1 endstream endobj 464 0 obj 9519 endobj 462 0 obj << /Type /Page /Parent 450 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 463 0 R >> endobj 466 0 obj << /Length 467 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1:)a㖆PZ/785byae7Ug"g9ivufZuyj:fꁰo;Dxoh%B/!k 1MS:C x/, 4S(vb%RQˁbE K=!KF&AD1E=m\!;<)F0bӡvw$%jYJPݬIe==^:h-Mw?^VX6:Yz\๭FYVfX[g E@Kz/ " B>`' zO2&3^ouXeګEW٨x`a k5bXlC`CPACTЬ$ȏb*m4z 1n\dRbvS*l7hu 4ce # DK99b灂t.b&Kq֨` ]*vF'y ӻNdлFG xq}T{̄417~P/ڣ*{К^h0Yrb 1*}Zo2!^-f94_◒c!Sz(h֜I'7 $ 0u>֤OʀN皨^Z \RSIj1hj4~uq<(Q6$0I)]Tz4B׎WeQ_'jD䛶**,e 57.p2GDiԀK G7"d;-9CSl)2P%X}PmE;y} óRMO׃ [sqU8|SC|a񥛵8Ob9tjg@WBeb(œYNU/Y( DO4TGA8@CxdN8vb TYQuB$1Rq/  \6)lFrtBL(IV%LڞVjQ ;nqR#v<5 9/ g0Za>RJ|x|  UZ-X$ 7߶`lN ?{$;{zt#z4+./*lPj(Ɛc hŞ&5._@A=>lRʣ$%fdK# A2(U#n2U"$>C Q&A"pRs%re'LPK(2+xNC0*++}lK-,L,hj(!̨"R^01|1l0 쐎p9 >PnDf$ QBȯ$l,>P0%hҨ%0n,g#kQ#6qy#=7fO7rS7XeO7(})e'9y(Q3:3k:9e,g:Lp^3\j'Y;R@_fRnI> }>˭*L3>LIs-Yj@.fb/edy i]/nl<0 ڎ P~ G 1a1-Жsg22U* m;ʶp®F 4(GDP(Mk6 4:Pe~!StyJ;d#$c28Ե+{%'LvŒ@ԡL҅:<<^4:뎟;s!T* QA49&>q@ )K*Q L@RRO'A ST2i)B*m'R430q  #D DRNg#12\2iԄ'TqsW u0 s5 ԎtET63t;t\ &HؾX="ԿC8o]2_^U-\I__;\ "<OUPA6 ;s8tb#ccL)?aV=S|i@7-Tdu=^vYT#>MZ#%Pd hYB6 \UbZ@kY'KZ"OWcm/p'R LY`Q xO^EYeIc"5b?mJv̺U>:T]b̴ o4N5:u-pU؜nw,D M7`,oew%PPVb [A7nQJy{nW+dtxsd]d (wgSlguP7v`l͢ ^ӭ<[061 1 EuEaXAFOFbъM:D\jIB4ze u"}65KJ[K7nKp KoL_SM8qNX#r˙@X)PPX/a1w87qr*WǗkSOZUqV-\^9k`+bu \uɨW_qaZT@ڥw?f$j ;$RhGqGz~Ce)Esst+GgGH: TwIIPKRu;,T8%>+Coq^{7w %RKӵINMyUAsYcsbwIu1sr;{d9oYx=Ķ'!yt1a0 HWX#@FtqWkk57ɱzM_ux[1۾5k"#^E+9%7r{?9&iI; Qw&^[;OUGթlHնu;mtkb]R<'>e 7s-87);9Em=7_b}OƓ5-4ug|T;3j-?؝|r`]٧=d[+UPHB=UWyV}UÜ0q4: q=bXs1oe< {ˠYc@M"!ny71K?L}u}A^5Ųo~WUu}Y:Q~b]IʒwsW=1Epe~j{9v3>h_Wh١>XH2zR @ `ݝ[W̻+{: 6tV54FSZM#<1'J9L'?9RQ?91[S9GP"~mCF>z?9yކl*݅#ӛ}_۫;O(v>uu^BZw~ lF.g,ZP0+ 0%DCQ:rff-x3H@3Q/h)E jz 5JkM4l-4Ws;80QC9jl+I6 xrG=C\9,N5WĘXV(-G:>pb4~%:L\e=>2ϞQmgSP6`g:gz59+̟dw3Cau/N2Q3n6DJg^aȵةnu!FrQ]ENsβ= %jU@M>"hVN!ރ Dcُa,|9/-)I3g(̴ecRݢKL$AC0`UMfT@ށ4"@u& xmIӂ^l1M?h DiQm-oT%[A>-JW8+yCED\Kq dFo)Z7LP۬EOD&ʚ,*G|c=7%PUa %vɍV<%pa9`oRJ14 fm-:h4&NNm& d1^qYHG9Aͧ{:؋&6#:CM[~GB{fޭd{oTN,#/F`.tP/Neoh2v~ υ;׷WγC^4^Z:C t~~9NNtbx~R5ڽeMaiuY2"@r q.' -X,W9V/pn1ɓ$s:gZyvgak\nGu', |i2Qɼm*~hCC&{I>MQ=wi)o\u3͠TRPʊ%cա00ài }*kfk  XFs@6-A`5@HܲBy2bR@+2A@Yٻɹٿ <$< 3 )Žcø=эL <<ㄽ@T/)AF(rk kFd"AմD"I 9 ;+5p<5/h4s5˨?d?Qi)-,a6 ;62ɿ=kDLLLF$+ցH K!><@P/|LDQ ġ7Ez8*EA„[F$!_ '# `F2ld,#=gF %3tfSEiFX ߟPо05 0ZU 205,t6? ]Cy9:ǶC=Cg?D?؉?;H;dEDsqIA K=d&HwDOܑFlA$WIlTSîI\TlɄV HDaE@J,j7&JcC~dYJs898GBS<26G D,6{P b&df?1h'1CP@qܚ@La7)ϛL̺,@IX̰ItMJ+,ъtXI[Md ͻ=L1LH N"B9IJΌ9\NL=> nX& KBRt9T̸: /3>;|A{!K)'Z=Z PD 1!4E4% 9d2psN$<)I-E4ó*3WQUNPL4m= ޳_RCiJt$ m%.;R,]%) $5Ct>>p&4td-)?cCD>P\C@TDD$] H E@uM8a ӻ*M Q$eeVMU}B=YIW9$ָ--IUe!k#LIҲ|e8 t@h#֣ܮ|V|B$9O􍌵K5TˌwD =Ǭ7S%BP+D>GeH5IJ4?1=LZdUء (oXV͕UmY-Yؒnͭ=}\=`EV#ټP%'8m"iRM35Z lIZn"l🁸C ,&OZlS}x3%5SRO}&A-Ѓ$;T=sbPe;JKs!yZѓ|KZ$ѳM•"U\\"٨=V (]lyF{R9dgƼݰ%*]A^;^]%] @}=]Fޙ^CeQ85g݈_B]!  endstream endobj 467 0 obj 9283 endobj 465 0 obj << /Type /Page /Parent 450 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R /F14 258 0 R >> /ProcSet 2 0 R >> /Contents 466 0 R >> endobj 470 0 obj << /Length 471 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1:)a㖆PZ/785byae7Ug"g9ivufZuyj:fꁰo;Dxoh%H45NnC8 p2(2 07)NKTXsܾ @jPbDRc'CCUWH&h'̛M&A@c2lrY?hf5T]▣h6Ry!s}}`yUecau iUooUZ[QW 还 ע}E@&Ko7/AdMAl<ƚ^?y0XPb]V,kp/֎&fk͆s 5J5"k9cرJk$m"n-ͺ6y;zo8'@_a=8Ғ1m9pP\۝$H:CrM'N<3BI.K):mwi7|I]w/B&Zy(rCӂ¨G, j_JO.j,ŕ>x0%T}x) ޤ-gˊWʙu1Rx g2YEe&Ϛ0DiƼfL؂t7 g)VxMr%4VCq =bȖ!CbٚH_!B0vEh]tR{ MRN-9\ۛ|bnE 2xp. /t ./Bc̉$- 3sT\zu ԐKg2KDУn8>h٧o^oܙ9 ѝ5#z<ҲP4ֆjJn Pyܳ/ ]^sXE뼝vɗ5{Ajz*o򡦋|k?_ ~̢ȩFHFwje5}7T  e]K&sJ@jڤT- Jmj'L\ ٯLnc()ςo)m06H4/0k,e?DR)hF^+Qjor9D㏹LuЀ|Pp)LƏ A j'  ć"epΔ3Bn实P $4?-Vժj-b`ب@ '$ L*@m'3.N Z  T,pƈ} `pBV xl&[l̺?To qc\ za#%ЊO1Ц&HʤpVǧ ql*0 $#Qq|f@ Q 7 qś! 7 /Q" Rq'#QK~? !1bqp"/'2d.%< @TG&Г$%"X;DLl@PB>$'m)fBbNRJGGR%Vm< q H"f$Z;G(H-L<#Y)̥d|(~5RH Jg,R)R) r8)z3'@ZOBt`H"*BD+I&£7ڦC,b޲Dh^#DHt'38vmD+$Rt4s!9Kb9XƓ @(@@: 72 8 "|. @8,â.js4MHqN 8<&\)Bd6@:t;${,M;6A8s z)(ׂ)Q9(&|4P, : [9n*SLvmĊ9ĐIDIĠqJH'DLd湴Q9BrS!;dPQErhPQ@ͩRE!ORH"D0/ eiG ~l*Uj%~!UvF$ UX^ՎH`!YzgȑY|D(d5W[[(fZ$՚uʔ0r$OV:˲&z[^_ " `aRv t>l4c΢5Dr %2I@cƨ/ @fJg@Fxgn))XsEnm횒Kw)* 4P'@%H!ZָXl'])j!ŁlhiU_Ă}][Z6(vY&կobh6^W \7o.1Zwnh+]v܃!pU\k`kb峗s _;$^Ct#t]uokZw]ajEbf'p$d!gx W-md. q ShB`n̏)gEN'F/6L=P8٢KQ@P `iÅ b i@6P֯k1Ssv+'Cq@ML(x 2Wehrox'/%1plӃx+a?$bx7&7u p+2 lbFf` mt'8e% ;`W\aq$fb)tOx1-.`ywy  f!{pgjFV)IT3BFbMI Ei$wxdd? =uxk$EInYVO8`EE_'⇓+u;# YDob-w2/, \ qɌA3KM{|,3|x|MU$hvQ{GiVv=Q7D kt-"xqy=xyA3x{MIVMbiѥ EF12}m;y^Kh7?VrY_ u rCa 57`8Ewȶ!󬘨귏w8ݞɞVr1%{8hP#60-! XyhZېֵWEh)"!Z07AK51iΏ%ZU\5]nM8AIZ{Oa8U v!%)LN516@fۄf9#٢UHϬL-LWŊl31A$TQ`QX ʄ F HpϊsW{13hQ,QI1N2yIBPkͽ[iQ]$VĉY"TFۑؗΆb1/Mjx_=ާj5Osq7^[Q_fv=^qTEyUXwm}s}|{E#|M"y[럱߯^۟ ƿGy=Q|u`_ P}i 3FAȸp4 #pp B\1FX4TF蠀d9b- aȂ\cFFSiP r(d;M8 r;Jәt0 &pi7KC)(Ԛ*0'ck?\ApP5 Eр)-Ap#|įT!.3K!Y+x1E2f`3y1 FA&F "ȷTgk'rxV;ÆPN7nڌ7z|g?jfn66== >6p# ,\ah{B>A/TNpn8$h.:G D⼉K7N2+%j0> m%̘I=KLR+L0\a!S<'H" 7LL=$)JN& bʡ˺~(2)p@*J+J⼰,K 4;R^ɦah 0Ps ,KPa"2IP˸,9V3rѷ3PH H8-7Ax8 c Pr2otr5T<3-v}䒺cOˏSzCWØ%{OPbl[ Dn5K8#Γv icHA$ʁO)IZ4$TL^83&"jzj+hӞgZ&P^y:I#ΫcEh-fH:z*j8 /c1 eS,ѴZu`!Wn {lQ5eb8l6[DY4Ii֭kun7ၺ PncwUyjyvd_3_I&(0ǫCyL_bɕ:!}O&Nc?5oŜǢ~ zmPb{G*h%o.ku anJ*p\IDFLKJBRN JcP\h1" WdBq*qƩ ˕rd6Ck6܍C˼aoc Ru ۴Y&۷u؛471M8W*x~pUxK%>h`=U#.<aVW{_|9 95s0`LaցAO~fALJ|w;'-Dn eOi - ~A4t.xo(% &g+f*Q©xЦZqʁȹ0oQU\}J`!jXd;2A2ffByA>թVu[YD> A5&65ޑNձ3K('2G>^ЪwVqٯ'ZiAWL†(^7ic&~K1e_ޮ6Tf旴ZD {+,{-u$Nآfu{x XJ"M;5sHEoDG !=%ˉ>kȟi{ &%Oʺjn<6b[d̳Zkc6i1/=WЊ!vl}pb `k^h, endstream endobj 471 0 obj 7079 endobj 468 0 obj << /Type /Page /Parent 469 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F6 20 0 R /F7 37 0 R >> /ProcSet 2 0 R >> /Contents 470 0 R >> endobj 473 0 obj << /Length 474 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1F1 DRPfl=@N6P@k/U,P1PD%,6P0æt##H$A-XR]$ MBE iDpu RFl4]u~B?Y DZZ9"{oPCxd !4@;Dݯ"eO~ymV9Q_Кf !(0D dYQw}3diE0ԣi`ħӨoM)'Bpa%)l 0X 0b{)26,2WḎCi 08Ug|5UjTj.*bkzitK1K@ge}=<2Z23A WfR*۽c4 {*BmNE5~,lJy6b}dΔԙ89zRǕ-!ŹJ\C/ "{{u04j*>;VBu_O o WaIuжYye%ū72b/$Q.2Xv8FOI(?X— Sk݆N2BSZWs!PzQJ`u[ 2P:*Ř ^QE a!@uXK60Qk3Lff^ K0  endstream endobj 474 0 obj 1800 endobj 472 0 obj << /Type /Page /Parent 469 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R >> /ProcSet 2 0 R >> /Contents 473 0 R >> endobj 476 0 obj << /Length 477 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1ߛ`gb4@.VaZjVL(|lf+rע~2A㜄@9x#;V KJF4z!>+Ѝ A3=Y6%ʨWz^AeHi)ҕ@(Ot\+AH@ljHX^*BDnnrܦ~E*W+B`o vP7^(o)yLҀ1F+}ǯYԩ9BRtxw_TdQj *t2_8"M~[FzpL;f`TX+ ]0(l L8CrYÕ>LA]VDԨSNg/QHU C`lM2c0$XHbTZ`#&|JYzI^ kxdd`Í)ICʎ*sRUNI.`PgWZ\i^ endstream endobj 477 0 obj 1912 endobj 475 0 obj << /Type /Page /Parent 469 0 R /Resources << /Font << /F0 6 0 R /F1 7 0 R /F2 8 0 R /F6 20 0 R >> /ProcSet 2 0 R >> /Contents 476 0 R >> endobj 479 0 obj << /Length 480 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1> /ProcSet 2 0 R >> /Contents 479 0 R >> endobj 482 0 obj << /Length 483 0 R /Filter /LZWDecode >> stream Ѩp.q\4 @h@0Fq4^F F"T3E1k g';!XN!DI S5J(<$@-Hb1> /ProcSet 2 0 R >> /Contents 482 0 R >> endobj 6 0 obj << /Type /Font /Subtype /TrueType /Name /F0 /BaseFont /TimesNewRoman,Bold /Encoding /WinAnsiEncoding >> endobj 7 0 obj << /Type /Font /Subtype /TrueType /Name /F1 /BaseFont /Arial,Bold /Encoding /WinAnsiEncoding >> endobj 8 0 obj << /Type /Font /Subtype /TrueType /Name /F2 /BaseFont /TimesNewRoman,BoldItalic /Encoding /WinAnsiEncoding >> endobj 9 0 obj << /Type /Font /Subtype /TrueType /Name /F3 /BaseFont /Symbol /FirstChar 31 /LastChar 255 /Widths [ 600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278 500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549 722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741 556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549 549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603 439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 620 247 549 167 713 500 753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460 549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768 713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603 603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384 384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384 494 494 494 600 ] /FontDescriptor 10 0 R >> endobj 10 0 obj << /Type /FontDescriptor /FontName /Symbol /Flags 16390 /FontBBox [ -250 -220 1248 1000 ] /MissingWidth 600 /StemV 185 /StemH 185 /ItalicAngle 0 /CapHeight 1000 /XHeight 700 /Ascent 1000 /Descent 220 /Leading 220 /MaxWidth 1040 /AvgWidth 580 >> endobj 14 0 obj << /Type /Font /Subtype /TrueType /Name /F4 /BaseFont /GELogoFont /FirstChar 31 /LastChar 255 /Widths [ 500 1000 500 500 0 0 0 0 500 500 500 500 500 500 500 500 500 500 500 866 866 1000 866 866 1000 0 0 500 500 500 500 500 500 0 500 0 0 500 500 500 0 500 500 500 500 500 0 0 500 500 500 500 500 500 500 0 500 0 500 500 500 500 500 500 500 500 500 866 866 500 722 500 722 500 250 0 0 500 1000 866 500 500 500 500 500 250 250 1000 500 866 250 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 1000 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 ] /Encoding /WinAnsiEncoding /FontDescriptor 15 0 R >> endobj 15 0 obj << /Type /FontDescriptor /FontName /GELogoFont /Flags 32 /FontBBox [ -250 -27 1200 1047 ] /MissingWidth 500 /StemV 91 /StemH 91 /ItalicAngle 0 /CapHeight 1047 /XHeight 732 /Ascent 1047 /Descent 27 /Leading 73 /MaxWidth 1000 /AvgWidth 500 >> endobj 16 0 obj << /Type /Font /Subtype /TrueType /Name /F5 /BaseFont /Arial,BoldItalic /Encoding /WinAnsiEncoding >> endobj 20 0 obj << /Type /Font /Subtype /TrueType /Name /F6 /BaseFont /Arial /Encoding /WinAnsiEncoding >> endobj 37 0 obj << /Type /Font /Subtype /TrueType /Name /F7 /BaseFont /TimesNewRoman /Encoding /WinAnsiEncoding >> endobj 43 0 obj << /Type /Font /Subtype /TrueType /Name /F8 /BaseFont /Arial,Italic /Encoding /WinAnsiEncoding >> endobj 44 0 obj << /Type /Font /Subtype /TrueType /Name /F9 /BaseFont /Helvetica /FirstChar 31 /LastChar 255 /Widths [ 750 278 278 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 333 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 260 334 584 750 556 750 222 556 333 1000 556 556 333 1000 667 333 1000 750 750 750 750 222 222 333 333 350 556 1000 333 1000 500 333 944 750 750 667 278 333 556 556 556 556 260 556 333 737 370 556 584 333 737 552 400 549 333 333 333 576 537 278 333 333 365 556 834 834 834 611 667 667 667 667 667 667 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 500 556 556 556 556 278 278 278 278 556 556 556 556 556 556 556 549 611 556 556 556 556 500 556 500 ] /Encoding /WinAnsiEncoding /FontDescriptor 45 0 R >> endobj 45 0 obj << /Type /FontDescriptor /FontName /Helvetica /Flags 32 /FontBBox [ -250 -219 1200 906 ] /MissingWidth 750 /StemV 80 /StemH 80 /ItalicAngle 0 /CapHeight 906 /XHeight 634 /Ascent 906 /Descent 219 /Leading 156 /MaxWidth 1000 /AvgWidth 438 >> endobj 48 0 obj << /Type /Font /Subtype /TrueType /Name /F10 /BaseFont /TimesNewRoman,Italic /Encoding /WinAnsiEncoding >> endobj 55 0 obj << /Type /Font /Subtype /TrueType /Name /F11 /BaseFont /Symbol /FirstChar 31 /LastChar 255 /Widths [ 600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278 500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549 722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741 556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549 549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603 439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 620 247 549 167 713 500 753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460 549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768 713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603 603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384 384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384 494 494 494 600 ] /FontDescriptor 10 0 R >> endobj 56 0 obj << /Type /FontDescriptor /FontName /Symbol /Flags 6 /FontBBox [ -250 -224 1262 1000 ] /MissingWidth 603 /StemV 107 /StemH 107 /ItalicAngle 0 /CapHeight 1000 /XHeight 700 /Ascent 1000 /Descent 224 /Leading 224 /MaxWidth 1052 /AvgWidth 586 >> endobj 69 0 obj << /Type /Font /Subtype /TrueType /Name /F12 /BaseFont /Helvetica,Italic /FirstChar 31 /LastChar 255 /Widths [ 750 278 278 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 333 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 260 334 584 750 556 750 222 556 333 1000 556 556 333 1000 667 333 1000 750 750 750 750 222 222 333 333 350 556 1000 333 1000 500 333 944 750 750 667 278 333 556 556 556 556 260 556 333 737 370 556 584 333 737 552 400 549 333 333 333 576 537 278 333 333 365 556 834 834 834 611 667 667 667 667 667 667 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 500 556 556 556 556 278 278 278 278 556 556 556 556 556 556 556 549 611 556 556 556 556 500 556 500 ] /Encoding /WinAnsiEncoding /FontDescriptor 70 0 R >> endobj 70 0 obj << /Type /FontDescriptor /FontName /Helvetica,Italic /Flags 96 /FontBBox [ -250 -231 1231 923 ] /MissingWidth 744 /StemV 79 /StemH 79 /ItalicAngle -11 /CapHeight 923 /XHeight 646 /Ascent 923 /Descent 231 /Leading 180 /MaxWidth 1026 /AvgWidth 436 >> endobj 75 0 obj << /Type /Font /Subtype /TrueType /Name /F13 /BaseFont /Helvetica,Bold /FirstChar 31 /LastChar 255 /Widths [ 750 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 750 556 750 278 556 500 1000 556 556 333 1000 667 333 1000 750 750 750 750 278 278 500 500 350 556 1000 333 1000 556 333 944 750 750 667 278 333 556 556 556 556 280 556 333 737 370 556 584 333 737 552 400 549 333 333 333 576 556 278 333 333 365 556 834 834 834 611 722 722 722 722 722 722 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 549 611 611 611 611 611 556 611 556 ] /Encoding /WinAnsiEncoding /FontDescriptor 76 0 R >> endobj 76 0 obj << /Type /FontDescriptor /FontName /Helvetica,Bold /Flags 16416 /FontBBox [ -250 -257 1200 914 ] /MissingWidth 743 /StemV 155 /StemH 155 /ItalicAngle 0 /CapHeight 914 /XHeight 639 /Ascent 914 /Descent 257 /Leading 200 /MaxWidth 1000 /AvgWidth 486 >> endobj 258 0 obj << /Type /Font /Subtype /TrueType /Name /F14 /BaseFont /LotusWPIntA /FirstChar 31 /LastChar 255 /Widths [ 600 250 500 250 500 500 330 272 500 500 500 500 141 141 500 141 500 500 500 500 500 500 500 500 500 546 274 267 769 493 769 493 769 493 769 493 769 493 1049 770 708 439 697 494 697 494 697 494 697 494 381 274 381 274 381 274 381 274 769 548 810 545 810 545 810 545 810 545 820 549 820 549 820 549 820 549 766 446 769 493 807 547 810 545 810 545 766 446 807 550 601 547 769 493 769 493 600 600 600 769 493 708 439 708 439 708 439 708 439 807 600 600 600 600 547 697 494 697 494 697 494 697 494 810 488 810 600 600 488 500 810 488 810 488 810 488 810 488 819 548 819 548 381 274 381 274 381 274 381 274 771 541 439 267 740 520 642 272 642 272 642 272 892 522 642 272 769 548 910 741 769 548 769 548 810 545 810 545 1093 882 769 383 769 383 769 383 547 382 547 382 547 382 547 547 658 326 658 326 658 326 820 549 820 549 820 549 820 549 820 549 820 549 985 676 766 446 712 436 712 436 712 436 778 541 807 ] /Encoding /WinAnsiEncoding /FontDescriptor 259 0 R >> endobj 259 0 obj << /Type /FontDescriptor /FontName /LotusWPIntA /Flags 34 /FontBBox [ -250 -265 1306 971 ] /MissingWidth 588 /StemV 113 /StemH 113 /ItalicAngle 0 /CapHeight 971 /XHeight 679 /Ascent 971 /Descent 265 /Leading 235 /MaxWidth 1088 /AvgWidth 618 >> endobj 2 0 obj [ /PDF /Text ] endobj 5 0 obj << /Kids [4 0 R 13 0 R 19 0 R 23 0 R 26 0 R 29 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 33 0 obj << /Kids [32 0 R 36 0 R 40 0 R 47 0 R 52 0 R 58 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 63 0 obj << /Kids [62 0 R 66 0 R 72 0 R 78 0 R 82 0 R 85 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 89 0 obj << /Kids [88 0 R 93 0 R 97 0 R 101 0 R 105 0 R 109 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 113 0 obj << /Kids [112 0 R 116 0 R 120 0 R 124 0 R 128 0 R 131 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 135 0 obj << /Kids [134 0 R 138 0 R 141 0 R 144 0 R 148 0 R 151 0 R ] /Count 6 /Type /Pages /Parent 484 0 R >> endobj 155 0 obj << /Kids [154 0 R 158 0 R 161 0 R 164 0 R 168 0 R 171 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 176 0 obj << /Kids [175 0 R 179 0 R 182 0 R 185 0 R 188 0 R 191 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 195 0 obj << /Kids [194 0 R 198 0 R 201 0 R 204 0 R 207 0 R 210 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 214 0 obj << /Kids [213 0 R 217 0 R 220 0 R 223 0 R 226 0 R 229 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 233 0 obj << /Kids [232 0 R 236 0 R 239 0 R 242 0 R 245 0 R 248 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 252 0 obj << /Kids [251 0 R 255 0 R 260 0 R 263 0 R 266 0 R 269 0 R ] /Count 6 /Type /Pages /Parent 485 0 R >> endobj 273 0 obj << /Kids [272 0 R 277 0 R 280 0 R 283 0 R 286 0 R 289 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 293 0 obj << /Kids [292 0 R 296 0 R 299 0 R 302 0 R 305 0 R 308 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 312 0 obj << /Kids [311 0 R 315 0 R 318 0 R 321 0 R 324 0 R 327 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 331 0 obj << /Kids [330 0 R 334 0 R 337 0 R 340 0 R 343 0 R 346 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 350 0 obj << /Kids [349 0 R 353 0 R 356 0 R 360 0 R 363 0 R 366 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 371 0 obj << /Kids [370 0 R 375 0 R 378 0 R 381 0 R 384 0 R 387 0 R ] /Count 6 /Type /Pages /Parent 486 0 R >> endobj 391 0 obj << /Kids [390 0 R 395 0 R 398 0 R 401 0 R 404 0 R 407 0 R ] /Count 6 /Type /Pages /Parent 487 0 R >> endobj 412 0 obj << /Kids [411 0 R 415 0 R 418 0 R 421 0 R 424 0 R 427 0 R ] /Count 6 /Type /Pages /Parent 487 0 R >> endobj 431 0 obj << /Kids [430 0 R 434 0 R 437 0 R 440 0 R 443 0 R 446 0 R ] /Count 6 /Type /Pages /Parent 487 0 R >> endobj 450 0 obj << /Kids [449 0 R 453 0 R 456 0 R 459 0 R 462 0 R 465 0 R ] /Count 6 /Type /Pages /Parent 487 0 R >> endobj 469 0 obj << /Kids [468 0 R 472 0 R 475 0 R 478 0 R 481 0 R ] /Count 5 /Type /Pages /Parent 487 0 R >> endobj 484 0 obj << /Kids [5 0 R 33 0 R 63 0 R 89 0 R 113 0 R 135 0 R ] /Count 36 /Type /Pages /Parent 488 0 R >> endobj 485 0 obj << /Kids [155 0 R 176 0 R 195 0 R 214 0 R 233 0 R 252 0 R ] /Count 36 /Type /Pages /Parent 488 0 R >> endobj 486 0 obj << /Kids [273 0 R 293 0 R 312 0 R 331 0 R 350 0 R 371 0 R ] /Count 36 /Type /Pages /Parent 488 0 R >> endobj 487 0 obj << /Kids [391 0 R 412 0 R 431 0 R 450 0 R 469 0 R ] /Count 29 /Type /Pages /Parent 488 0 R >> endobj 488 0 obj << /Kids [484 0 R 485 0 R 486 0 R 487 0 R ] /Count 137 /Type /Pages /MediaBox [ 0 0 612 792 ] >> endobj 1 0 obj << /Creator (Microsoft Word ) /CreationDate (Thursday, April 20, 2000 3:06:52 PM) /Title (PET405.PDF) /Author (Unknown) /Producer (Acrobat PDFWriter 3.02 for Windows) /Keywords () /Subject () >> endobj 3 0 obj << /Pages 488 0 R /Type /Catalog /DefaultGray 489 0 R /DefaultRGB 490 0 R >> endobj 489 0 obj [/CalGray << /WhitePoint [0.9643 1 0.8251 ] /Gamma 1.9 >> ] endobj 490 0 obj [/CalRGB << /WhitePoint [0.9643 1 0.8251 ] /Gamma [1.9 1.9 1.9 ] /Matrix [0.511 0.2903 0.0273 0.3264 0.6499 0.1279 0.1268 0.0598 0.6699 ] >> ] endobj xref 0 491 0000000000 65535 f 0000696724 00000 n 0000693210 00000 n 0000696945 00000 n 0000000672 00000 n 0000693244 00000 n 0000682483 00000 n 0000682611 00000 n 0000682731 00000 n 0000682865 00000 n 0000683952 00000 n 0000000021 00000 n 0000000649 00000 n 0000001162 00000 n 0000684232 00000 n 0000685322 00000 n 0000685598 00000 n 0000000840 00000 n 0000001139 00000 n 0000001741 00000 n 0000685725 00000 n 0000001309 00000 n 0000001718 00000 n 0000002391 00000 n 0000001887 00000 n 0000002368 00000 n 0000004720 00000 n 0000002549 00000 n 0000004696 00000 n 0000007193 00000 n 0000004878 00000 n 0000007169 00000 n 0000007928 00000 n 0000693361 00000 n 0000007351 00000 n 0000007905 00000 n 0000009995 00000 n 0000685841 00000 n 0000008087 00000 n 0000009971 00000 n 0000016132 00000 n 0000010167 00000 n 0000016108 00000 n 0000685965 00000 n 0000686088 00000 n 0000687210 00000 n 0000016254 00000 n 0000018656 00000 n 0000687485 00000 n 0000016350 00000 n 0000018632 00000 n 0000018778 00000 n 0000022225 00000 n 0000018874 00000 n 0000022201 00000 n 0000687617 00000 n 0000688706 00000 n 0000022347 00000 n 0000025691 00000 n 0000022445 00000 n 0000025667 00000 n 0000025813 00000 n 0000028770 00000 n 0000693480 00000 n 0000025911 00000 n 0000028746 00000 n 0000031561 00000 n 0000028954 00000 n 0000031537 00000 n 0000688982 00000 n 0000690112 00000 n 0000031683 00000 n 0000035968 00000 n 0000031791 00000 n 0000035944 00000 n 0000690396 00000 n 0000691523 00000 n 0000036090 00000 n 0000039214 00000 n 0000036189 00000 n 0000039190 00000 n 0000039336 00000 n 0000043302 00000 n 0000039434 00000 n 0000043278 00000 n 0000046205 00000 n 0000043474 00000 n 0000046181 00000 n 0000049479 00000 n 0000693599 00000 n 0000046391 00000 n 0000049455 00000 n 0000049601 00000 n 0000052488 00000 n 0000049697 00000 n 0000052464 00000 n 0000052610 00000 n 0000056483 00000 n 0000052706 00000 n 0000056459 00000 n 0000056606 00000 n 0000061408 00000 n 0000056703 00000 n 0000061383 00000 n 0000061533 00000 n 0000066676 00000 n 0000061630 00000 n 0000066651 00000 n 0000066801 00000 n 0000070604 00000 n 0000066898 00000 n 0000070579 00000 n 0000074286 00000 n 0000693721 00000 n 0000070790 00000 n 0000074261 00000 n 0000079162 00000 n 0000074473 00000 n 0000079137 00000 n 0000079288 00000 n 0000082518 00000 n 0000079385 00000 n 0000082493 00000 n 0000082644 00000 n 0000086312 00000 n 0000082741 00000 n 0000086287 00000 n 0000086438 00000 n 0000090828 00000 n 0000086535 00000 n 0000090803 00000 n 0000095005 00000 n 0000091015 00000 n 0000094980 00000 n 0000099837 00000 n 0000693847 00000 n 0000095192 00000 n 0000099812 00000 n 0000100874 00000 n 0000100012 00000 n 0000100850 00000 n 0000106302 00000 n 0000101037 00000 n 0000106277 00000 n 0000110471 00000 n 0000106489 00000 n 0000110446 00000 n 0000110597 00000 n 0000115587 00000 n 0000110694 00000 n 0000115562 00000 n 0000119706 00000 n 0000115776 00000 n 0000119681 00000 n 0000125099 00000 n 0000693973 00000 n 0000119881 00000 n 0000125074 00000 n 0000129322 00000 n 0000125274 00000 n 0000129297 00000 n 0000131897 00000 n 0000129497 00000 n 0000131872 00000 n 0000134728 00000 n 0000132084 00000 n 0000134703 00000 n 0000134854 00000 n 0000135828 00000 n 0000134951 00000 n 0000135804 00000 n 0000138687 00000 n 0000136017 00000 n 0000138662 00000 n 0000138813 00000 n 0000141769 00000 n 0000694099 00000 n 0000138910 00000 n 0000141744 00000 n 0000145277 00000 n 0000141944 00000 n 0000145252 00000 n 0000150292 00000 n 0000145464 00000 n 0000150267 00000 n 0000155813 00000 n 0000150467 00000 n 0000155788 00000 n 0000163609 00000 n 0000156000 00000 n 0000163584 00000 n 0000169488 00000 n 0000163784 00000 n 0000169463 00000 n 0000176386 00000 n 0000694225 00000 n 0000169663 00000 n 0000176361 00000 n 0000183999 00000 n 0000176561 00000 n 0000183974 00000 n 0000188302 00000 n 0000184174 00000 n 0000188277 00000 n 0000196812 00000 n 0000188477 00000 n 0000196787 00000 n 0000204247 00000 n 0000196987 00000 n 0000204222 00000 n 0000210462 00000 n 0000204422 00000 n 0000210437 00000 n 0000215621 00000 n 0000694351 00000 n 0000210637 00000 n 0000215596 00000 n 0000222675 00000 n 0000215796 00000 n 0000222650 00000 n 0000228835 00000 n 0000222850 00000 n 0000228810 00000 n 0000236172 00000 n 0000229010 00000 n 0000236147 00000 n 0000244100 00000 n 0000236347 00000 n 0000244075 00000 n 0000253156 00000 n 0000244275 00000 n 0000253131 00000 n 0000254540 00000 n 0000694477 00000 n 0000253331 00000 n 0000254515 00000 n 0000258882 00000 n 0000254715 00000 n 0000258857 00000 n 0000263456 00000 n 0000259057 00000 n 0000263431 00000 n 0000264175 00000 n 0000263631 00000 n 0000264151 00000 n 0000271664 00000 n 0000264338 00000 n 0000271639 00000 n 0000281853 00000 n 0000271839 00000 n 0000281828 00000 n 0000290806 00000 n 0000694603 00000 n 0000282028 00000 n 0000290781 00000 n 0000299286 00000 n 0000290981 00000 n 0000299261 00000 n 0000691808 00000 n 0000692930 00000 n 0000308969 00000 n 0000299476 00000 n 0000308944 00000 n 0000318955 00000 n 0000309144 00000 n 0000318930 00000 n 0000327815 00000 n 0000319130 00000 n 0000327790 00000 n 0000334499 00000 n 0000328005 00000 n 0000334474 00000 n 0000337449 00000 n 0000694729 00000 n 0000334689 00000 n 0000337424 00000 n 0000337575 00000 n 0000340277 00000 n 0000337672 00000 n 0000340252 00000 n 0000343374 00000 n 0000340452 00000 n 0000343349 00000 n 0000347681 00000 n 0000343561 00000 n 0000347656 00000 n 0000353205 00000 n 0000347856 00000 n 0000353180 00000 n 0000361057 00000 n 0000353392 00000 n 0000361032 00000 n 0000366897 00000 n 0000694855 00000 n 0000361232 00000 n 0000366872 00000 n 0000373816 00000 n 0000367072 00000 n 0000373791 00000 n 0000381423 00000 n 0000373991 00000 n 0000381398 00000 n 0000389671 00000 n 0000381598 00000 n 0000389646 00000 n 0000397742 00000 n 0000389846 00000 n 0000397717 00000 n 0000404143 00000 n 0000397917 00000 n 0000404118 00000 n 0000410790 00000 n 0000694981 00000 n 0000404318 00000 n 0000410765 00000 n 0000418607 00000 n 0000410965 00000 n 0000418582 00000 n 0000420016 00000 n 0000418782 00000 n 0000419991 00000 n 0000424610 00000 n 0000420205 00000 n 0000424585 00000 n 0000429162 00000 n 0000424799 00000 n 0000429137 00000 n 0000430011 00000 n 0000429337 00000 n 0000429987 00000 n 0000437518 00000 n 0000695107 00000 n 0000430174 00000 n 0000437493 00000 n 0000447692 00000 n 0000437693 00000 n 0000447667 00000 n 0000456613 00000 n 0000447867 00000 n 0000456588 00000 n 0000465140 00000 n 0000456788 00000 n 0000465115 00000 n 0000474847 00000 n 0000465330 00000 n 0000474822 00000 n 0000484749 00000 n 0000475022 00000 n 0000484724 00000 n 0000494299 00000 n 0000695233 00000 n 0000484939 00000 n 0000494274 00000 n 0000499880 00000 n 0000494474 00000 n 0000499855 00000 n 0000502351 00000 n 0000500055 00000 n 0000502326 00000 n 0000502477 00000 n 0000504257 00000 n 0000502574 00000 n 0000504232 00000 n 0000506924 00000 n 0000504446 00000 n 0000506899 00000 n 0000510276 00000 n 0000507099 00000 n 0000510251 00000 n 0000510402 00000 n 0000516221 00000 n 0000695359 00000 n 0000510499 00000 n 0000516196 00000 n 0000516347 00000 n 0000522665 00000 n 0000516444 00000 n 0000522640 00000 n 0000526584 00000 n 0000522840 00000 n 0000526559 00000 n 0000528625 00000 n 0000526759 00000 n 0000528600 00000 n 0000531224 00000 n 0000528800 00000 n 0000531199 00000 n 0000533916 00000 n 0000531413 00000 n 0000533891 00000 n 0000537307 00000 n 0000695485 00000 n 0000534105 00000 n 0000537282 00000 n 0000537433 00000 n 0000543319 00000 n 0000537530 00000 n 0000543294 00000 n 0000550182 00000 n 0000543508 00000 n 0000550157 00000 n 0000551203 00000 n 0000550357 00000 n 0000551179 00000 n 0000553269 00000 n 0000551366 00000 n 0000553244 00000 n 0000556315 00000 n 0000553444 00000 n 0000556290 00000 n 0000556441 00000 n 0000559061 00000 n 0000695611 00000 n 0000556538 00000 n 0000559036 00000 n 0000562242 00000 n 0000559236 00000 n 0000562217 00000 n 0000566407 00000 n 0000562429 00000 n 0000566382 00000 n 0000571911 00000 n 0000566582 00000 n 0000571886 00000 n 0000579286 00000 n 0000572098 00000 n 0000579261 00000 n 0000584739 00000 n 0000579461 00000 n 0000584714 00000 n 0000591556 00000 n 0000695737 00000 n 0000584914 00000 n 0000591531 00000 n 0000598864 00000 n 0000591731 00000 n 0000598839 00000 n 0000606422 00000 n 0000599039 00000 n 0000606397 00000 n 0000608260 00000 n 0000606597 00000 n 0000608235 00000 n 0000612726 00000 n 0000608435 00000 n 0000612701 00000 n 0000613818 00000 n 0000612901 00000 n 0000613794 00000 n 0000620878 00000 n 0000695863 00000 n 0000613981 00000 n 0000620853 00000 n 0000630903 00000 n 0000621053 00000 n 0000630878 00000 n 0000640382 00000 n 0000631078 00000 n 0000640357 00000 n 0000649528 00000 n 0000640557 00000 n 0000649503 00000 n 0000659333 00000 n 0000649703 00000 n 0000659308 00000 n 0000668917 00000 n 0000659523 00000 n 0000668892 00000 n 0000676297 00000 n 0000695989 00000 n 0000669107 00000 n 0000676272 00000 n 0000678383 00000 n 0000676472 00000 n 0000678358 00000 n 0000680580 00000 n 0000678557 00000 n 0000680555 00000 n 0000681700 00000 n 0000680754 00000 n 0000681676 00000 n 0000682333 00000 n 0000681874 00000 n 0000682309 00000 n 0000696107 00000 n 0000696229 00000 n 0000696356 00000 n 0000696483 00000 n 0000696602 00000 n 0000697046 00000 n 0000697132 00000 n trailer << /Size 491 /Root 3 0 R /Info 1 0 R /ID [<640207f7837cf0b0d1da8cde10559889><640207f7837cf0b0d1da8cde10559889>] >> startxref 697301 %%EOF minc-tools-2.3.00+dfsg/conversion/dcm2mnc/doc/ge-contour72-1.pdf0000644000175000000620000230175312574624760023176 0ustar stevestaff%PDF-1.1 % 1 0 obj [/CalRGB << /WhitePoint [0.9505 1 1.089] /Gamma [1.8 1.8 1.8] /Matrix [0.4497 0.2446 0.02518 0.3163 0.672 0.1412 0.1845 0.08334 0.9227] >> ] endobj 2 0 obj << /Filter [/ASCII85Decode /LZWDecode ] /Length 459 >> stream J,g]g+e/h_!_gCtO=0f)$P%cIi8Zdfc5&3j_8$7g.@L`YKUJNGBP\poR=_;Dl'P(T (7Boo^^S:71(MN]ZQX/+Cbu.lK"p74pe1T%s.DY%&\1TdJhr54.M9au6>79n6`Q:4 PbLSZTLEE(8E@'*1mg_*eTnN*;*'V3+gm-EEetX%;Bo$ur2ss*N`.-!.kG_q6GDD' dKoL!8Ka#EV,@V!\j8ZFbp6EE<9cn=N6j0nf;(&;QU6bUD')c@\ 9-d\DA=cZ0Q>gIM$$;cd2O@&a;X,Nn_aP(]I1aRc(K1^ue> gF/(+GaKo$qneLWDrQ#;5\S(\$q'LM9bYJX9N;hHO_e;>`Y"/'J:I~> endstream endobj 4 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 466 >> stream 5P8$ BaPRDbPhtN-5ⱘvHc /$JbT,L` i5MX( (e6OQue&wWV'sԎ5W69t<.\.TltaZ~P/0ZUN2+&.d#ܔS+c2Dg*:OVnNw[69_k:|Gw5n_qC{I\0"8v >kWQ 4/t"9V=O+x<:pk2p5 IkK( :\bĬj -*Bqlꚵ!/2b'G136{-+1R.&*9J <Ҳr\-t6;LU7D>K9- CϓЬ endstream endobj 5 0 obj << /Length 554 /Filter /LZWDecode >> stream P2 DC4b3aR a]`BF 3ʆ9Q(LfS9m7NgSIP !0h*/Q qtpAH PELCIl'3sDX*A kU FA3 * CqH1/u`0ҡh7Yl8b.%B&w?STt4 6tP@5U& <0hCNR#AerјG 4gYE/)NƓ܄B Pd2RhN>☒#@!p'7x&"p"\>ͣ6nЦ*"&K&2@d̓$7A$Âh@;" " ^' ,L~Ǩpr)(֤$j׆ZB6+H9?cʰ#pո,!̆QtS2 7C 872(f[jcx> /Font << /F2 7 0 R /F4 8 0 R >> >> endobj 11 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 328 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*y[@zcZ`vUmίuTo_O V.crk%f,r#|9j3'l1,^0[.+ƶ-\vΌs,+C֋u2=7r"y2ތk ~3?_#P> @K??BXA B  endstream endobj 12 0 obj << /Length 371 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1> /Font << /F2 7 0 R /F4 8 0 R /T1 14 0 R >> >> endobj 16 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 597 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*r{,*5e`앻do2k PjrJykT"|% 'eZ8\}ίT'c2Vi$UG`rr-6<NN'/ۺ:_>ޗ.9Uw{ިf3WTB.s/4X>؋8K,K@m;c <- 5zL_C{ǣ.;л+A$4?[Ce ;Jd•$qP?qv Z h"J U04I\*LN?DF2ܾ(A2-4Αd7S[7K 39L$NHkCȍ@L=CIIځ<Ǔ-ϴ}1ԭR1D3dt%+D,dNyGѕSV r2K[VUMS$TZ.UqWDob!u;w+ endstream endobj 17 0 obj << /Length 1998 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1pR 7pf>.ĭ n$r+Ƌ <#Ab!;pF,&("xŰب)>Pˋ &f))$H@K ) 3MpRNfRZ7(di h"12F0PAC(\ :jϒt`eJR$*[aѮUǐ+VAg:D":D@(Ul67m\+ ҈[ĿW{6BP(Qaʼ@T:/qJT(K;eC+[,y d\8]qgq! 7ܶWsnZދWD];@Wn֛ ak0 |kCzS(_ vDsB^幏[끍0,k૓na4޻a@˅5N n)q|kU`ppj:8hFVz]|BUJV]Z9*XUG盉 zYgPe)@܌츠d~xHN~-{m&wApk 6i5pB/vԚڂd / kQɿViSWG+qʺ+Id@@ endstream endobj 18 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F7 19 0 R /T2 20 0 R /T3 21 0 R >> >> endobj 20 0 obj << /Name /T2 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 146 /Encoding 22 0 R /CharProcs 23 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 14 11 0 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 0 28 30 26 23 30 0 14 0 0 0 37 30 30 23 30 27 23 26 0 30 39 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 0 12 33 22 20 21 21 15 16 12 21 20 29 21 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 ] >> endobj 22 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 45/G2d /G2e 48/G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 67/G43 /G44 /G45 /G46 /G47 73/G49 77/G4d /G4e /G4f /G50 /G51 /G52 /G53 /G54 86/G56 /G57 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 146/G92 ] >> endobj 23 0 obj << /G20 24 0 R /G28 25 0 R /G29 26 0 R /G2d 27 0 R /G2e 28 0 R /G30 29 0 R /G31 30 0 R /G32 31 0 R /G33 32 0 R /G34 33 0 R /G35 34 0 R /G36 35 0 R /G37 36 0 R /G38 37 0 R /G39 38 0 R /G41 39 0 R /G43 40 0 R /G44 41 0 R /G45 42 0 R /G46 43 0 R /G47 44 0 R /G49 45 0 R /G4d 46 0 R /G4e 47 0 R /G4f 48 0 R /G50 49 0 R /G51 50 0 R /G52 51 0 R /G53 52 0 R /G54 53 0 R /G56 54 0 R /G57 55 0 R /G61 56 0 R /G62 57 0 R /G63 58 0 R /G64 59 0 R /G65 60 0 R /G66 61 0 R /G67 62 0 R /G68 63 0 R /G69 64 0 R /G6c 65 0 R /G6d 66 0 R /G6e 67 0 R /G6f 68 0 R /G70 69 0 R /G71 70 0 R /G72 71 0 R /G73 72 0 R /G74 73 0 R /G75 74 0 R /G76 75 0 R /G77 76 0 R /G78 77 0 R /G79 78 0 R /G92 79 0 R >> endobj 24 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 25 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 26 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 27 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 28 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 29 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 30 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 31 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 32 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 33 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 34 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 35 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 36 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 37 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 38 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 39 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 40 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 41 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 42 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 43 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 44 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 45 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 46 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 48 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 49 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream @-CT011&h@m1y^8!C $u2I$A?x?>x> ~ umC=eY֋UoX.5bvT4ƒq}@C?AQ=Tt(F (` endstream endobj 51 0 obj << /Length 144 /Filter /LZWDecode >> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 52 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 53 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 54 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 55 0 obj << /Length 194 /Filter /LZWDecode >> stream "b B@dAGpxHm1!y\A!!CI $u2ĉ$Ap~;Q*qCӏ yTA՞Y\+ ~,`al~/:| ?-dſw(qOH#bPendstream endobj 56 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 57 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 58 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 59 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 60 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 61 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 62 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 63 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 64 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 65 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 66 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 67 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 68 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 69 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 70 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 71 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 72 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 73 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 74 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 75 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 76 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 77 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 78 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 79 0 obj << /Length 85 /Filter /LZWDecode >> stream F10Q$11- B&3lDI HHE*BI&#"I"@z=ȉ@ (` endstream endobj 21 0 obj << /Name /T3 /Type /Font /Subtype /Type3 /FontBBox [0 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 80 0 R /CharProcs 81 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 21 21 21 21 0 21 21 21 0 21 0 0 0 0 0 0 0 30 0 30 30 28 25 0 0 16 0 0 28 40 30 33 25 0 30 23 28 30 0 0 0 0 0 0 0 0 0 0 0 21 23 19 23 19 14 0 0 12 0 24 12 34 23 20 23 0 19 16 14 22 20 30 0 21 ] >> endobj 80 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 48/G30 /G31 /G32 /G33 53/G35 /G36 /G37 57/G39 65/G41 67/G43 /G44 /G45 /G46 73/G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 97/G61 /G62 /G63 /G64 /G65 /G66 105/G69 107/G6b /G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 /G77 121/G79 ] >> endobj 81 0 obj << /G20 82 0 R /G2e 83 0 R /G30 84 0 R /G31 85 0 R /G32 86 0 R /G33 87 0 R /G35 88 0 R /G36 89 0 R /G37 90 0 R /G39 91 0 R /G41 92 0 R /G43 93 0 R /G44 94 0 R /G45 95 0 R /G46 96 0 R /G49 97 0 R /G4c 98 0 R /G4d 99 0 R /G4e 100 0 R /G4f 101 0 R /G50 102 0 R /G52 103 0 R /G53 104 0 R /G54 105 0 R /G55 106 0 R /G61 107 0 R /G62 108 0 R /G63 109 0 R /G64 110 0 R /G65 111 0 R /G66 112 0 R /G69 113 0 R /G6b 114 0 R /G6c 115 0 R /G6d 116 0 R /G6e 117 0 R /G6f 118 0 R /G70 119 0 R /G72 120 0 R /G73 121 0 R /G74 122 0 R /G75 123 0 R /G76 124 0 R /G77 125 0 R /G79 126 0 R >> endobj 82 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 83 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 84 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 85 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 86 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 87 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 88 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 89 0 obj << /Length 147 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH`0v~@pP4 @@Ql ]p+'e٭T 0n&Nq(,Q@endstream endobj 90 0 obj << /Length 133 /Filter /LZWDecode >> stream "eAFCb bd Lf؁I" 89MGS,@D0 P'{?,.Lq*O=2T31?L)fU"Eendstream endobj 91 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 92 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 93 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 94 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 95 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 96 0 obj << /Length 128 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"`O~C0=LS*`bLgt>+=$Y(endstream endobj 97 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 98 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 99 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 101 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 102 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 103 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 104 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 105 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 107 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 108 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 109 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 110 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 111 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 112 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 113 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 114 0 obj << /Length 133 /Filter /LZWDecode >> stream c@#b b!" 2A3l@I 𑜄 84MGS,@D{> stream FBcACb b,TA B f؁ ".884MGS,@D}?PhT E1endstream endobj 116 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 117 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 119 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 120 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 121 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 122 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 123 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 124 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 125 0 obj << /Length 148 /Filter /LZWDecode >> stream  Z1 ",11T,2 "B$ExHDH$D""$DC$x  xM,xp燅J:>p\/=dY7>ö;?sݯB (` endstream endobj 126 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 128 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 506 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*S@̬>PBM.JtBEcm=v=DopK&loWL}?_s9.ItZwIN> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR120ӄ:RZMKIOW Qb4àZ: mp] WMgճDsVuMaAZSR3RG( )|(FH& "ԋ2@$.5abSr=.ÄW5M2cJ4J8@$46'CVyt(p 3JW1nWӵAX!I$CH? p( 9ݕd^o}lka56"pnvԬCCs}wo`&I#'I;Ԡq_rz˷Ȅ1f=(P К ' p oګ_!::C`6O{fwG*R5MwDn @(X aL0"P`@A ASBPL`*?V\Aݡ>GtP@ T$ԟf I 6F{0& UILpU3TJ9vF. /?Q N0!'AB4p^Id\C.`*N3F(@3J D#B{ #LXl F(ai-P]ce{CO KDlaYhnFɟdClh!5'lj 6( endstream endobj 130 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T6 131 0 R /T7 132 0 R >> >> endobj 131 0 obj << /Name /T6 /Type /Font /Subtype /Type3 /FontBBox [-4 -9 37 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 146 /Encoding 133 0 R /CharProcs 134 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 14 11 12 0 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 0 28 30 26 0 0 0 14 0 0 0 37 0 30 23 0 0 23 26 0 0 0 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 11 21 12 33 22 20 21 0 15 16 12 21 20 0 0 19 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 ] >> endobj 133 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c /G2d /G2e /G2f 49/G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 67/G43 /G44 /G45 73/G49 77/G4d 79/G4f /G50 83/G53 /G54 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 /G6a /G6b /G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 121/G79 /G7a 146/G92 ] >> endobj 134 0 obj << /G20 135 0 R /G28 136 0 R /G29 137 0 R /G2c 138 0 R /G2d 139 0 R /G2e 140 0 R /G2f 141 0 R /G31 142 0 R /G32 143 0 R /G33 144 0 R /G34 145 0 R /G35 146 0 R /G36 147 0 R /G37 148 0 R /G38 149 0 R /G39 150 0 R /G41 151 0 R /G43 152 0 R /G44 153 0 R /G45 154 0 R /G49 155 0 R /G4d 156 0 R /G4f 157 0 R /G50 158 0 R /G53 159 0 R /G54 160 0 R /G61 161 0 R /G62 162 0 R /G63 163 0 R /G64 164 0 R /G65 165 0 R /G66 166 0 R /G67 167 0 R /G68 168 0 R /G69 169 0 R /G6a 170 0 R /G6b 171 0 R /G6c 172 0 R /G6d 173 0 R /G6e 174 0 R /G6f 175 0 R /G70 176 0 R /G72 177 0 R /G73 178 0 R /G74 179 0 R /G75 180 0 R /G76 181 0 R /G79 182 0 R /G7a 183 0 R /G92 184 0 R >> endobj 135 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 136 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 137 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 138 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 139 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 140 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 141 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 142 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 143 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 144 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 145 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 146 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 147 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 148 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 149 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 150 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 151 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 152 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 153 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 154 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 155 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 156 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 158 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 160 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 161 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 162 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 163 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 164 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 165 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 166 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 167 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 168 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 169 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 170 0 obj << /Length 119 /Filter /LZWDecode >> stream F"Z4DdAGe xHm1!y\A!C $u2ĉ$A P'EBj:V8p@"DY(endstream endobj 171 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 172 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 173 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 174 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 175 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 176 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 177 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 178 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 179 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 180 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 181 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 182 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 183 0 obj << /Length 122 /Filter /LZWDecode >> stream G"c 3  8#xDͰ&/+D.>B(I4@t9LI@=hF?TU_m!ߓ<aendstream endobj 184 0 obj << /Length 85 /Filter /LZWDecode >> stream F10Q$11- B&3lDI HHE*BI&#"I"@z=ȉ@ (` endstream endobj 132 0 obj << /Name /T7 /Type /Font /Subtype /Type3 /FontBBox [0 -1 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 90 /Encoding 185 0 R /CharProcs 186 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 14 11 12 0 0 21 21 21 21 21 0 21 0 0 0 0 0 0 0 0 30 0 30 30 28 25 33 33 16 0 0 28 40 30 33 25 0 30 23 28 30 30 0 30 0 28 ] >> endobj 185 0 obj << /Type /Encoding /Differences [32/G20 45/G2d /G2e /G2f 50/G32 /G33 /G34 /G35 /G36 56/G38 65/G41 67/G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 /G56 88/G58 90/G5a ] >> endobj 186 0 obj << /G20 187 0 R /G2d 188 0 R /G2e 189 0 R /G2f 190 0 R /G32 191 0 R /G33 192 0 R /G34 193 0 R /G35 194 0 R /G36 195 0 R /G38 196 0 R /G41 197 0 R /G43 198 0 R /G44 199 0 R /G45 200 0 R /G46 201 0 R /G47 202 0 R /G48 203 0 R /G49 204 0 R /G4c 205 0 R /G4d 206 0 R /G4e 207 0 R /G4f 208 0 R /G50 209 0 R /G52 210 0 R /G53 211 0 R /G54 212 0 R /G55 213 0 R /G56 214 0 R /G58 215 0 R /G5a 216 0 R >> endobj 187 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 188 0 obj << /Length 77 /Filter /LZWDecode >> stream Fb Apa116 &3lDI @484MGS,DD'SD b0endstream endobj 189 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 190 0 obj << /Length 119 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$  pN2xT*7Rc+HrxX"DY(endstream endobj 191 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 192 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 193 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 194 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 195 0 obj << /Length 147 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH`0v~@pP4 @@Ql ]p+'e٭T 0n&Nq(,Q@endstream endobj 196 0 obj << /Length 152 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHp/ @oU0LQ@`0!wcۮp0,6d Fendstream endobj 197 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 198 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 199 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 200 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 201 0 obj << /Length 128 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"`O~C0=LS*`bLgt>+=$Y(endstream endobj 202 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 203 0 obj << /Length 121 /Filter /LZWDecode >> stream bc B@dAG`Ѓ$B$ąxLD/$C"@IeHp}452Mj "RiպrZB (Ġ endstream endobj 204 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 205 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 206 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 208 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 209 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 210 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 211 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 212 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 214 0 obj << /Length 170 /Filter /LZWDecode >> stream  Z1@!11T9 "B$ExHDHȈE IeH `F pQx||߇i]oeߏM㵠O/ ~1׏d Fendstream endobj 215 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ F=endstream endobj 216 0 obj << /Length 160 /Filter /LZWDecode >> stream c@03  8CxDͰ&/+FD.>B(I4@t9LI@ tp3xUX @Q 7m*/]7~ '" A"ǁE|endstream endobj 218 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 474 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLaؼagLk%ve\JhR^&o˖6,ٝ-nNE_~Xu Apj5>_e۵)Vx*CoiV 6̧V`}8]EtyG]79.!7;R:/x><PZ2l Bj% ½ M:Ð61"đDECd;CQ1B'BAq=r+###%6rcS'B2B3(,RU)@-2//!+B3?SB5C3d7>9)Ӥ=;Ds=#S?L> c3P-DTdAEb endstream endobj 219 0 obj << /Length 1213 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1CvsrurZd0ĔO $CY6G#,<̈'Gbck>5H7ga!-1mE*r#=ٍw AsLPP2$ނk/e *&5"@S9*> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T8 222 0 R >> >> endobj 224 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 437 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadXz%c9hUK- 0˦Sۥݰhf"=Ur6FN1E{m]@4T~{ӣ^7 i7SE5s;ļ^wYKz]]?Io OmW0׫u>|o_?"90C?pc> 63 6P4 #0'210#]2B/0b-1.S5;¹"1=!Hr#%#?2C%I2c'?) )*Jr ,JKﴵ-ʓ ̫l<6S|+ endstream endobj 225 0 obj << /Length 1046 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1Pa.!8] w5" v9a]" , Fpw.2aLUh6 %{cf2]~ڕkwpʥ͎p3\yc9v"b\P7  Փx A71;Ap\""*:0C&+K%7wEf[]bS:l\l@&@CcaԚoó!9xNU:+*N::"R 0l)+[}gY Iq6-$&4PQ  endstream endobj 226 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T9 227 0 R >> >> endobj 229 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 664 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*̆{_XggK앻dNmS}t9mBU=L/4Ʃ8<ƫ&w~S-활jߒgcfOEi O3ћwoˍ}Ł#\{Ksp2ބG:2[y.3xm8+[=׾m랕/ ӂ7+;X6 cPkj7+0 .,j4:it>ms797IbLKlVNt'k3 Jf+THpd~B\I:$2"W7O;,3*g/ɰ| 3$É=,,n4L:T !BS=1 L_ 8o5*s}"+4H*YJL:0ԥ(/3lsA0Hr0"/.9Q* B/jIZJq[XUBP/oZGLucNE2QZǕYhkTUn]-*ڎJRփ!e4',-^לĨto)u*[S8`F!axV3"w+wb 3c6c5#O4B endstream endobj 230 0 obj << /Length 4733 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1ӸA loM6庞m;[l|Gl\c hkqVmf{'3o> NqV[:rkp|ZuHiRzm;5r6\Ɗ6mŢܠ 6~MBݤs%֪IA2%uೌk)rb ʢ?~ՠe\)|A~ɑ?%$P΍ 'Nu`|VC([9PAB<(B |=0B$`B|Rk*#jR Mk>cGId`yt*XN2L#d S#: RBi ƈnepB5&kvdM 喒FYᩖŭypե(,ZX,prK5H^ ]iݦSf.&IrNgĔ&kK(^\HoUGY)s.rJ "lI:~&9>b@PB}fݾ|Nfr$Z3ItJ^?PC vG3WA/D QTzYqFxbZ//J7V*qMt|ոOW1E¶DoN^3jC>"ЉPh%zhNV!{XTUةId |86j)ZrjlHVse&*(MV5$ P= -qDA̞AF vfʋhevn\'?q֣.19ss۝ޘLϵ2Nב9jDrwBZW}緭޻Ew`|ohCnRsiJiY}ȈSLs%Zt*s %  _d/ʠԌe[.*t*S<3J"j#P gT+9K׳k\cX- _)\4zf @{C:}fN[; PG0.6͔G76lگvzgC弜k2=qqgZAcim:]/t:Lՠ~ʚ-j,R&X!F `Tn)f^X 3-\nnxu)O!~0cRU8)SQ53FS*u\XԸ$L*05 =aQDZ2IvQ=#7=rHs{$c%TzS!6y2Nq\0Ma'DDB.uL)7(Dh]?O(k6#64.i+zeZsG`5{%ػG_c'g)ceMe}arF[sk:Ne?=o^4[سi[Ok 6 [=#;'*DE3lߐUt={TCB韐5/>%6CoeNr?$?g~Fw)G_-ҍ-K4m>mNޗ-kʴ< dl”i?؋ @HVc @5.BFi`gfix'D/ܰME>2P`o7"n!',$کL,Sbg-= b,'&b,.KKkP8/_  3 ;B6mbI $ tІа0 VmF i pQqNp{vS밪k@Zmا %C|P V-v ߐ'1#h" ?Q$C GNpR !`Cֺ Pѽ!nτB8!-|-"4mQE LjO<q a иurs ү=.&!1ܱ=%cbQ3!2=ou"w$ԥ#2 % Q&#&r;Rw$/'qD"=1/cQ"o'I +M9i;PsETKEnsD '/Ɇ88fo|wHE nJ IH)JԔox4H~ϖ4렷P{L/ԫMnGONsI LwPSHmHR?Z‘m+9/2Q,BBRBB?Tt5.Ѥq/PVKH hL4un0tUwVkîj2E4LY-Z 8po5P6Z$M5[0[g['5 0;1]PՑ'^)`y2[_/`1`rCQ U2R +U&^*"DoNe5x1}SсA)CGJPeY/=CD)^"Ֆ"_j.yb%,20XڣB"k-k ?.S_L5ә$gJĖk's8ro6V^ WkRYjKi%:o&Ulґ090p[M(n"wq)J5:*J'?V6;RMvB#/ GSeuAA;BbTfSf.NagI<#4674mNShW0nLE3MKM;o-= \_4s4pӀ2+8slcXJ?gE]I6w6 8.x2o>74QU0tRϱom,p&ufx"7|veBׅuT7ky2,Nv}EgQJ;O郘ԁ|6ڪZf||ڗT砳Ow8jmLX gDpo|x: Y+9A/w#> WG=;9b+bɷR 9(sN/7nU3I/;VH @ Y+lDD "]Xm%te5>4D " Y1Elydə59١tRT8IbX/L?bȹ ŗ9w]вטQ_'#{^Klip, njx%Bx<#S*ddk:ebffjoy_0MPEEа7oN^(LPNMI, V `6 H@ 0-f#SâpȊ n0`  1 @ Z "f[ `(@`2"0Sc 0whkJQA`s u':N%ZRڵ:4 ۔xZd']V pa"JZ=ibgu›-Pj- a5ۻP P`%.Ü{ `k[ؕZ)Н#{X[/ “e& b endstream endobj 231 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F7 19 0 R /F25 232 0 R /T10 233 0 R >> >> endobj 237 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 686 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*{_KV$k Yl1N&Y+p}&c2˦69}W,$ lR)I@2U =צw;>fj%matLƲ)vZyWi9MqfQA%_s;ԇ3KwOz1 u;öѪӬ浲ӱ.r26/D]:DB;-Qt0jTsMpc;z/K8|@| HyP3ACU%KFӭEtT>}Ֆxd\Rk<08\[l6yZ0+ CR7UyX#Tx*םov~;RvCFdvV,dWe6Zebfykx endstream endobj 238 0 obj << /Length 2580 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1SvmZ-8nJp/2$:(: #79:ip#L$`!DZP4 㺪:FaJZquG:H4 |3LC4(b jd/!D("p:@"6#p sb5atAfQzBtm3%%! UZЋ5hUt _d/xMTw*Jc|8GC` #46 `@1Z:#cSXeI;ALoshk+$R'>3)U,L2 ݐGs@2#1P\ C(" -+5!F!hSv'EkFVAhn cH4AP" , ,rHϖ9x\u}I"H8] MfP/pnV+c8Nʐu<u6!pu^B.0 1^$4@Tk;(&5skUB 13RT/s+;S9Oj-~;oV4u!""Sl(!sa'M zQa ߻d$큈8")#\]BJI3mn.YL !*2/*koC_)L˙w'&4|If>$'ʨ>9B{`P!^BTX=ifޠ1d]K.ESYF@仓Oy-Ks(P]>ϋ`|!B:mdf,WeT)QU42]$z4&y5 KZ>pcJE 7Z%Fi}SI{2n+SmVCI/{@c"hӜNJaWS|eΩ3&i@XOGl a`CCi+i pi@$UOp`R*kƹ7xK/=q-_A16<v(n-lp50xB:@'> /Font << /F2 7 0 R /F4 8 0 R /T15 240 0 R >> >> endobj 242 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 676 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*{=+T]kȨZ Ɓs)ee_+|f-T$*c~0%Ծ]t7t)T*Ynz='}3;6 KL\Lg[jWͫ2}6ևή2yayg?ό{oιe`n*֪Rک--OALH8F6f7)3 =@+ %đ z 4ptE|<ĸ %QF1*/{6ED{DKW* - 9 Ah63\J37/g7M`"L=Os&B1˻BP4^>3YO*k'nQB.1.(Ui-õ%ITA]"4D "RUFXR&mCY GP5i*d^'OӮFѣ7M^ޗzO`s 8`npFa|bt*/2cov/c1JNOehRe( endstream endobj 243 0 obj << /Length 2903 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR10R A)rH$ؤ˪THܩ "#2BnԳ!*.f(2'R|* *Cf08S0xR@5͡4 *1lx:@1 7F93ل4p@0ơxj6+ʽMb 숵kܴ 13C42:c9c8a@b,=vA@9׳9 /Ls(A5M8[֤;3fTH fhRUU#, 4M3S;K [.Lm0ҔÈ$DT{M`5FD$[#9U%':y# \)h A7INL+#8d`@D IFM@B0iMEJ $" $Ik:@ؓ>V2pE??b OAWb, 7C+˧yxr1,bn7XJ88c sq3ƚ 8-[qp7ߍ09{,ɞL 'GGRdzQ GTd(0 ,Y'ܖ2ÝUrFK_Ä?nT:nz%&*q'[c#ԥxN$/7bðiFL:uz%T9_ڕ@߶6k ľs.wV ɉA 2 <'im+e4XYچpU6͈մX .'ge])7E@R`lFi*ef>؎joѴ87P5v 0YUGeg* p!DU? 0,@<->ZoM,t @E%c;&F."Eve !/4~(xkc.=Fc>k In(K=e`L``Pkdב-X-~AM(9~4JVф(0roR?P)1Q^Ԑ 3ChS#K{!'Uhe`7X+z/Op C rOnj48vdX%SH Ϡwn"na+FG u R3~m\Abrc<,'}ǒ><1V e|AC% #'pK쫋م{\W @ܡ *qFnk&[2"ff(o*~QIwN"9ݡŪeUxB 5rgDə(o^ 8$~A1[JM1^ NYˣ\ r@– XC|:ElH22 wB (NDJjh3_64=$y5P-i4Q35ug'b g?{>ג^VbÌwFB8(FgF*ꪹl|X \ \*b.x/FgFV@Pn #lV,Hy@PGt"|* ˰Fo^" lM~MX*E"VȼMRh)h6"2 G'"|EH(6zFgO˜L"[O*SdNf˰D{P JdL,KMz<~l1 onPhIX  > /Font << /F2 7 0 R /F4 8 0 R /T16 245 0 R >> >> endobj 247 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 886 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLad'4jLvإǪ즁Iȯ_|p[^3nKff'+ZT:<NX*;ga:Pd6DC oo5n.ee&v0LsjIxto϶fo;1lr.?:դk& 6r# .+43uNV7Cd{EϓGVΩ1ѶU"S\/1Je±-.CU#<ŅfFqzݩ/ͥgy^Voxԡy>yꏉ~O( endstream endobj 248 0 obj << /Length 4193 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1M NHr%PZApNLAf̨H*Qi/$xN%Sc"hjNAm]!ѰmiKlPiE4©06*NV}b4 ?I8 4ԔYAi(B 7, 9Sޠxjeb)6148 5=Xܡ7c3 HMcOڳgY^Ah8@؝]X `2Yeoj?kb*9c06e t#J 䆁uf(e 6_,uW2eۋcˆJ3S)rpw$N۫H7 :x9A9@^hɋVJPVUӝ *`Tj ue`26| A (TA4@C:JdmU c//ͽ3zo͊t7,{@( +3&AiBqNyƛL PʂxJ5T,' b't Ii+;|zJo`0 t{7)e1"~[/2yJowLY2 b/*`Z뉑8 <2G| <sTͲ9) H ˄9.5rWt*Z}c,t Tm2է.T21pJPbt^w b54XZQAI)E, \h KM;G \n/O#m10bUjaB+\j+9fsM?CMa!ѤƽT6KXyTmRp $Ml*qʵfz]6o }6FJ kS*bF JU"F6 4E%=1BiA89W|դr9vglxo'd+DN|NC>*Q^s ýۚnh*&TGXW 4mD!b'1\ƨj 9E|`y/V/& >bM5`*&UUUU0r6jd C. ˗ x,p_K +›q`x: TP0vXd&< JX+/V'\W]˄#YnaњЙ CsIcR-Jڵܝxk {QƦ3inrCH5WPc <ܲI,t~`e@9KSj<$qf7Q=6yOSk3W4Cm|- "pr2҄WnvIpZxfKKMK-p[`T84eZJ|6sn;D;`Uk3R޴;%CjAVt[M(NH~e'L i>̡BXc~2 F UsE"mp 4{+4qQ-ޏ>}d奪04ۃ CUP a{tS-wr@7]6~>R[E.!&wX\WbEQkY2H68DZ7;yǓղ09]TiA\Ԏ/FE@խS1y9]vĉ@TC:D9k/ k69'C/2 8a('ѽsNؕ>q'ZC=hD#U/Sx| (ڬ'k5j#1TV;(+gVծY^_ݫÓOT`t6l-Gtj* O b^ *ta)4LeN:6B.'KKN\vƨ/NETeHvv:KD.rN؟R#.V'lm~pkjv ]g¯ȕ 2pr<|K|'dGl І:IQNqnP6iRjJIMh艢.\FPՉ.e2f(D^ m  O(*T s Q D^&qd_ Q(Dp  &]rRG) F c20Ir|YLfbg:'I(I31w0҇ĉ2lj2W+o9lFyzmY?&P&c@6]sxY@.yM2@)g~bb= +@$ @%t0~SP< kX dRmE?۱I'q@EztƬDIhzSzb,a$T-2OcD ,&,Z^*V"U`f* O|s23 œfj/ʝT2.9)C~qD9Mh{TZh#s6P YTr"-:Ңq; gFTRlmrxFA.C~g(Vg?K CPGjoRu2NpW]N `؛Ǭfls**Nu]󪝕2ZP,N(2ankʘkeB)ng1Ba-p%-:ĿNUg,5HЇ^Nk53&ȣ' UK\SGeWGN$+/ 'o1i[WCtZn"#KRب&+jt'l6pckfGAFStR LD$K4S q O“t Kϒ @lͩ8;Yx#6yN =s$BN[BJ(B]j8T}c;_SMpb@ ѧ8v-Hϧ6f:*'Y jjF ytj@s WG tt;b^Q 8V9#\!;sh苕1psqnJw^M ZRfJ 3kro T|\$ Fm(Wh E'A LB'(7w\a@B>4Ƃ eGupUZ?n5"`$HX$ qÒ,@ 6OFō ٠'Rqx1]-hU(56D'@>4H4Wz^Xllb*zV wPWr.od|ρk.;" /]HO"0e"p"I~Ð돓2u5,ɶ BIeg 24" endstream endobj 249 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T17 250 0 R >> >> endobj 250 0 obj << /Name /T17 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 48 /LastChar 49 /Encoding 251 0 R /CharProcs 252 0 R /Widths [21 21 ] >> endobj 251 0 obj << /Type /Encoding /Differences [48/G30 /G31 ] >> endobj 252 0 obj << /G30 253 0 R /G31 254 0 R >> endobj 253 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 254 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 256 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 477 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*@V5 {TW-6}oG%%S`R&te/ܬ<&D,(ZXl:pS9s1Uƒߣ@N 2:톺/Ln{T{L XyX̊A]痪>_sro^m_yWC.of} G {hns?i39 ۗ&1l7sZBDTRL[EbFojF\rGmzEh~Hm$Io)JhJKkl¦Ls$Msdt+_;N;> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@bMrH$ؤp?[tN*;p9( p2qyB P!*pChZt*'R| !hn J Hb4: R`@; HM8j1 (@: ȂQ;ɸ`PLH(I,SIT/A,( 9709 blmu"C<6 8n~A1#fJkM"&9q&rdI.dyS{ :)y|°p~=CW#`#S~O15 SQF ',Hq-F(ie޴GrIy&APVd6 3$Om6=VS{jqeкJ]x?W8dxXj6"0hMҾ1&XtM.U9Wj HVQ5g:+8lD 쭢BD!$sO%4^jk p@E x BA endstream endobj 258 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T18 259 0 R >> >> endobj 261 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 546 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadX=\( ^'oR"~P(HZa؜UciA',&>5 YX+-sKmKw;lns]NR]]es)3In2W&e`Y׹{gфLtw'xofSOnv:sA|^HNM75R>Լ/Nި=;i8kl , 1"1qC1b8߸l04 BƑt_>tpwI81  @o+&0 2Oî6$( R n-];EU=EM?DA:#OC4CEE3c-G1IG/J0KRGO"t!P4"!Q,BU-U5OW,uYI)[K%XEu[n] endstream endobj 262 0 obj << /Length 1276 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@bЌ!m>O@Hn审D@ D߆p1"qd]/!K k'1{"Ʉ .̳bݡ1*!sc &ჶèa-tv*;p/K ) $IBtNm8hΊ귮4 *709 10Ch7b2cH44T03 cx8 MERAHZkãK&  5h2H_X' ;)5(6 #^Cx~4_*6 :>_W@8`# 1գH #e 9cM8u&f!E/` ñ8;գ۶ky޵4xIP<9fvO %} # è_ܥ^p\j9z5XzTvb2ϩ: D meٵ[P{D+[\$ԻFO5h3c«}vGO `;ݕ.eT9_⇟VinM;Y-f#p|Q7h#8ap6P6D3ָ:[P`kR0́9|[NT1]ʥ*/9 (e %!;4tY"BQ' )T"8#dwO'𙸣BhP `u j1C$LВQlx{ қ**pV.ݒg8 dT4/Xk+Q5VNi|yViJl"̫EVm![(,iܹͪځ/mp_n C;vA:E 4TU P)F dEJSI8lA9 &[k4ahAuv$q'*֮ʛy q endstream endobj 263 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T19 264 0 R >> >> endobj 264 0 obj << /Name /T19 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 50 /Encoding 265 0 R /CharProcs 266 0 R /Widths [21 21 ] >> endobj 265 0 obj << /Type /Encoding /Differences [49/G31 /G32 ] >> endobj 266 0 obj << /G31 267 0 R /G32 268 0 R >> endobj 267 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 268 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 270 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 842 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadg̤*nEx)Tg6T>!}Ys{&5#enS~=C5kR)p]܆P]q0n:6aBgx.v6v{8>v~._ٙnhx,"- 5:i變 O7/t KؽP"޹<Ů3>pCo ]*R|3i$ePrYA=38RUTtgiV]L=Z\IMLvESTUlHW8@wADZ|6RGv5^M\(7tmn{8Y vn0B8|2N-`mم8FU> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1|he 5 Ku Ħ"ؓ3 qfyXAlQ!fI si 0P'0(UOf Kd*Ei.3~EImE#voq=R,<صVi/% R8dxujEnV,:4j0Q&TqBnt:brۖ;th7~ %P0, !/9-Q=.2qT{fҚsPjI !,ĞD+(!wHb56Ϥoagj*m=<s+~g&8ӲeWMz0˻$ ?:G'{'yCedֳ/-;՗n]Y$+G@$;. >)l`!yG{59ddJr͠KJFkԗCV(^:<Ҟ@~J2 * _%tz.0$ۤ(:`ݠ܁$i"bnCbp (lፌwhl`ʞluɬLmFr殱WNK ėD"c(.6`u ȿj!ĠIhC6\+jLL PfRpx(0gap@(u*cLm#mIhˆ"'E LGRc4"%ZN # C B(BPIS@ `" zPO"/ X-J6F"^L؃Pс*" 6cjl~Lc&KQ6 `g*Z?Q1=1b6$1eQq #HBvq"#1-`k-q4LO- $S!!?%!rV&d&rC'Qr5#!"@A(L_} %R'l-ң&s*()&2HDz_'#O@[{,-2jR(-@nR]$+rdGRM!$RϿ-(S Q/02_ѹ1.r21S"212C/@Ws?2!)j9 q1495.5#_5/+.742.R+5s/T\{&]7IBF;7i858,cK:2dRk6r/.r9S,n*/F=3,C%j39rQ>s-D9SY:,br63)3-M?7 (s1+y0<5p? ?15mm@r=R45qA3CT* ?(5r4I=NW-1C?=RVF^$pyD3EcRG2GSt$b.dTUD6/FF$9ԓFzvgHdw!GkF$u')x4>BtBVߔF'{>TD"5>Cc?Gc@THqAK4N*%AME\N8p'MQ!Q\!QavA3R(L!5GlI>@ eI3 ,UgVgTW[i&VQOB+S3IVND[*($1Y4DF7"q"F4fPb KK.E]UW!KcS* lr PKS;ǃ! ,  dHQ &5d"LBfB_cD"RIZ ZCWvϭT1X$Ճg@$AF㒇$WH3=ibC'Vj5>D_7k)hjbBk LsQ֥Tv޹֮EZ!G!BShb'CL\gʹh Y1OY+YAZIZCI`2k[v /a5Pļ>r{Gbtgg@J(QvEel?(6jZrϾ?$ki1OR6wS~pqxgyWk֔Ȫ6zz֢/(4N{Br7Kyr6poW}/+Wznk;xpoqu>BꀃM$!UV73XkSTkX1{!uWQyXqux[˝+ PrQSbp$[)y8i!rVTD21>oX!UqCgW2iod /\N}sv!'9*]*b*Du-!VXpСL,Gu[w`*%z/VosxQgxKj`p$ yd306#HҚkj#\9'{y$;evYAjsE$tvÊ8m dLQ;kAoI!&87#g9?ij_x8!$$Ŋ!fCrV8O#M'tB;cCcP:1vQ6 ¢H f1ZE+#~YB)#1kkYWj(^#ZyvJ'YAbO6)7)_~Te!z C:O\ٯv75]Veq|&.#&xX7_4BQ5)`qXNK;3Ic)LLaVS0`efo{mrmdu`ww__gYDז5)TE>[IIG5id&}wk}vW>쩤(C|Nq5Z5;抻O[YxҚ55HʛV)3;'zZX!$8)مڕ)yӼxkMq][z%x'5=r#uuaĸ#zO44b͝$v- ˌuyvkZSolw#W;WT.w::5$W~R$|!<[7 [)<|u|!<ǻT/:Ͷ2KJVrWZ(= ǒ!{\YCF$4rv '<+z$}b&ǗA(=55>%~;`v}ѽ%'VuSxyg[y#UV U{W2j5C{xgޅc.[>[y]]#~{:)1[7<0;xwiApĻ99ޏPd C eb ]y}i ޿VӞs9vWӄT6AC- !c FSie PHXl4 F"e"BapьB$ NS7BY> /Font << /F2 7 0 R /F4 8 0 R /T20 273 0 R >> >> endobj 273 0 obj << /Name /T20 /Type /Font /Subtype /Type3 /FontBBox [2 0 17 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 51 /Encoding 275 0 R /CharProcs 276 0 R /Widths [21 0 21 ] >> endobj 275 0 obj << /Type /Encoding /Differences [49/G31 51/G33 ] >> endobj 276 0 obj << /G31 277 0 R /G33 278 0 R >> endobj 277 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 278 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 280 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 846 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*J{_XlSXe,V4[,6dTSYԋʁ@-ոfW298~:q\vYd Bfp3-kImmrES;;]_HupKV'I}oO9TnTPleS Ͷֱo\\R,U,RוFw-WTpٗF_%5ԵhRv\WWrJ ̕'SɀMJN(ÒWՎYtUe#JD$kذdr:,5IKCK9n5#W ޗkX/'۲kCUGгə&1]$)nl8;=cg6}vm;'j h5$75sf rr)«mWME)3˘>U=SMSu]}Oklv endstream endobj 281 0 obj << /Length 4151 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!B37hJ\Jp/(I4@(@4pR|~ C*x6 UM#4HtZVFxuרAf(nKi}f"f pP`PU"Q %آ +6"2lAy햔o7N@msS,J\.[u%JR8|nTRJsIC9s&F0b%cK08B܉$,$C 6.؎[؆SPWbK 5 *zCɻ̈́ nHb& E2C"kI"Wf wx;Sg{\<֎_v i-@ L)!Wk>yHO#a 9kZꩺ Zr1kf 7Ot2Kv];}rIz-3 š[ϝjD)F~GbW=* e,XrOʏu6TWE0H H:Projcl)L$tOY#d]hiCQ8nJd <3\2}P-+촯JAQS.koFs>h_tx}&>H;X7GF 07#pE{uVP0 (/UzB?v}"cy[ 2ꁬE'ybia|1 ͼYLuaR75Y#6ߗ(s= [6aWp6+f5?mv١v3Ρ bQ`ɕ® PL)0!nugl*P>4A 0g:d+c,Hq\ɳ-f5}fz_F:sC1P̛0  M?$:㆙RJ.C7[OCReQ4>Q A$nͰR$1LLf'v&FfF=D )p-Yus^5JX\4#4K>eq{ fbD -6jL22 Ì=*Sztc"2`M`4>rVβZ2^~t(-&@Jhi/@x9!"5f}MK'CR>DGTNg FeF $/,&,s.Gkɠu,h k2J$X/drgqT-I@֣l4c bQ*%SKtFTdp\]5!J$6$^P^Q^V 6Nrg. ̺v]Nd'̾@G 7ybMupBVfZY:`Vjg32i:۬ PBnmPR0&BeV, q~#_u("L̬x ƘHTXlQj_r> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T21 283 0 R >> >> endobj 283 0 obj << /Name /T21 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 52 /Encoding 284 0 R /CharProcs 285 0 R /Widths [21 0 0 21 ] >> endobj 284 0 obj << /Type /Encoding /Differences [49/G31 52/G34 ] >> endobj 285 0 obj << /G31 286 0 R /G34 287 0 R >> endobj 286 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 287 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 289 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 713 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadg\h&2yT]1ٯT5̭<&WS߱wO)Xٲ٘o9, =Ij3]U+zVP7X&{mc,2[p8;V)Ωfy%Fɦx|^?'e nNQ|xF?ڧNR_2J LC2-3{04 00BB|/ͼ8dIQz >-QheRotU 'Ž hHCS\1Kql'ӢJ>(k=3BDNT :Cw't9Q4P@ \F1I/|o51nehm  endstream endobj 290 0 obj << /Length 3791 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1-@a Aw<}plM&S,6A"5=p龈va*$DHa$}嚐z2|֚k.^ e_^\(쌃`r zHPc{ϸձMDO&B>R$DEF#䌊V;2CgPZ(!HJY+%\jJCk ܘrUm LRHL-R&bɹl^Tp$bv9,ElMB %l;`J&ݑQtȡКe4I>aPC: !9 Xk孃`d;Da#ZFs:_L@E4"LJV'#$nG'n6Pi5%4cdB)<S)Ȫ,&҃4MI 4|rL+"墝`H5nj1cQfAO<(E?T% 蒝 lQ Dz1%Q0 ( tԃdpjM[.Hj0RM-)1S=P%DU[_f,>ʿ*Uk=6ڙU<UKm w6G5G̺J@ l)XuY@Tl GrAj$D,Mf#odlOƮۛG3Q*V) >UvSakjY+mQ3ټ%3aRE0ϑlU;sEҭSSV5FJ[]=|(M򪫐i~(` ?$u;m/"xz[MkiT h-vֱfǛ 5#FVlYm.@Иp\ XnTaخIZԫd&(ӕ=wbvPȨPt u^r !@BtW0΋E,Ua2vg 6Vi8 Ssr m{P͗lTt$mPw7Tl[7); \0imFrY 0f޹H@פ6w+nmtM;MSR4knRtuZYkE AM X"r; kedKX7#2m֮Fz Bq@9'ܳP}\9<]UW_+v.چnR@ wȑWz;Vi<"#cQV4P{i͖B?Fed2_ːI%ϚڳSL䏥 ܚe$y- gV' Oޛc8au㨭o6TA#g6.rǘ~ e*ﱷlTyߜR/)F5DI/LXo/pH>N& 8RN, PHь:&ߍ,M0hS :?Bd? &cH]/_ j׎r _M#PE$; ź, ,b0h S  odP pD-ʉ+ NN# I`ݰ.pl8'dF)kڠJIJJ 퀿Lc0$: Wm],oPk BIHH=%O'KxSڰPe1n0fjN< &J *+ ̯j6HLъpI~S oW- qDkh^ᑙ JpOI"H~* ǏH^P( H k]د,KMkЦr !hf2e02!Pr*!RҞ" x^,PBRЧ*r-',qX-6r ,W"[.RC*O~-.!3 ,WN%!-0/q1S -I HBR (t@@Ʉ ,S,zpH!$R-x'f*# Fh Ϟjqk./zd ** G `YG.]) j賜<&ثTTobF`/H )fQ>@baU< ]lq#Tr Thڍ`@Xp)` "c:(6q:J+xD IFmFTc)Ntw)r gH33|ԃGFe25ȑF\,2} CyKRIf J4Hs4+LиII)M39m2qfR#)[}An:5ABOYHQ(0Pmji%÷9 v:.a6%G"%2h <9 gWcg&XpHve TsrNHŖ + W"34{Ufp P)bT/Mg/:u^h(be##\ľtsqHT6d_`UKSbuU`DjWupfgaVX U(T9JS[h%dC~9Gd3,5HDKYe~QHbSN(iVXdc,>" @*Db endstream endobj 291 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T22 292 0 R >> >> endobj 292 0 obj << /Name /T22 /Type /Font /Subtype /Type3 /FontBBox [2 0 18 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 53 /Encoding 293 0 R /CharProcs 294 0 R /Widths [21 0 0 0 21 ] >> endobj 293 0 obj << /Type /Encoding /Differences [49/G31 53/G35 ] >> endobj 294 0 obj << /G31 295 0 R /G35 296 0 R >> endobj 295 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 296 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 298 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 632 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nL uI4*ĥeii\ec]n ?aIw]_|PrR|+IͶr֥ MC*y*~kWbJ岳`=wՍ?5W./xn/7u=FN5a[Գۇs$k8+n / Բll\@  cCLk:nD75D?B) t^dDN.|J0 CBKc[˜O4- -2u@3G",=ICkh6ӬN4<3!R S9őz#:UMBD-qGLM]>~,DF-!3C+2MJC6mGqȵ6uUgE҆: Bl[v_qZ|x$MB)`b+ endstream endobj 299 0 obj << /Length 2093 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@bAMj#!l4Ỽ bx c`9( " (7H"p@4tc8 㒤2R*k"2LrU0#4Ez5!4`R|7N  LL57,f9p#2K$Q@$J@: <2+ k+'#t*M?"!ޞPA@"LHd%MgZ$9#l J0c@ӐA8!@v,x3@6H2HҪ;5 @^w-Z, ۯOUd X7wrR932._0aJ@H3J%`*QJCRI9 p fD"4my\%UiIKsT9k%M#quCvC\"£8A'UQrzA\AZI)LhxО[0pZ lͫ pLwע_Z\r6(.ir59`,c 'dYo ~6bƝr$~,-"hZ"Ci`5>BZ__]V<hlU·@lDC»XB,hɴw ׀et'ӶNbHn]ۋiyŢW=s/D!+_u-08>Zyw~p.S _G[.X2]v7$7 ph!y/'2$ `!1*:IyYzLA6W*|V֮G!h5Ô QqM^i6aЮC`dw+;0Pyx(JTPi| Ct NpeeEpϗ AN47_ݢMf*40Z'EBYK~I/O$t~JlͲ3 49f= $,eKL9Bk"LJ,n;D+iM!N2A随%IsN*`d )h<{}P:Kdd;%b}+?RB*cɥRABi rSrqz)1 NrON~u x;e4洊r ( endstream endobj 300 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T23 301 0 R >> >> endobj 301 0 obj << /Name /T23 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 54 /Encoding 302 0 R /CharProcs 303 0 R /Widths [21 0 0 0 0 21 ] >> endobj 302 0 obj << /Type /Encoding /Differences [49/G31 54/G36 ] >> endobj 303 0 obj << /G31 304 0 R /G36 305 0 R >> endobj 304 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 305 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 307 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 804 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*J{_Xl5XŊ`Oi}g[ly[okK8:v <[XB]1b9&g:G,&9ޞk:N)<o u0s{?_ucz~O,3Nǡz66pڱ,A2;pC.hak?4| Hn{$+b:00G͠3i4$I Q@|lLEPRT=NNHM|1m\jb endstream endobj 308 0 obj << /Length 5719 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!C7hJXd9n* (@70: 8:!4Q2(:@2 7FH7 c@7rx dr$$ JީD:L0lĨb pP3C{:F3(;#\6* (:r %0a0< $ AMPۿiM2K%KJJӘ@CaN! \gRM, 1eM 9@84LoU9pxv;4F!Ȳ:)QMJ2!e 2n5 5=T}NCeeZ@ GRMWTUՄ-Kh@2f6EZL:9Zi@$C->O5Aд=iQ}"7u5-LZQOuH2&b0C7HCgC g4*Tnu|_ZUm)e&4$=X3bC(,}%)ϩ9b;z8bIبk wKT Wˑ/A>rMR5~%@ʐx˹^H6{.b^U9UWp\C4RJXDU^\ r/abGPCf*H4ZlU#YZ)BwZhC#p(9 +{K$ ~^ERAQC2ƃ#B4|Ģ`O\UUXꐌ1"Ȅ9_P׌g]6 $` y6dy\fi5Dm1!!sd@fj!SyZST~ LBjh8A: 093Ljwo'%~O;5"mк@աp!8pb"]dkɸB$8bu IH,\4TԸLܚs{J|HH8z 5b =p ^yjRN̲S! ?6VPhE^@V+h 2rTz=gUyd\W*Os} _eCTR:֘p36JU) kYd5v5E5YdB huu}Q W<(" G AU0rPHPPCQZ2M |YOzE ʆTiG%sLNN Bt־:&kZtpR&6VdOF,<%Vl#bVV(D{Vl̫?cGե972lM~6jch=s@ f]6 dB.j1!um $,4H+,ׂ-Ks\=1q=m"Vސ ȦovG/cDѨٱ;`O! QVp; /"0P8JLcVP @d_P!8jgq :ZK"SH `#eG&DCD@2)_-o g&F&O= !J ;U$JPI+pNʴ"#bU'"#Qm;"l#Q-{ le.G JP.o 0 {/9 s0.o10oG2kG 1I 0p #T˃Q/B' 3O33O1 G\ #4q.N;HՐ7Y%8QT5 M0{9]:3~mk5qĽUUq#G/ڪ%o,BLۑ$ 1I~UQZTQ=>->o3?%Op&)/!6'Bg oo/P,0  )PAP #q< pE++#³.sJrgsvo2.865URkŴ8sC8m 9UrmuD:*ɖmU 7Q71NQU#5Uq+1p6K6.7"I;W_׻-]v=)v*HDM'D%DITOo1cG`#?ott{$V~C!4!GAiK֟vc-qqEowP+fe 7A_H[:Vv @QO^!YF-Ts4:x9xJ~Tz:??[{oxyQVLX|/mZ,UNqV\MvW|9œg5%Z|FXϸ;}rP|z͎zpzK)TcPLol*Kھ̺lVATOʃ084L9 \Ւy2V9̜:O\vqpy=W<Ы44=3-ӂ,Oy/}E|m3u= |үSK=jϹ SܵK < Jк= ;Ie;1gWZz:Qסzm[0z׽BosϵGUZSCbӾ$=~ozب{ICZ}G4-,{\[=s=` HDX#b9bI/E?cv_6>dyvO&U]X:'0af: )}xCCG}0I˺aˆ -mDOCqk'(r׻#|z='6]KI yr@ ?WtTyG[ 0dyz ľ^H_4Pi@\!dbWgnH^e̻gF)&x] (?<`ODbΈ@@9'/;7> /Font << /F2 7 0 R /F4 8 0 R /T24 310 0 R >> >> endobj 310 0 obj << /Name /T24 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 55 /Encoding 311 0 R /CharProcs 312 0 R /Widths [21 0 0 0 0 0 21 ] >> endobj 311 0 obj << /Type /Encoding /Differences [49/G31 55/G37 ] >> endobj 312 0 obj << /G31 313 0 R /G37 314 0 R >> endobj 313 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 314 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 316 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 747 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLrh]OeeYT%4 M2rZ7csw\sTVd77}+s> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@bAMj#!l4Ỽ 7 2#7 q4Ch: 1A"àq~) 09%a`@: H"20B' 4:4ة&ƃ6)c,4P9CL@@s7Gy4tS <Hñ #IT'JGp/JCCa x0rPc6E :q <-K$2YI3\7FuDGۦ>`YgˁF̻Ī3aEQg6L@o72~9g*#/ 9Nn:kվ[&ћ6rv(Ab~9lHݮIcI[Vfw 3F#&Agƒ+L͂ si.`:tL@CWl`6ЖT&KK r)K%eSfbhrC1rıMK A@iL9 Smt(i :\c6j "<q)2!A:^ћVa)+uZ֐ 0FֈnH&w C2fڔ-]^쏪|*%X=χ[-V蔃w7MU8Ԩ:X-_lX¤zIet|*}J2GNT96qEr *Y{{+(t!j`SY7 |\M7tP80w4b}uZdqCu )([EjTh,X/&0+;$jiyt&F*F$%B"7H K,դEPi`!.u,tl=Dz5ƺCL)^,Cs]4rZo^&ey+oýЇ(||&"iI6<Ai5J<|l>|qFM)Im9L]eW27eB\$"3i;i%TAÓS9a`䃍B |1 U EGwmN!=?=J:Ny"AXU|i>}~rBP.$s:eۀ39s*ôz BEO/.x ܀d~w ^`^ ,2PŞ6l;vFBŰp܂Fv o*P`f;b""-L  7 #p8"g0@ޅXzmb4q0[ ?J @ 簤#r- P d oŮ6.pmP\g&D\?<` /ŶT\琣 nL#BEn&Mz/ ;L+.1b5^> /Font << /F2 7 0 R /F4 8 0 R /T25 319 0 R >> >> endobj 319 0 obj << /Name /T25 /Type /Font /Subtype /Type3 /FontBBox [2 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 56 /Encoding 320 0 R /CharProcs 321 0 R /Widths [21 0 0 0 0 0 0 21 ] >> endobj 320 0 obj << /Type /Encoding /Differences [49/G31 56/G38 ] >> endobj 321 0 obj << /G31 322 0 R /G38 323 0 R >> endobj 322 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 323 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 325 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 768 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLad%dhU$ ơo^iTOA0;V1mLE e nS)P"7vⱘdi1]^z?sm꯵TwW\>3x1ji=/<[+kmqo/'#=Z&Sp>/noӀ@p*,ɳM#ƻ.LpäP{-|sM06 e !B@{#ϰ LH$t*jDqp<Ӵ+.>1!>)r:QS&;NҞJIKLԡ0TjНn@$GDLJ20l#7;Μ!;L%!I8*JvT2ëcȩHW,SX"UiM'p;u6{iԱ]tİu ESécV0Օg#kAj5D;i\Q?R-[jݓxZI8m'Q`x5oSTm>8$J/3V[ 1NGJ{;_Ԛ[r˙:GU[7&JRetaڞjj'a,3bkf@Vɟmms^4n  endstream endobj 326 0 obj << /Length 5533 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@bM&LX&`C.ѴsBnԳ!+E$9 92p0H70rڤ<j'@.@) XZ+2,H HhxAK5ta6FRJOKaf֔oM4@6TMqEZ4Qd7cJ40,J*Z86r,pTʈ[#/M / 9x.]1 -(s}-AxI|23#]w11l;*ae4 :WpDNGR (Z# "p6ce@`r 088 P~4\ 4д8]DSx{cL1GQuP2A %ïx c1#2.46#plj w4!)bDlfW N̩@-}EmH-.ёTVe]<<9Ks2ίyB P8A(!466 PAj42I;FԁhR -P2HTT @O!4+ؒu6lIPUB}8S<@)/NP#9@ @BZ ̒eHBٳl*"%MWefb` S:i`JmA(Vf1.AXcgFia *Ч`-BtQ'Tl13BrXʋ>D>b0aB{ʰ3AB?eZRֱ+amItDb%R R=t{մ`oU.{A#g0Q$$8; l09`(@d|N&$xI?F/XT,j1+ŧ\cIpA b2A LpA R8XC 4AGvdVÎdLDSV%~LSKeZ??SXs* +ig2+^Eԏ(|Nrs2H_*j8s:OQO;팷{<53 g\!:|ꜷrHa!afЊsdNَWqr>}s{O}k=)U[xl38Re-7'`E;_Ote;4#t,]ɋ;xK`,^<i6,W4[?{xFf"kNZ!|LWZ@͹4QXD%_we#p6d`-!0FgnJ5P,X_/`ϼM ROlV" OZ5 1殭X2 iPeώnkpH/ N! /%  o В0o~.H-L$6'tg/4Q+LOOo6U*/2Q!!t尃5)3GdԐ 92Xrh謋43`E99N&r8#:%q9'=/r&R-]R+"MB%RQ/ 01So"j39_45GT3I1q k72G+4k*OF/6"ԯTK4+HS}ԔH/8GE/<]GyN39IsCNONsm;;4MaN{#P;N[.oD j".c(QTb@ ɄUPwQ))HTT4U+5aAw5%,T)KU%E1|OCt:?TCA}&rH#=X/EeXVtmM+g1WH[\1}:\LP"չJLM[^+n.Sk^^S[7L 5 [Pl]\PSmI^+$4`.Scm~ -~O<$g?A?VUuhQ%EV/oi5i+qeWjfUC"qXDeb.VU.r"_BZN-ZFFtianTya5\B4o:\6to^QÑ65T0[KV^4o 2|vadOqQ5K0#NQo"u4Џ Cuur;i;7c%Gvwrkl5,51DpFIf]6dWt)rE#; v;9.8Wڥ9BChOnVt;UV~uio UTVԲRF@sDVWxqD,AW'2 `S6 gn.T0!a\HEt3&|xSNo^#MA gpgqerouc75[;7gtK8xgk02~5M׋"Aؿnx'2z錇#@"ywXoǍ.p= 玔e5ILXяs7kx/@y xstPNJd%Mwǔ=w=W//c `P$ 3{/;p"hZw|N|=NO(zYVP=~ ]Xg@;4EAj̸9b#`a B"Kq{.{X /mZTbo rKw\GVQ#^_ g#r[ek=ٜ5Le'~W'~dM8}8VMakvSڜ?Z7)(//3AS$S`AS>_c$Ggܧ?xZ}Ɵ \d΁-SdCF@ܜ=~i}cH~~S&Q6eҲsi.*ú C M/> #@ * 7&zֆ!MUt tEӜ9. \2&|4YyAڿ7 Ca b  a:DHQ@e6MSqRT5@a@1 !`4 %b <@7pq#6(l/* 1 '2e*Q9&)8TTxL.yT@@ endstream endobj 327 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F25 232 0 R /T26 328 0 R >> >> endobj 328 0 obj << /Name /T26 /Type /Font /Subtype /Type3 /FontBBox [-1 -1 33 20 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 118 /Encoding 330 0 R /CharProcs 331 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 0 0 0 0 0 0 0 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 0 0 21 18 0 0 0 12 0 0 12 33 22 0 0 0 15 0 12 0 20 ] >> endobj 330 0 obj << /Type /Encoding /Differences [32/G20 49/G31 57/G39 80/G50 97/G61 100/G64 /G65 105/G69 108/G6c /G6d /G6e 114/G72 116/G74 118/G76 ] >> endobj 331 0 obj << /G20 332 0 R /G31 333 0 R /G39 334 0 R /G50 335 0 R /G61 336 0 R /G64 337 0 R /G65 338 0 R /G69 339 0 R /G6c 340 0 R /G6d 341 0 R /G6e 342 0 R /G72 343 0 R /G74 344 0 R /G76 345 0 R >> endobj 332 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 333 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 334 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 335 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 337 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 338 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 339 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 340 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 341 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 342 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 343 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 344 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 345 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 347 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 864 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*̚w@X';$OZlRvee\g]n,z3d7xEh\KTۅrXVSXNN{ӳ|2:~N >لJu][F -}aVtzXćeM~#<*Ogdc}m|<%K5O)1mʪk ̪?o p@-/gm ʨC 1˛k3 1@pztE=~r UJdFLɏSJ/|'4KmDƨ3]E[Xu]L}X2/gE!\L5^w4(}H ȈILh֥ks|rrSoV+/|QneZtU=AdSm?Qo݁VMˊV)c90j}W .5_#NF^vO> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@dAM 9!P"d#̀hmnA="t^Eуh6$j8n(DzF2f"ܗ&."GJ"Ҭ} T(L{L3`5T1Fq#c-bጐ!g!,GS$)R45)+Re3'MӔHKQtmOTtDyPӳrBnԳk|)Hʪx9 #4pR!`xRTD(%7.C2T8R̵B=yR{@77 v2XU=楋^NoAb D(-{!2фXSMB%8槏  284cKDJrl8U)LӭI>2jṘ}ly?QTN2o/=^^0oΛ7ӆR&5>mADao%mUu!{')?qѸޱ\i<ֺ7-m$%b4R|&#p3 #:cd5qEt:z^;uLݳQ|]hWua`n&!e?IV"o`øp[`O"#*OO?q/$n;gPSDm0=aTFy XR,-HbXjvSIn ]1 b56!o, a LP-]#ن+e"҃v,F$\!"H}V:73ZHcSo*&3C9 #..at9&<#,FTdȗ9"ӴA1ҪXW%[-)'EWS$tHI,K:Jrg]_x'$x !585Y}q.GA݈OMI틯rIU @<\<bx /} la2=Tw/&^ٳ8gL4<ȣ`xE;*Ȳ|#ȋpcH A8UT <2P)L aAض$aq)EIvDGq%J*^h)X[V'KT%ԥKRN&*^ֺ2W)[`h«W(=[055a$IH`j!쁭q7yhl\YEX[h|_\$$$lbO"ZYl-큀3P<*3OXWĢkN mv$_ă[Ivϲ۪'~vzy;$:!{a$J RNW~U 0bDV _bHU?۹ZAm$L%_ 9lȕ*w;wpگ ujrSɖ'S#([ hUd= ԟT9èm Hld Л롱]k9.ܻ7]Iٟ*|72*0="stxR (* 嘴 X7ilӀ2оAx3G \Ƌ1i[K5 Ç-;Wᚢ;eDMЇQmV2o ]-ՖGs< ^{6o 2;|o:untygt@퐥>i% 5/7TP u*DW=kR&[*=V<~Ґ|լhߠG?f5/76\y8 }E⠃tݛa~FkvLE)+cN!6kSm:{Zoq7+tsJd.=Y,6沌gZ;a/W-O[^s]6.m[m<ɳOpi9k5v*q|eu/װ>}Rfmt0Q_Ij ~kJ3(Ovǫ i#T| @xs i䫤'LаlnppVOj*@mDDc +W J(ff-jH4q0E/ $n,maP"89O"P GMPH0>CpF,o1qw/=s:0MQgOTOZm"DА"g P` an|yp%p!C и.0@k|qknnӎj f/Ї POQ4Q)-rxRQ##mj#-$ $k@q2A$O$(L TNP8A02~1!#LQyOr߲8񀎱R)t+Q,ȷKp %[,;qʺ' rr#'AձX,"#  M@2r/1.1r$- pl$r##0"ZA5QY,O%K'M#6Q6O8ia(;34[4!+T-9reөҫ'r9Me  pGtlZx-# m?2qHs/q6=P[2 =34b2 o2o / &nQCZiHICʼnS6t\Jf6AFsbɰ%GkEdgy8ry8F抉SwCGjD05s:/:+LHOYKODK3;{LCS̘oT/)*g `Y[(ЯTy/4"tf .A0!*TaqN#"dE C M,e4;RAE3l10KEHS5_5rEHL7)ugG5%5s6?usH}?{75&,]s99mIT6QAZ:q[SX[Ҝ[ė!8ScTŐ|⽃UΧB[y[q 6;l)˰;!wIz?y?5CG֡OHa˺_o]Rmzs%''H({uc jx? {ڵկY="%EZΚ漡1ծՏoQ}Q<Ƌ\ϰRJ ޢ{o۞#J;[iE?avZ[j滟ҒmowdR; ߸S:e?R3"᐀Z1 3 $"D^FDC4Db0  "qm TcC1 )LgAL$n9 ItJb࣌F4IF j Ȇ~JlQ+5}*GiV3R̢O5lb2dC@gfX( #fbE U:FA0r TOۈ)s|eGr>h3"(-NF0n3 h* ExX|F'(%m=(Adý ZDC@(npZ>+8]C= DZ>;@d#ʹf1P\+L9hrh2c7#H2mQjȊ Q;,+d fLJ"}tîkP`SP+vܥ0(lѵɹ+ͦ(е 3F;PXgISg%mF)9SPкnnYq5]/5Og`%o´H-CR#%;Lcdm^؆PRLSmsF\O{ү."#<^Dɘ3G(:L9d1zl4M_LC1z\,MZd]4<DijޖL[=S㫹QoS ՋNY`qfksKCke ՟hyLlv@4`ܻGe]vmwP'Xm+~:N;\& I&7zD嚆Ϛ]̰cG_<)@}::L~I[1-%p}5 ?h@dz{gh6YZ1pm.<߼"N-@[C_G 3\2IB_.ĉ)߁@N1@@0 4D U+U/e?\n|Bf Sbs3 qt BS u\̻F]4xW$ox.Ҹ voKØ_Qs!Yoǥkwԁe 1#+Л#[Cؕk.p lfM |m)˷2R *AVX5P&X6FI:!SZjpv͸o:D;nf9ޑ"RK0 !eD))qŵ}3I+[0g#B4z1F̡Ln:gO1/j g|å20Q?0v%毙.^zNYHTH0*O̫$FR/K񣵕'W ]]gUO̟LŮk$ɖy=hRWG^U<9i[[Ls pic48rdeͶ!uI]m)"< M@R!MQ`fHbËQU0vƛH%tT9R#֕25TWS_=5@WRkjDTK/1Sޫa'USd(c¸d-0K<,\s /+E3 c$~ZpE?m]MJm>:s9ߌ(JܯEnĸal #n -rqs4\剓c=P;4n p ȯ K~tRTsq\:UU jpѨY8MK 6#NUގjl+/V|,%'7.Ȗwb]٧Mygd\ɏ >nm ͓B@ǂxtH0Ðd !+ O0k;(ɹumןocw祺/ ?M#Ĩ#9FL !j{KMtx5,?/E:yɑ#MtMG[>]x}ȹX`6:@ou|lb{!&U}x$vT#u߼w|ŭ͋_{Fߕ7h>nio|;Gw50񽻛;ɴcϾ;HIߘ]c`Jo}SʍkwTa3ǥB߫{WOG_C>'҈ p;19xo_g%?@JӞ(H)s:x06(:9벳,s,<[/3 ++{Kk* .K9+8︲@qz/KB {ֽ=zM*?%4ACBV0D)5b|+3CQ>AB Y>234K]?#Cs_ж+:?$A?ۯ{3j%a (!T3@\|(7$ -zh@Ƽ7Ak  E̮|I@"}r:Ţѩ3GKT7;|t;K{1˜yz˻&Sܚ:gë$)$Lk;siȓ>Ȼl,?d;LDJ%50$,ˌpt60tGySL;%CPSۢt8sDT;H,-}PT+HjxK.#M-Qw@#QBF{cQ\&S+S:O?MbSZVFqSz4-5:%sµC4ڲc ]sTu ÍsTD~TaW >PHtЬнTǭTǿ-U ֤DE(4I!O9ݕD4W5"XEc;VLlUgRb3OdM DwB-}mSnOo=4?MK=r>%K]8WLZ[ w[> /Font << /F2 7 0 R /F4 8 0 R /T29 350 0 R >> >> endobj 350 0 obj << /Name /T29 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 48 /LastChar 50 /Encoding 351 0 R /CharProcs 352 0 R /Widths [21 0 21 ] >> endobj 351 0 obj << /Type /Encoding /Differences [48/G30 50/G32 ] >> endobj 352 0 obj << /G30 353 0 R /G32 354 0 R >> endobj 353 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 354 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 356 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 669 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*J{_WSɬr[c-S;,f0%s1A,t@kx: Ln  AntE|Rr5?V3h?W/Ud׮kksn=N^:=ss;Gm)F:qx{],WFz+nO{w1-o^tT۸-~2;p"=˪1/2:P$AOB;˔T&L<4-] Fkr̓ [ qz/$ v{#R #JDmkd$IYK3j,Kt F<.MQq5"dAzO.}ѓ8*+2%,O0/e&3SAE,-1 rOs4To.ҴSUUS4:Qf5)SPNmm5m 8r.+cB"Km7HRWiT8"/Je \,4,I;gx2-FۋbM ɑC9ד`yD! endstream endobj 357 0 obj << /Length 2378 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1wkX94^I0]-,:Ux{NI:'d( !$G`e4'blcy@giRa0zV 0ˁ\S"mj^\b(9%$JsV>O S AC1Iv3($Ҫ@PU2 *g,o<6P 7 喩Rfϰ35ć,myRr<S1PP)L /tŒ@sLS. kf"AD\  I05d A᥻g1X (q@ˀ@ho*@ N0NY!@(OZBJc@*lXK %(] 7H3JNT6c@@ڱ@iL&ȒCk NcK{rI! 4Q(0UV]Pc*m[ THqC⭴ZGgBHnERbÒ✯9m$h[_l}*Rz[<Ѵxp2O'ּPURV[«Sp6"]"䵜 9ic[닿ǒZ@(rShfDɄ&h$IdœE!8ʁ^ԩ 2%:LZlA@0X:cnhsLi0Y$?Iy42@{L?h!-dÏbpk4x5{Qb VOA]SgAU=3d9AF,LDǧ w9 #5# ,4. 4lu!2]+r-ͥbE~^Pc$m4gUuan֎L\up[=iB3Y$I)?jdnzNs^ݶ#Tp@n&sgFIyl 942oQ.Iи B6ne`MPxy`yH:4" endstream endobj 358 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T30 359 0 R >> >> endobj 359 0 obj << /Name /T30 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 50 /Encoding 360 0 R /CharProcs 361 0 R /Widths [21 21 ] >> endobj 360 0 obj << /Type /Encoding /Differences [49/G31 /G32 ] >> endobj 361 0 obj << /G31 362 0 R /G32 363 0 R >> endobj 362 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 363 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 365 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 752 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*J{_Xl5X͊2K"5،iJkvCO__?_&o.N> >kAӘ C@= s1cʴ0./L{@ Ǒ!GrDtH1:d"J.ĵ0Xѳ-,kJr.B  V@p4Ĭs%h6˰-4|NRd ScMEϮGtIΫTײmux/`HJG;բATOM4M X3e6G+9(,՛OqB[9Vc'{kIͱ$rVDDd*6ͮ_i}]N ~.+~LDaMg=J #xA% ڋ,9J־Y<+>c[wEfyVPeJ_#6Kaz:~z5#ή:S.{%lEgb + endstream endobj 366 0 obj << /Length 5611 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1x˹{`ZC{V /@n B_p@U8G^:cA#OEbi<7 XNkЈO(8<. _/|=H @PQ1+qnM"-!r "nr͵`pU(P%LlKg%'rlN<޲!%2o'J~rw&&I(V6F{%◰jbGАɪ҈N Rkn9* /а<1FpQN1p 7R&a"nhqB1Z]2΀LNc C[ Q$0M :q'qoZL`Z:9rLU987$I?>R6ҵ9͠`kӫ 3853"MΙ<2:+?U$3nI>-9eĴOri%ǽBM=4+;BItC1#CtDt<AD%<2<+m;"?o#5@'#/\AXA†YBq`S'%["44(Uzv"Ma&R9_%cIc):kշG5t*0%2f4+I2tI-C0t0aM I %gKgT56|-%RS FNK6.263&*,$3u3ASLu5SDҧMUUQ6Ds"WqwWWX*8ՈW!ә70@wG:5Ƶ/57F#t'&S\SR`Jl E* DlQ"@<QjMfKd  #*Y]Ω$ QĠ ABYe?R6]gd hJ*1(}zM$T@@`GID݀d`I_Q hAf ԋꝴ/ ȲdĎI Ha_CJdJMZ _E*"܀~cl6F&EB* ]HY]~d$>Vg]gd p^RwRݸaaFFH"6xB@`0 u@ޏY%Kߌ0M@ "sM3e9S`n2 a3DsIb`C d Jh(^F P/!p΁7"Ko,L&SY1 aC ((\ #cS)bF  .nqsDy3[ TyRhTC?8Y,;E/)R_ endstream endobj 367 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T1 14 0 R >> >> endobj 369 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 718 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*^[=X,V::T̥Rih 2JgvJ~|Oo7v;e18!kTJYd wIWkfMah8>fxGCS`1LkB ٿ/Ȝ'j,K 5:x.j|6S+B-‘P.3Q7I&i:@HG!QÒŨY$+P/p8ȩo"RBJK$dܷR(;,;3Qdt>Ҡز t;Gl491 CT;:1F*(LSۃI)Cӣd>{f.kF.Fxƛ &  endstream endobj 370 0 obj << /Length 2260 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1 *@:cEP4].صg,I6m1MSInt(ocxѥvB|}y!ƊI(0cd:82K0V;@.aԞ09c0ak*(#xLH7N_MR͘;#c234ݔ]:*S<;#b7S8A]m#b7Ts{_60bj i47Jp 6> L֓2.9RtXߕԄͶmBhT-2%Wr}$MI;2ʐr <҂j} aKhW^ !f 8&yRQ)mH:8(@%fL#XEHಠ 1[nE:R(ax}fhƦK\ɣ@; Iv_|3RSUR2:땺(%(sRvpV*x2q3+ȘSLxMVXkj HWqdY$@ʤTj '))$3Rv3H"L}:39xT(7EI(AHgKHve^(y|NVY>b (78 d +80i}ѩ6hĘҒ/E6"2KriXԜ`{am3JW󃢶 )QYg:%DCME%Wy"ԅ9gy\+pnRam\xM>MAmvZ: &IB gxHCMxU-:>>J fxi|2+ iX3eP*)m*l)9gQEJKপ-qYZ8:iPpE/L/`%p9np (+jv&*`h A1D=uIO3_{X:W~4[U1K+`59k2K6IސbO1 ˲k2TR4;@LI))Mo̡7JEk?qN-}>Pj ABͳQn"P T]Iy)I&?gMl]![LaRYWs`c*PɄMR RNT_x\3|HLtQC"1 P$DaJ!%LwV@R,-~+B-,tvcԈy&C^Ne6WNq&לTS._oÚ A8W4ӼSJ@5Fj8TI8$ C&|V]qDpPG_ˎfАB endstream endobj 371 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T32 372 0 R >> >> endobj 372 0 obj << /Name /T32 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 51 /Encoding 373 0 R /CharProcs 374 0 R /Widths [21 21 ] >> endobj 373 0 obj << /Type /Encoding /Differences [50/G32 /G33 ] >> endobj 374 0 obj << /G32 375 0 R /G33 376 0 R >> endobj 375 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 376 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 378 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 559 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*"W.O&RɝnX v)QKk%Axͷ [:cUdn,k+Y[9%Z;N>矰4R#6fQhNfqdsf|{vlWH>!"|? ;nw65 b$.r8 L;O\5;@"+n`Q*-|*3|Ȑ|7@d Cqj#$oĎɪ"%1JH޾/S*-Gև/l%Z/CԢϒ#r- PqQe"Rm*2K;!BlK.hNUlRխ}^nMk[V-s]Ws{W`Xu?%]aؖ @Ymgb endstream endobj 379 0 obj << /Length 2432 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@dAMB\6?.!pfFT&K:"Y 9p0C @) XZ+'#4H._-jہ[cKV>BQo T rO/sA9Bq{GL"}Pw0t8i]M-ݷ((DGm_cFٮx[\=VՆGW|tCpkGC 苤GQkCTD+}NʸjdvB?(&lB" l C5uOhxȃc$\wF D"sh)CؒD@tNE!Mn$o׫8T?WY8&r )iC)xnh2Dl R >E'W+ SYN''q1ype -=n!E߃8Ҫ0Koѭ>EY3tZ]0`IS"L;3%"spIH&15@i:f4)&KxFM0E|И橑 cT!E' `CY ϛp2oDs#W17_NoILݥe%RLj&<싔hSh{@tq hu;M {8 )x6MY$5uRk \phs͎F9r[5&o]"VKI}~C80po7O-Xc8(bХBaz(p|BHD@Qa aAEJbh-"5pz%K _{=sAIJ*·Ѹ,Ffs@͆H NEk-Ҹq*t]D2\mz*9F [m]&ѹGeDUVVnNUk%zֺbSbԣ?A&F/_"*RmvĀc#{>{Ul/ic߳$VF^K{CLT*pn Hm@UNJr$uOES.[kTV5C^I4"!/(> @PҡT"wy1-۔'(!ˈ1 .|. lyM+.F.GkȑR5Qzi/t0p t7΃mi MkvѺA 6'vLI1PS{=g&r vRR aI81wSJCZzʱvn]eIw?.h8y,PYޤkS8+p HYk4t>T *9-Ph ? c Z$cݗOfZfhD/@X%T$ –[YKJ":>op4e p?P_B* E*VDK #n@ endstream endobj 380 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T33 381 0 R >> >> endobj 381 0 obj << /Name /T33 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 52 /Encoding 382 0 R /CharProcs 383 0 R /Widths [21 0 21 ] >> endobj 382 0 obj << /Type /Encoding /Differences [50/G32 52/G34 ] >> endobj 383 0 obj << /G32 384 0 R /G34 385 0 R >> endobj 384 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 385 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 387 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 429 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadT, ];)4 jies)gV٫o Wji?1w9\e7+cT[Yl,ZɅSeN]_p.^_+:=.csq^\'{^A,l|5(Ͽɴܖt `?/ K4"n*B2m;\BDptKD?(VEbFl[FrFtvrHDGIJr,ī-,+C 2C 3ˢ endstream endobj 388 0 obj << /Length 882 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@dMrAl q $cb(* ,(@X&p!H # "`)MHIc!&I2GR᪾)7Jh:H: #x c1j `r.!JƲ;SpKZCh\:"Jbx  Y$:0 *:ʎ7 X(HAQL$#ln&uS&6c8P*YDQTe4B QPaj.oUs]@gZ@c]x04A9OXJB@'5e٪zڌV&/ip> /Font << /F2 7 0 R /F4 8 0 R /T34 390 0 R >> >> endobj 390 0 obj << /Name /T34 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 53 /Encoding 392 0 R /CharProcs 393 0 R /Widths [21 0 0 21 ] >> endobj 392 0 obj << /Type /Encoding /Differences [50/G32 53/G35 ] >> endobj 393 0 obj << /G32 394 0 R /G35 395 0 R >> endobj 394 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 395 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 397 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 558 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLadT, L;ȮDX!I]2K' 4g5biجdkAf6W+[\wZ:wI)iMyU"M,giPdR{A%_in]㣙}:Cz۽FQ{y; [x=ɬqWL1n?4+oO{:l+:nd.0<<Ы0!I0Sű+Eq-4q rvHqI"ƒ RdtHґ-М00)k{5M̫7B.LlNrG7;˓2M-P4%+!,"M4UE'H,R;;SSƒOϵ5QNL mV")kW'NR =\T 4I2 ѡ,݆ŒYhkc  endstream endobj 398 0 obj << /Length 1206 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1 MㅚUJ7Q e #ߖNYxc;bgM<# )b>> /Font << /F2 7 0 R /F4 8 0 R /T35 400 0 R >> >> endobj 400 0 obj << /Name /T35 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 54 /Encoding 401 0 R /CharProcs 402 0 R /Widths [21 0 0 0 21 ] >> endobj 401 0 obj << /Type /Encoding /Differences [50/G32 54/G36 ] >> endobj 402 0 obj << /G32 403 0 R /G36 404 0 R >> endobj 403 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 404 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 406 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 741 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*J0+$FeA,MT["5?.QBM[/P슁)e|DcfX\Vٜb-9P185>_j4Z v]'͟+pxXi&Ѭ^?QALEOjjl"[Kv1ЈU(Sͩ?拆<3=nlϔI2N7Г.8;D =QCo,P<0|1!i@P,uDXCc<3"<ԿѤ~BpkqJo\ɒFHTE˲+{.KqtGsʍlC Md;:O3l>T/LS5 ,̴\c4EH$9S.Lܛ0T4MRKu46d%N='U4-5<ԿTO0A'R_7W/uGCר|u_?v&Q[UnVkWlA_H,LFeSʶo}WU5e[ͱYQTJRMyt]aԮv;u;b79td8Jy:͕,YNXez*9if^sgyzyΛjyz#hv~khkVÓy( endstream endobj 407 0 obj << /Length 5003 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@dMj#!l[ !"8')A&8T4\"6E(CH0A'e3qD ,HpaZi " ^' 0 n"L@@0a0j8@:xE !62CA@4A3SH/ fT@U%'ѓ8dHLS#20c;74CH4]5!{alEfâpީST*m/DӴMϳA4U6*c(:\YC(FP" -:Vh_r8d.6ͷ[2aK(4fjtPmCnKM2c(M 0'2~͛jہ[@oN 6:Hm{Rn߸IȘk|^ W <糹;:4ȞPA<%s\M֡ܠ\Bn8Ԗ2òUdb!JA Őh1c(kxA4lۯK K\gG~Gr<]{n_rB.ѭd@tA Q"A(?< Ӵp\D慟 vήB_4T o:Fm7E@Tظ] *g:E2rU81\ȷ."AzĒ)"M>X\/'#z8 ]xT'(TUK3LA呆 ]<+04\`pQod`$^"iSih톗iko7d1`"b$F fCnLzЊq,MR' NpjdH]s1Jl'L,ͱ<&\߇9'F>Z-8i66&Cؿ #ƇF⨉!fQG5N:-8BeALKBcOR!ptӀNoHdGShS'BjSO N֦ڞj!/rӪi}Ev.Φ\Ϟl@yu=nW3\phm7@@r;4KItmiشO' PNx+L]DN@38! YCz^J%j0\˹P0ibWJ;{`ZCn[I5HXo2fCqTz]7qL[6'hnj1V36\oq+8cqiX sY& #YZ+."ueA]ӝ-],ĝ{mޅ>,^:!q-+U"AP}0fUYc3YA-Z< U!!SXrG|D0 %ybr{<4d}+FiZ;Bj)4rg^j.G_~OCI$& Z YqoHŸ/d:϶2t,M7WB>"7og͞n/<5B(7ɿOJ9X?c`H5ƿ̰F,8(}Ȏ(v̐ς莢piGMLI&hUQ d@E2~_0hئ-~olBTڂB ~\Iȸώ(KQ- -\Xgz-P Cn-\pX谚/mv׭~K" Pz ) pmnx#V/˭4tMk&kg Q5&H1 fH0qIf REkͰlCH#LerN@Ec`PQ #Rfw4Ή N".MAV\hΠޱnt7| .% d6ь&P&C2Z;@`c lO&~6W8$:O r8 >eФe#-d!<%tXE@r;#R5&a*"/^`/bk5Q5|òs/*Fyb+k/*+OQo--o/C-r.R̗,n0j 5|r3"-)ȃ>-.5S7ƒ4)3vo-4L4!]1OLJ6MvS1Q44ȹS0518#=PPQA M0;o=G]9>mә>0{:q~NȸkH$S;`cx[A`C9(#ѽS8ņ-2t?%SkwB mFQ3:0G0Qu:-?N|z-hUDn'91Y#EryQ[J 6S>5H49:Bf5.3?@/A&$vNL4TV1JvfueտKHunhA/q}j] @SIpcT$ vWSjqa 'LVoL69oQ\]0H-w469gs "q bz c'QI2RR"PR'"/S.So_x@$TvPNRhrlW'R2}!rVS2{(WWʹH9 %`Kfwf832Â=[V@I=s)Z5r[Y[).Kcu]Y/ۆ[^#TVEDS!lEC39aXdH4nie5U%a͉8Ջ?dحeuYeYKJ{K7vv pwqC~5`朞.|q;-jI7ăp4SZޙOSmA`oKy!ƜU|;y;9tnG̴`$?i\[= \3tÔVY3ek5&zxTl7]%OUWiMu5[!|oSRO}U=G$9zkU7ϭ wz"f"i3z7{X fo )Y&6s`<D܋'/Q endstream endobj 408 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T36 409 0 R >> >> endobj 409 0 obj << /Name /T36 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 55 /Encoding 410 0 R /CharProcs 411 0 R /Widths [21 0 0 0 0 21 ] >> endobj 410 0 obj << /Type /Encoding /Differences [50/G32 55/G37 ] >> endobj 411 0 obj << /G32 412 0 R /G37 413 0 R >> endobj 412 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 413 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 415 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 552 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*_2 b-u55rZmmjVjҁ,ȰMJ n3U]BtSŝErSa(u65|j"DPBfF<G{뺐F04Jﴲ5cJJ2y$"25=6# ' H,> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1979 &2è@s> /Font << /F2 7 0 R /F4 8 0 R /F23 221 0 R /T37 418 0 R /T38 419 0 R >> >> endobj 418 0 obj << /Name /T37 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 33 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 119 /Encoding 420 0 R /CharProcs 421 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 21 0 0 0 0 0 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 0 18 21 18 13 21 22 12 0 0 12 33 22 20 21 0 15 16 12 21 20 29 ] >> endobj 420 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 50/G32 56/G38 82/G52 97/G61 99/G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 /G77 ] >> endobj 421 0 obj << /G20 422 0 R /G2e 423 0 R /G32 424 0 R /G38 425 0 R /G52 426 0 R /G61 427 0 R /G63 428 0 R /G64 429 0 R /G65 430 0 R /G66 431 0 R /G67 432 0 R /G68 433 0 R /G69 434 0 R /G6c 435 0 R /G6d 436 0 R /G6e 437 0 R /G6f 438 0 R /G70 439 0 R /G72 440 0 R /G73 441 0 R /G74 442 0 R /G75 443 0 R /G76 444 0 R /G77 445 0 R >> endobj 422 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 423 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 424 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 425 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 426 0 obj << /Length 144 /Filter /LZWDecode >> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 427 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 428 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 429 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 430 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 431 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 432 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 433 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 434 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 435 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 436 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 437 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 438 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 439 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 440 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 441 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 442 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 443 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 444 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 445 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 419 0 obj << /Name /T38 /Type /Font /Subtype /Type3 /FontBBox [-5 -9 28 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 446 0 R /CharProcs 447 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 28 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 21 19 0 0 0 12 0 0 0 0 23 0 21 0 0 0 0 0 0 0 21 ] >> endobj 446 0 obj << /Type /Encoding /Differences [32/G20 65/G41 /G42 100/G64 /G65 105/G69 110/G6e 112/G70 120/G78 ] >> endobj 447 0 obj << /G20 448 0 R /G41 449 0 R /G42 450 0 R /G64 451 0 R /G65 452 0 R /G69 453 0 R /G6e 454 0 R /G70 455 0 R /G78 456 0 R >> endobj 448 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 449 0 obj << /Length 157 /Filter /LZWDecode >> stream Z3CXP@dAG C$B$ąxPDHȈE" IeHa='E|Q^+( p?)om[q? [0ywt?B (Ġ endstream endobj 450 0 obj << /Length 167 /Filter /LZWDecode >> stream Z2 Pb b8,VALf؁I" !bx(^I&# I"8 .;2@(Hq`?'`; exzzu.W>Ǐ˼@bendstream endobj 451 0 obj << /Length 150 /Filter /LZWDecode >> stream " !Ȁ11 A3lDI𱜄 HEBI&#"I" 4U2?<< m^xo?;ؠ r  m7 "E1endstream endobj 452 0 obj << /Length 125 /Filter /LZWDecode >> stream G"c@B  8#DͰ&/+D&>B(I4@t9LI@p|O@p 7 çV*g` v~c>endstream endobj 453 0 obj << /Length 124 /Filter /LZWDecode >> stream FBc@Cb b< B f؁P E@I&# I"{?N 0 –T. MA50"Eendstream endobj 454 0 obj << /Length 124 /Filter /LZWDecode >> stream b 2#b b4ͱ& /+!D(s!p$ :X$ ~ !8<5>/ P0>p0g7¸hs;"ǁEendstream endobj 455 0 obj << /Length 164 /Filter /LZWDecode >> stream "Z5D((dAGh@c6ĈD IĔP!" y$ :X$ \(@L@):Lk5o~U>| M|]6~Ӱ. bDY (endstream endobj 456 0 obj << /Length 131 /Filter /LZWDecode >> stream "Z2 B@dAGg `Ј)!bB(g#$C22@ IeH}O# 4N`;` |H"bPendstream endobj 458 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 682 >> stream 5P8$ BaPRDbPhtN-5ⱘvHc /$JbT,L` i5Mh zO'jEQ1S4VTjU:FRkU]Tt%@(XmP[{Ek\'Ջ N{/w|Uf{^$۵+1bD/@Y^I3t-alvVM.[s%rxjǭmn>g6yg]}r wvi+yG/ o׺ߎ* . #@;K@818(Bl0B &n1QDD#-Q;Q\XpQ:= 2Br`1 |ǎ<}-K脙,r۽.42?-Ҕ{5Ms2΍F$s:ƓL;@гGQ;O rѳC5N,s#HSTȦӊؖ/}3gLJt6WyY)hMC[uM:\=XmV7VmحZH;PT^<mǶ=UwMuas-kڱ͵|ZѾ )t)Mũy3;\JV:YfOd˕Ty&+ endstream endobj 459 0 obj << /Length 6304 /Filter /LZWDecode >> stream P2 DC4cBl. $Dr 򒡎Xw 'd4|)*a& F"1b6G) (`. !Rىɨ4(Ap`DBñU rdU%r "`PlZ,{J3H%xwdDE_o,MZ. |!ppN0ޘr4#%N=yb9&[#)Q^aVh9]gYk^e/:-x[:R&(E?̚ɹVdd~ow.~LWDі &5NRUYıvY,P$H[ZAPH/l!P)Xl1% b8.hצčιy*\n/ ܂tpmV]Ejut'%BJ1 0UֻB)%m>R0m@مxbPE_rN%=8:ȁjOj n =[!+lmg 102t/B&X$JmOZ@q m9T)n6.Ç*3і7_"3܍3("㈋*7ڇ)/ԿEdEz$rLLS>2l0@흓䫶|2@\ 80p3 p z" ^ۅG/;љc_[]? ֋qD?Bd3VHZs5Ƞ c˞q&{TO:hmC٘1ّ8Ba% OD;cT+س:qnQ ~ m b `it3kzeGD3G* c iRY#&^U16h:&ʰ#US55ӧYh5Q EeѶ:+]NdN]Vjҧ?W#eʽW`+e'F+-«" A.8 h8fR"S A6D ܥ|ʩX2ZeLTB`ت2&,ªV2@T}NʬlɉUY\S:[e֑L\2RyEpĹ 8/"6͎XiB' *V5š}0U]V;kXG`ؽgaȖq!Z+06s Gzv*>ƉHK"Ҥ2*J#rWg| E}+fZd܂;"Q ;e@T= RɸQ9nk&ؠh_<]Z雵4&k)0pLBr/ʧFNk`pBO*tzMMg/\U_:)ѝA (R ]n48L&߱os]<2Nd'3퍰7ʖ#M c{պI{o1&=]U7=!v0Ɠ _;Q+0j %QY qh6gqeyؾ[ >B[dS:W=”zQVz-;l-T;uB݃xOl[zCøgqwWXx;ond~>k^ Eadanʳm:9 FyO:634_Ou@Њs(394 n۫|1N;qnԾCۿF4*nɍڽnoؾ?8[83&m "m o FDɪ͍H~mVxoF,D@֏NDRVokOnBd"L+/njlp~q (& poά / ̬  PmK ά0 fdPpp.1OݑYF,#w&XNy ĜƮm XQi$L"Ս2m aT?L3ZvYH5Rf2(mN\6Oe\geg*vw\He2Y?UWFV`)tCsjk]j;UV2Vt2 brvI'~)iLAedtLoMo3ޔ s,T 4'pBqOqhArܳyriur2R^6ptfk1OWQZOD.awSk-[j59 *ԝmeg;b0`!mv޷5DeooV#&wv]'էzs0Bwn7;uwzvx}|7P|վS|*4}U!+U$JMuQ_*TAm_HQw wRasw+xgdt v>h("XFxig8y,gBxlWnˎi9jxÐlI;'7إ3H`vaWya~=zh/& c +)5yb?w?tY^!%co:[={FX\RYKhCwvwA&1KL\@wH֖K|AyۍfAяx"Y淚"Ǚ:Y8vY6: {z' mVF58JOr╦9;zO+&yK nƀ H 2~ 5:jXl8{ X|ð.ڣ|-Y)/Wz2=G[=qxx!yG ~OvYR D6)&+R#5#`ғ~nfH*)VN FJRyz:ó"5mnճĚ8%IjK:h^.0ըI")$OowmU{5.cue1Sþ=r2[kB+Wף-Xr^pusl |I+LP9D=_<[Ĺ!qniLو۠aii<ڲcƻ+һ+٭K]5Ѭ)#}F;v%BB EzzIl)Y݈:_jޥ'S[bUԡ_ =O>Mu}B*oGץS@d~9דu oYAU?!4|3 Q?_ݟ_jd[sy|OE l~CM)Ԩl5 5 -g2Ƙ.GPAFCkN;ƣѕv_Xe)$ެrY]4ImU`mZ7~x2=+,:ҙ"\h4&%Ai" # ð0CC -+ܫ1#y*7 1BhlK܉>rD$qRılhrDz,2 (ar& B4bH #pȐ:p<:8nk`S;Q> /Font << /F2 7 0 R /F4 8 0 R /T39 461 0 R /T40 462 0 R /T41 463 0 R /T42 464 0 R >> >> endobj 461 0 obj << /Name /T39 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 146 /Encoding 465 0 R /CharProcs 466 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 0 11 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 30 0 37 30 30 23 0 27 23 26 29 0 0 30 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 0 12 33 22 20 21 21 15 16 12 21 20 0 21 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 ] >> endobj 465 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c 46/G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 75/G4b 77/G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 88/G58 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 120/G78 /G79 146/G92 ] >> endobj 466 0 obj << /G20 467 0 R /G28 468 0 R /G29 469 0 R /G2c 470 0 R /G2e 471 0 R /G2f 472 0 R /G30 473 0 R /G31 474 0 R /G32 475 0 R /G33 476 0 R /G34 477 0 R /G35 478 0 R /G36 479 0 R /G37 480 0 R /G38 481 0 R /G39 482 0 R /G41 483 0 R /G42 484 0 R /G43 485 0 R /G44 486 0 R /G45 487 0 R /G46 488 0 R /G47 489 0 R /G48 490 0 R /G49 491 0 R /G4b 492 0 R /G4d 493 0 R /G4e 494 0 R /G4f 495 0 R /G50 496 0 R /G52 497 0 R /G53 498 0 R /G54 499 0 R /G55 500 0 R /G58 501 0 R /G61 502 0 R /G62 503 0 R /G63 504 0 R /G64 505 0 R /G65 506 0 R /G66 507 0 R /G67 508 0 R /G68 509 0 R /G69 510 0 R /G6c 511 0 R /G6d 512 0 R /G6e 513 0 R /G6f 514 0 R /G70 515 0 R /G71 516 0 R /G72 517 0 R /G73 518 0 R /G74 519 0 R /G75 520 0 R /G76 521 0 R /G78 522 0 R /G79 523 0 R /G92 524 0 R >> endobj 467 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 468 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 469 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 470 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 471 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 472 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 473 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 474 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 475 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 476 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 477 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 478 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 479 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 480 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 481 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 482 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 483 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 484 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 485 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 486 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 487 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 488 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 489 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 490 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 491 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 492 0 obj << /Length 152 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ x?4Ϣ'@~?UgQP=UU`\W=<h h #Ax'dc>endstream endobj 493 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 495 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 496 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 498 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 499 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 500 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 501 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 502 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 503 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 504 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 505 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 506 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 507 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 508 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 509 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 510 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 511 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 512 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 513 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 514 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 515 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 516 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 517 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 518 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 519 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 520 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 521 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 522 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 523 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 524 0 obj << /Length 85 /Filter /LZWDecode >> stream F10Q$11- B&3lDI HHE*BI&#"I"@z=ȉ@ (` endstream endobj 462 0 obj << /Name /T40 /Type /Font /Subtype /Type3 /FontBBox [-5 -9 41 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 525 0 R /CharProcs 526 0 R /Widths [11 0 0 0 0 0 33 0 0 0 0 0 0 0 11 0 0 21 21 0 0 0 0 0 0 0 14 0 0 0 0 0 0 28 0 28 30 27 0 0 0 16 0 0 0 37 30 30 0 0 0 0 26 0 0 0 0 0 0 0 0 0 0 0 0 21 21 19 21 19 0 0 23 12 0 0 12 33 23 21 21 0 16 16 12 23 0 28 21 19 ] >> endobj 525 0 obj << /Type /Encoding /Differences [32/G20 38/G26 46/G2e 49/G31 /G32 58/G3a 65/G41 67/G43 /G44 /G45 73/G49 77/G4d /G4e /G4f 84/G54 97/G61 /G62 /G63 /G64 /G65 104/G68 /G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 119/G77 /G78 /G79 ] >> endobj 526 0 obj << /G20 527 0 R /G26 528 0 R /G2e 529 0 R /G31 530 0 R /G32 531 0 R /G3a 532 0 R /G41 533 0 R /G43 534 0 R /G44 535 0 R /G45 536 0 R /G49 537 0 R /G4d 538 0 R /G4e 539 0 R /G4f 540 0 R /G54 541 0 R /G61 542 0 R /G62 543 0 R /G63 544 0 R /G64 545 0 R /G65 546 0 R /G68 547 0 R /G69 548 0 R /G6c 549 0 R /G6d 550 0 R /G6e 551 0 R /G6f 552 0 R /G70 553 0 R /G72 554 0 R /G73 555 0 R /G74 556 0 R /G75 557 0 R /G77 558 0 R /G78 559 0 R /G79 560 0 R >> endobj 527 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 528 0 obj << /Length 174 /Filter /LZWDecode >> stream be !Ȁ11 3lDIaĄHE IeH4˂?4]` :U@ p¨9x v|Oo??n@@0x,Q@endstream endobj 529 0 obj << /Length 72 /Filter /LZWDecode >> stream F" 6B h( A`1 !a&6H" A IeH lBCHQFendstream endobj 530 0 obj << /Length 132 /Filter /LZWDecode >> stream "(Qb b- Ef؁y"884MGS,@D?߀;@?A`)OL zU@U5p>~W(fxYJ E1endstream endobj 531 0 obj << /Length 148 /Filter /LZWDecode >> stream " 2  8hL. 1a"L<^WB#ĉ> stream Fe `Ȁ11- 3lDI𑄄8LMGS,DD>|?*%BCte."Eendstream endobj 533 0 obj << /Length 157 /Filter /LZWDecode >> stream Z3CXP@dAG C$B$ąxPDHȈE" IeHa='E|Q^+( p?)om[q? [0ywt?B (Ġ endstream endobj 534 0 obj << /Length 160 /Filter /LZWDecode >> stream e !b b,.ͱ& /+D>B(PMGS,@D??oD~`બ'W6g֋]k{{ "ǁEendstream endobj 535 0 obj << /Length 168 /Filter /LZWDecode >> stream e 011C`Ѓ"B$EqVDHHE IeHxSP_*Q@V+HhZ,@wl=π{"E1endstream endobj 536 0 obj << /Length 170 /Filter /LZWDecode >> stream Z1CP@dAG S$B$ąqZDHȈE" IeH|@O#bQ߯ʕPuz;~?S>>^]˳/@?d Jendstream endobj 537 0 obj << /Length 138 /Filter /LZWDecode >> stream FZ2PQ@dAGC$B$ąxPDH$D" A $u2ĉ$A|i Li*4M+O;^, f~>[ +1",Q@@endstream endobj 538 0 obj << /Length 211 /Filter /LZWDecode >> stream Z2p 2 ALh3ahDm11y^3!!CDBI4@t9L2I@ "ӃS**X>⮢J`>,K*@،VNK(>I)9~$F ( endstream endobj 539 0 obj << /Length 187 /Filter /LZWDecode >> stream c b@dAGiDhA!bB$i#$C2@ hr:bD ?C~.zB`?Tի {<@bٓk}G{)'aW’,` "ȁEendstream endobj 540 0 obj << /Length 175 /Filter /LZWDecode >> stream  Cb b! @f؁C"91</$DC$x'7~ AׅL? ]`?b m!lx .p/ o|å/ x?.> ( endstream endobj 541 0 obj << /Length 157 /Filter /LZWDecode >> stream i 011C`S"B$ExHDHHE IeH|p 6Ǣ/_ mX+'e٪>mېnv/n[p^@(B (` endstream endobj 542 0 obj << /Length 125 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@F@|>E` 7MAzV @ AX ]Ic>endstream endobj 543 0 obj << /Length 146 /Filter /LZWDecode >> stream " 1P  8h,.( 1a"L<^WB#ĈP> stream G"c@B  8#DͰ&/+D&>B(I4@t9LI@`0|?Ea 9p56I~0/9<aendstream endobj 545 0 obj << /Length 150 /Filter /LZWDecode >> stream " !Ȁ11 A3lDI𱜄 HEBI&#"I" 4U2?<< m^xo?;ؠ r  m7 "E1endstream endobj 546 0 obj << /Length 125 /Filter /LZWDecode >> stream G"c@B  8#DͰ&/+D&>B(I4@t9LI@p|O@p 7 çV*g` v~c>endstream endobj 547 0 obj << /Length 148 /Filter /LZWDecode >> stream b 2"  8hL. 1a"L<^WB#ĉUre\p/,Z/xn p 'y: ( endstream endobj 548 0 obj << /Length 124 /Filter /LZWDecode >> stream FBc@Cb b< B f؁P E@I&# I"{?N 0 –T. MA50"Eendstream endobj 549 0 obj << /Length 124 /Filter /LZWDecode >> stream FBc !Ȁ11" B f؉)"984MGS,DD?z~PF<SxT RxV+7`8"Eendstream endobj 550 0 obj << /Length 151 /Filter /LZWDecode >> stream b 3#b b4ͱ& /+!D(s!p$ :X$ ~x x8. n>ٟ|Aۉpw"ǁEendstream endobj 551 0 obj << /Length 124 /Filter /LZWDecode >> stream b 2#b b4ͱ& /+!D(s!p$ :X$ ~ !8<5>/ P0>p0g7¸hs;"ǁEendstream endobj 552 0 obj << /Length 125 /Filter /LZWDecode >> stream "AF#b b$V)ͱ& /+1D>B(I4@t9LI@`x |E` -7Uw mC 4hcBendstream endobj 553 0 obj << /Length 164 /Filter /LZWDecode >> stream "Z5D((dAGh@c6ĈD IĔP!" y$ :X$ \(@L@):Lk5o~U>| M|]6~Ӱ. bDY (endstream endobj 554 0 obj << /Length 105 /Filter /LZWDecode >> stream F  G"  8X,* 1a"L<^WĈHx^ )/ 1@o;E0endstream endobj 555 0 obj << /Length 111 /Filter /LZWDecode >> stream F  G"  8X,* 1a"L<^WĈHÁ>}|߀w Na8=,tQ@endstream endobj 556 0 obj << /Length 117 /Filter /LZWDecode >> stream FB 1CQb b4ͱ& /+!D(k!r($ :X$ a~> stream bc Ȁ11-3lDI𑄄84MGS,DD>|O|/Mp . UHb࠴5QxDHQF#endstream endobj 558 0 obj << /Length 144 /Filter /LZWDecode >> stream  20  8h,.( 1a"L<^WB#ĈPWe|<|CȱQFendstream endobj 559 0 obj << /Length 131 /Filter /LZWDecode >> stream "Z2 B@dAGg `Ј)!bB(g#$C22@ IeH}O# 4N`;` |H"bPendstream endobj 560 0 obj << /Length 146 /Filter /LZWDecode >> stream G"Z4@#@dAGe xHm1!y\A$!C $u2ĉ$A>~)GN)ʝ5yMSO3k:^׃~ sCDY(endstream endobj 463 0 obj << /Name /T41 /Type /Font /Subtype /Type3 /FontBBox [0 -11 47 33 ] /FontMatrix [0.02 0 0 0.02 0 0] /FirstChar 32 /LastChar 116 /Encoding 561 0 R /CharProcs 562 0 R /Widths [13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 25 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 0 36 36 0 0 0 0 19 0 0 0 48 0 39 0 0 36 28 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 22 0 22 16 0 0 14 0 0 0 0 28 25 28 0 0 0 17 ] >> endobj 561 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 /G32 65/G41 67/G43 /G44 73/G49 77/G4d 79/G4f 82/G52 /G53 /G54 99/G63 101/G65 /G66 105/G69 110/G6e /G6f /G70 116/G74 ] >> endobj 562 0 obj << /G20 563 0 R /G2e 564 0 R /G31 565 0 R /G32 566 0 R /G41 567 0 R /G43 568 0 R /G44 569 0 R /G49 570 0 R /G4d 571 0 R /G4f 572 0 R /G52 573 0 R /G53 574 0 R /G54 575 0 R /G63 576 0 R /G65 577 0 R /G66 578 0 R /G69 579 0 R /G6e 580 0 R /G6f 581 0 R /G70 582 0 R /G74 583 0 R >> endobj 563 0 obj << /Length 16 /Filter /LZWDecode >> stream Fb  Tendstream endobj 564 0 obj << /Length 81 /Filter /LZWDecode >> stream Fbd D(@dAG A"$B$ąqDHȈE.DI&#$I"haĈQF%endstream endobj 565 0 obj << /Length 119 /Filter /LZWDecode >> stream i "@dAGo`Ѓ$B$ąqZDH D"K"$DC$z@?( 0>)4*LTj:}VVU tXbDY(endstream endobj 566 0 obj << /Length 161 /Filter /LZWDecode >> stream c b,113lDI𑔄 HEBI&#"I"7x`v1>/1U+5<e`XvT=e|?ܔ%RA0dFendstream endobj 567 0 obj << /Length 201 /Filter /LZWDecode >> stream  3cAb b4ͱ& /+†D(i!bx(^I&# I";OϧaBQ= MON?|hO*9=@t y~?|Cp&~\k9p8_``^pQ,xQ@@endstream endobj 568 0 obj << /Length 179 /Filter /LZWDecode >> stream d D1.dAGưhD(m1!y^2!cY!CDABI4@t9L"I@~_8@ w` bX[mr-{͢NQ+WjoAǸ?Md`Jendstream endobj 569 0 obj << /Length 161 /Filter /LZWDecode >> stream c Ɛ116 3lDI "H 84MGS,DD:р}*J~SB*+d٬6[eJVp=ZR?m'xdFendstream endobj 570 0 obj << /Length 106 /Filter /LZWDecode >> stream G"c@c1b b" B f؁É A 84MGS,@D'OP'%BQi4zU6Oh"Eendstream endobj 571 0 obj << /Length 210 /Filter /LZWDecode >> stream c @dAGm`Ѓ$B$ąxHDH D" "$DC$ <'z"L~ SjOS,ζ٪v=mϭx  @e0siq={(",Q@@endstream endobj 572 0 obj << /Length 194 /Filter /LZWDecode >> stream "d D1,h 2 ALf5FbhD(m11y^5$B@ hr:bd C|7\@8cD[ Q/{{_W{} tD60UkUbRS_E*E?:L#bpendstream endobj 573 0 obj << /Length 169 /Filter /LZWDecode >> stream c 𑘀11" 33lDI𑴄 84MGS,DD:w>߀:U2*>թ=CpS=, m,T6]+E]W*@ pO "Eendstream endobj 574 0 obj << /Length 179 /Filter /LZWDecode >> stream f D!h 2 ALeFcX4"c6D IaP!"@y$ :X$ π0d TwU ~`y?k [C] a*dNendstream endobj 575 0 obj << /Length 135 /Filter /LZWDecode >> stream be !b b8,VLf؁# "/ !bx(^I&# I"?<)UjnZWl5%~g{;~DQF!endstream endobj 576 0 obj << /Length 140 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A| `< R0 _ NTV\ q?P8wD#b0endstream endobj 577 0 obj << /Length 135 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A|\``)" ˜j TpU5?0PH0QF#endstream endobj 578 0 obj << /Length 129 /Filter /LZWDecode >> stream Fc 111 3lDIā HHEBI&#"I" ~E~T)TUJv^WV>@,Q@endstream endobj 579 0 obj << /Length 110 /Filter /LZWDecode >> stream Fe Fb 113lDI(^H $$"!$DC$`G8Aj:E "$Y(endstream endobj 580 0 obj << /Length 116 /Filter /LZWDecode >> stream e 1b b!" 2A3l@I a 1</$DC$pvr>) <j:}VV0dBendstream endobj 581 0 obj << /Length 131 /Filter /LZWDecode >> stream d `8X11*  ЈQ!b"  ICIhr:b$ |>|@ UիtSӁ qE> stream d F0af 2 ALd4FFq<&c6DIIܔP!"@y$ :X$ =@6?Oj\UjUJ}0Io?ۮ7 ?H ( endstream endobj 583 0 obj << /Length 120 /Filter /LZWDecode >> stream Fb #a dAGk P9!bBk#$Cb yCH$u2ĉ$A|?#>xT*UNUT?",Q@@endstream endobj 464 0 obj << /Name /T42 /Type /Font /Subtype /Type3 /FontBBox [0 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 584 0 R /CharProcs 585 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 21 21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 30 0 30 30 28 0 0 0 16 0 0 0 40 30 33 0 0 30 0 28 30 0 0 0 0 0 0 0 0 0 0 0 21 23 19 23 19 14 21 0 12 0 0 12 34 23 20 23 0 19 16 14 22 0 0 0 21 ] >> endobj 584 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 /G32 65/G41 67/G43 /G44 /G45 73/G49 77/G4d /G4e /G4f 82/G52 84/G54 /G55 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 105/G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 121/G79 ] >> endobj 585 0 obj << /G20 586 0 R /G2e 587 0 R /G31 588 0 R /G32 589 0 R /G41 590 0 R /G43 591 0 R /G44 592 0 R /G45 593 0 R /G49 594 0 R /G4d 595 0 R /G4e 596 0 R /G4f 597 0 R /G52 598 0 R /G54 599 0 R /G55 600 0 R /G61 601 0 R /G62 602 0 R /G63 603 0 R /G64 604 0 R /G65 605 0 R /G66 606 0 R /G67 607 0 R /G69 608 0 R /G6c 609 0 R /G6d 610 0 R /G6e 611 0 R /G6f 612 0 R /G70 613 0 R /G72 614 0 R /G73 615 0 R /G74 616 0 R /G75 617 0 R /G79 618 0 R >> endobj 586 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 587 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 588 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 589 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 590 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 591 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 592 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 593 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 594 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 595 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 597 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 598 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 599 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 601 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 602 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 603 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 604 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 605 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 606 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 607 0 obj << /Length 153 /Filter /LZWDecode >> stream "d `111* B!b"+#$A22@&IeH}`4p8n.LR x{p1*=pY G wÁpDH QF#endstream endobj 608 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 609 0 obj << /Length 87 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".884MGS,@D}?PhT E1endstream endobj 610 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 611 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 613 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 614 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 615 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 616 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 617 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 618 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 620 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 722 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*̚)dVgiЋEo-U9WBo0{- &٫iZ [#8\K_3ƴ9f!vV;)^!}oq%x[w cp5]~㻦򺰾Gv6] yoVwٶo~~Lܼcx?NJ\@P;9+6 ?/0é;>HQ*0=?38R)E˄>(Ɲovȑ#D4(rĤѤKr˨z$ /JBl_j;1SLrOO dS <'3$@,$;H,Rnۣ2TG3%Ҩ%0u8KdSbNO+=Lo|bL^/5FTZ// XPIRbkUҸ#m?\m!ULݑ :gV-5^QI$'ה]~׽P SWHw4ҨXbdcwz/RK-Z l+3jN"Q`cH3Z&v֛iƇ:.+ endstream endobj 621 0 obj << /Length 6808 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR16b^W{!_p$pjj(2"ƘRzO*4-ĂQGވ <@y$`ZA= 7sN; o (6P 9U U4FhT;+-`[{dK|T,' C'?  tqQ2UHڒ%*͗xr,w~,:$S~N3FX>vH= ،e"Ul [?#Zg:hѨ mƉI$Lձ/)HxZ0Hd\  l KfA 8`)F9@oPV3V V49EKY]^uI+RrJ ?QcxV [\X~q5H:Yn{&d nhQb'J 2o' X 2bQY_`z _T4dP@Wjʼn@O+E̺sdEuq]w,2N'ϝb7_wl Yu  Y~W9Y\db|>G)2ܤ0x7lI1u' 2Ba*KŽ a-go꜂f+Ӱ5ʯ]*EX7adcNE̝\jQ2EK^_ܘ 5^;l:l7T[G6Isk m-3tċ &)CRo1| !g9 D&f[`4 i .>eak{²&KjZ"zXľLvVzv, *qӑ+%m2{]1gI5I&mv}-0miKw-j޸)](&Lp7׳SrVC8gA] ~ j-G; jtЪCMnFurp8ƽ9u{;sM=g=ڏ,e[>eqᯫA]$3j,[8}pS:޾ؤ^ڴ'S,:}.)PqQA1/-U4">P6B'j<~gkܮAGy"/0@!_}ݽ5{uh?=Kiq$y6|^[} Xg𓋯O ]P g0a4O OdMh/"Ph~/Tp/pݬ0 錠wk|uNrُ~@L0HPLO.RJPl:ސvN~0v*VN 8nףMJz<p#0@j0 ʢJ8f k ౌ0ikPKƞ |0..08-J;[Rfu BHx Q+){ϓ,`ЫFC\ڪn  @ TJrb m M >ќ䏏oo{ڑn "PKS|j5ÓQϰ. KA N7 dYMIX )FӢr`Pfz $%iNFְ.-xOj,0rh'(0z>ҍwru-)pU*N*+P19 ,g1Oҿ,Љ*E-1M-qQ+ro!!05b.yn@% ł5%ihr5T-O$4K3EQQ-P>KPOT .SSU+SB!R8K:WTMhR4bTR(ISUeUU97O2Nu}PQNSkY%WR쥲I;GsvN*` )` PKʒGQ71j*Zi>D&>R "奴e Z`   x<ZB_NZ#MHKe`~  r2=>P{W5FeDueuaM ES4gVkfGS*WW_ZkgueAUShchCQgcivYNWSV"vjOYiNFQ0EXΞfmkUmYVՍ6AnF b3beb n HrdVI0ZHn4@s4@Vi.g`ؠl!BԹGetosp;Gn9d8!bX/B^/C ԤD4@%~tHT:DiuVmR~jWvQSz26{Q­׫fͳB|4V{TJ|כIP4IOlgT4{q~,AT{7TQYsXGG!Zo jc%WzC'.[` \NeHHE/ ZcbK\ # @ H^^ 3utAs{WOs>PWN9"Cu]v5vS wbWy wɈuvi#'ѭzz3}v4-]|pW|LkɍElJ(r9Gw7 FM בw8ol,XZkD7t9"!b%X('ڃB@cqsa rPJ65\ aXhsnXsɇG}Xˊ"UW_9bR"Bm|{8wO$J #6)<# >PL*T`p̦ZQ;h$*"(1 $&Z&r*GRG! r|ˬ A(Q.'$<F̯,))SB)N-"E ¡kFD/:4T#5!; SI7q1W$5 JGQQGc°K|!D3 kS 2x @9#0("˒;*P) 48@6vY ,Lm5+AtI c.;,!kb ¥  v2å׶!72ӹ'H)QzVNW3|4~ǫ25U'Bt,k$~RSn7.MLln Uwhlжh0bj' =t?(܅+Or|jǮ%19%`Leb3֓V# Cl~A* #;~~P*#sgh#i,} l?4l+sc=Y,D@ș5 j : 6t &R7M-X|Pg> IR@Dk z|iBڟc^&<[: *WE`%PōQPdsιC o2өqieWPZf*@a 2\2` vr A5""Ìb`c~("p_.DM4SY2fA2Ŵ9uEdPUf,PJZ:`S#...BAA3I0YxaL(F6w$&3ޑИP_Mܽl"GNeƩbqwN;> /Font << /F2 7 0 R /F4 8 0 R /T43 623 0 R /T44 624 0 R /T45 625 0 R >> >> endobj 623 0 obj << /Name /T43 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 146 /Encoding 626 0 R /CharProcs 627 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 0 11 0 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 0 37 30 30 23 0 27 23 26 29 30 39 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 0 12 33 22 20 21 21 15 16 12 21 20 29 21 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 ] >> endobj 626 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c 46/G2e 48/G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 77/G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 /G56 /G57 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 146/G92 ] >> endobj 627 0 obj << /G20 628 0 R /G28 629 0 R /G29 630 0 R /G2c 631 0 R /G2e 632 0 R /G30 633 0 R /G31 634 0 R /G32 635 0 R /G33 636 0 R /G34 637 0 R /G35 638 0 R /G36 639 0 R /G37 640 0 R /G38 641 0 R /G39 642 0 R /G41 643 0 R /G42 644 0 R /G43 645 0 R /G44 646 0 R /G45 647 0 R /G46 648 0 R /G47 649 0 R /G48 650 0 R /G49 651 0 R /G4d 652 0 R /G4e 653 0 R /G4f 654 0 R /G50 655 0 R /G52 656 0 R /G53 657 0 R /G54 658 0 R /G55 659 0 R /G56 660 0 R /G57 661 0 R /G61 662 0 R /G62 663 0 R /G63 664 0 R /G64 665 0 R /G65 666 0 R /G66 667 0 R /G67 668 0 R /G68 669 0 R /G69 670 0 R /G6c 671 0 R /G6d 672 0 R /G6e 673 0 R /G6f 674 0 R /G70 675 0 R /G71 676 0 R /G72 677 0 R /G73 678 0 R /G74 679 0 R /G75 680 0 R /G76 681 0 R /G77 682 0 R /G78 683 0 R /G79 684 0 R /G92 685 0 R >> endobj 628 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 629 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 630 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 631 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 632 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 633 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 634 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 635 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 636 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 637 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 638 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 639 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 640 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 641 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 642 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 643 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 644 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 645 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 646 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 647 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 648 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 649 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 650 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 651 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 652 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 654 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 655 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 657 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 658 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 659 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 660 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 661 0 obj << /Length 194 /Filter /LZWDecode >> stream "b B@dAGpxHm1!y\A!!CI $u2ĉ$Ap~;Q*qCӏ yTA՞Y\+ ~,`al~/:| ?-dſw(qOH#bPendstream endobj 662 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 663 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 664 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 665 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 666 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 667 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 668 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 669 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 670 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 671 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 672 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 673 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 674 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 675 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 676 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 677 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 678 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 679 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 680 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 681 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 682 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 683 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 684 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 685 0 obj << /Length 85 /Filter /LZWDecode >> stream F10Q$11- B&3lDI HHE*BI&#"I"@z=ȉ@ (` endstream endobj 624 0 obj << /Name /T44 /Type /Font /Subtype /Type3 /FontBBox [0 -11 47 33 ] /FontMatrix [0.02 0 0 0.02 0 0] /FirstChar 32 /LastChar 121 /Encoding 686 0 R /CharProcs 687 0 R /Widths [13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 14 0 0 0 25 25 25 0 0 0 0 0 0 0 0 0 0 0 36 0 36 36 0 0 0 0 19 0 0 0 48 0 39 0 0 36 28 33 0 0 0 0 0 0 0 0 0 0 0 0 25 28 22 28 22 16 0 0 14 0 0 13 41 28 25 28 0 21 19 17 0 25 0 0 25 ] >> endobj 686 0 obj << /Type /Encoding /Differences [32/G20 46/G2e /G2f 51/G33 /G34 /G35 65/G41 67/G43 /G44 73/G49 77/G4d 79/G4f 82/G52 /G53 /G54 97/G61 /G62 /G63 /G64 /G65 /G66 105/G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 118/G76 121/G79 ] >> endobj 687 0 obj << /G20 688 0 R /G2e 689 0 R /G2f 690 0 R /G33 691 0 R /G34 692 0 R /G35 693 0 R /G41 694 0 R /G43 695 0 R /G44 696 0 R /G49 697 0 R /G4d 698 0 R /G4f 699 0 R /G52 700 0 R /G53 701 0 R /G54 702 0 R /G61 703 0 R /G62 704 0 R /G63 705 0 R /G64 706 0 R /G65 707 0 R /G66 708 0 R /G69 709 0 R /G6c 710 0 R /G6d 711 0 R /G6e 712 0 R /G6f 713 0 R /G70 714 0 R /G72 715 0 R /G73 716 0 R /G74 717 0 R /G76 718 0 R /G79 719 0 R >> endobj 688 0 obj << /Length 16 /Filter /LZWDecode >> stream Fb  Tendstream endobj 689 0 obj << /Length 81 /Filter /LZWDecode >> stream Fbd D(@dAG A"$B$ąqDHȈE.DI&#$I"haĈQF%endstream endobj 690 0 obj << /Length 135 /Filter /LZWDecode >> stream F@-#QdAG.  !bB(k#$CdyCH$u2ĉ$Ap?T' ƣu1/R?V+rX,7Z-D QF%endstream endobj 691 0 obj << /Length 159 /Filter /LZWDecode >> stream c b,113lDI𑔄 HEBI&#"I"||q3T|_n>)%XP_]n '{"E1endstream endobj 692 0 obj << /Length 135 /Filter /LZWDecode >> stream e 8Qb b!" B f؁P EH/$DC$>' >`R' p0S/;Gyv=GOpg?5oQF!endstream endobj 693 0 obj << /Length 157 /Filter /LZWDecode >> stream e 8Qb b!" B f؁P EH/$DC$=(/"yzU1? >*aE_ E\=s<8`O8> ( endstream endobj 694 0 obj << /Length 201 /Filter /LZWDecode >> stream  3cAb b4ͱ& /+†D(i!bx(^I&# I";OϧaBQ= MON?|hO*9=@t y~?|Cp&~\k9p8_``^pQ,xQ@@endstream endobj 695 0 obj << /Length 179 /Filter /LZWDecode >> stream d D1.dAGưhD(m1!y^2!cY!CDABI4@t9L"I@~_8@ w` bX[mr-{͢NQ+WjoAǸ?Md`Jendstream endobj 696 0 obj << /Length 161 /Filter /LZWDecode >> stream c Ɛ116 3lDI "H 84MGS,DD:р}*J~SB*+d٬6[eJVp=ZR?m'xdFendstream endobj 697 0 obj << /Length 106 /Filter /LZWDecode >> stream G"c@c1b b" B f؁É A 84MGS,@D'OP'%BQi4zU6Oh"Eendstream endobj 698 0 obj << /Length 210 /Filter /LZWDecode >> stream c @dAGm`Ѓ$B$ąxHDH D" "$DC$ <'z"L~ SjOS,ζ٪v=mϭx  @e0siq={(",Q@@endstream endobj 699 0 obj << /Length 194 /Filter /LZWDecode >> stream "d D1,h 2 ALf5FbhD(m11y^5$B@ hr:bd C|7\@8cD[ Q/{{_W{} tD60UkUbRS_E*E?:L#bpendstream endobj 700 0 obj << /Length 169 /Filter /LZWDecode >> stream c 𑘀11" 33lDI𑴄 84MGS,DD:w>߀:U2*>թ=CpS=, m,T6]+E]W*@ pO "Eendstream endobj 701 0 obj << /Length 179 /Filter /LZWDecode >> stream f D!h 2 ALeFcX4"c6D IaP!"@y$ :X$ π0d TwU ~`y?k [C] a*dNendstream endobj 702 0 obj << /Length 135 /Filter /LZWDecode >> stream be !b b8,VLf؁# "/ !bx(^I&# I"?<)UjnZWl5%~g{;~DQF!endstream endobj 703 0 obj << /Length 144 /Filter /LZWDecode >> stream d `H0@dAGxHm1!y^ 2!rB@ hr:bD ~> x/)a1?882-6pз D0QF%endstream endobj 704 0 obj << /Length 146 /Filter /LZWDecode >> stream d `af 2 ALd4FFq<$@c6DIIH(^B()q$ :X$  Gѩ4U4?` ,63dY]YՃ~`?dY (endstream endobj 705 0 obj << /Length 140 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A| `< R0 _ NTV\ q?P8wD#b0endstream endobj 706 0 obj << /Length 149 /Filter /LZWDecode >> stream d `af 2 ALd4FFq<$@c6DIIH(^B()q$ :X$ o F?M. U0( :G,@; eQ4|߁L$bpendstream endobj 707 0 obj << /Length 135 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A|\``)" ˜j TpU5?0PH0QF#endstream endobj 708 0 obj << /Length 129 /Filter /LZWDecode >> stream Fc 111 3lDIā HHEBI&#"I" ~E~T)TUJv^WV>@,Q@endstream endobj 709 0 obj << /Length 110 /Filter /LZWDecode >> stream Fe Fb 113lDI(^H $$"!$DC$`G8Aj:E "$Y(endstream endobj 710 0 obj << /Length 97 /Filter /LZWDecode >> stream Fbc 8b b< B f؁P E@I&# I">OT%EQT"Eendstream endobj 711 0 obj << /Length 141 /Filter /LZWDecode >> stream "e 0a11C`S"B$ExHDHHE IeH~| @ K^SjrZl ~gZlT"E1endstream endobj 712 0 obj << /Length 116 /Filter /LZWDecode >> stream e 1b b!" 2A3l@I a 1</$DC$pvr>) <j:}VV0dBendstream endobj 713 0 obj << /Length 131 /Filter /LZWDecode >> stream d `8X11*  ЈQ!b"  ICIhr:b$ |>|@ UիtSӁ qE> stream d F0af 2 ALd4FFq<&c6DIIܔP!"@y$ :X$ =@6?Oj\UjUJ}0Io?ۮ7 ?H ( endstream endobj 715 0 obj << /Length 116 /Filter /LZWDecode >> stream "e@C1b b" B f؁I" 39MGS,@D?߂8  N?*L RUUH h>cࢌBendstream endobj 716 0 obj << /Length 131 /Filter /LZWDecode >> stream G"d Df 2 ALb5FFCH4"c6D IaP!䤒hr:bd :`h$;/ ~@xFCL$bpendstream endobj 717 0 obj << /Length 120 /Filter /LZWDecode >> stream Fb #a dAGk P9!bBk#$Cb yCH$u2ĉ$A|?#>xT*UNUT?",Q@@endstream endobj 718 0 obj << /Length 139 /Filter /LZWDecode >> stream @-CHP@dAG.  Ё!bB(i#$Br2@ hr:bD @ Es=Mxz~Vߕ?d6DdcD (Ġ endstream endobj 719 0 obj << /Length 160 /Filter /LZWDecode >> stream @-D!,d 2 ANphD(m11y^4!CDBI4@t9L2I@ ~p Sϧ3>?cm6 8;Cwa?{,, #"Eendstream endobj 625 0 obj << /Name /T45 /Type /Font /Subtype /Type3 /FontBBox [0 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 720 0 R /CharProcs 721 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 21 21 21 21 21 0 0 0 0 0 0 0 0 0 0 0 30 0 30 30 28 0 33 0 16 0 0 0 40 30 33 25 0 30 23 28 30 0 0 0 0 0 0 0 0 0 0 0 21 23 19 23 19 14 21 0 12 0 0 12 34 23 20 23 0 19 16 14 22 20 0 0 21 ] >> endobj 720 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 /G32 /G33 /G34 /G35 65/G41 67/G43 /G44 /G45 71/G47 73/G49 77/G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 105/G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 121/G79 ] >> endobj 721 0 obj << /G20 722 0 R /G2e 723 0 R /G31 724 0 R /G32 725 0 R /G33 726 0 R /G34 727 0 R /G35 728 0 R /G41 729 0 R /G43 730 0 R /G44 731 0 R /G45 732 0 R /G47 733 0 R /G49 734 0 R /G4d 735 0 R /G4e 736 0 R /G4f 737 0 R /G50 738 0 R /G52 739 0 R /G53 740 0 R /G54 741 0 R /G55 742 0 R /G61 743 0 R /G62 744 0 R /G63 745 0 R /G64 746 0 R /G65 747 0 R /G66 748 0 R /G67 749 0 R /G69 750 0 R /G6c 751 0 R /G6d 752 0 R /G6e 753 0 R /G6f 754 0 R /G70 755 0 R /G72 756 0 R /G73 757 0 R /G74 758 0 R /G75 759 0 R /G76 760 0 R /G79 761 0 R >> endobj 722 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 723 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 724 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 725 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 726 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 727 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 728 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 729 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 730 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 731 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 732 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 733 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 734 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 735 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 737 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 738 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 739 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 740 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 741 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 743 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 744 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 745 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 746 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 747 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 748 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 749 0 obj << /Length 153 /Filter /LZWDecode >> stream "d `111* B!b"+#$A22@&IeH}`4p8n.LR x{p1*=pY G wÁpDH QF#endstream endobj 750 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 751 0 obj << /Length 87 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".884MGS,@D}?PhT E1endstream endobj 752 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 753 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 755 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 756 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 757 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 758 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 759 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 760 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 761 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 763 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 734 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*̚=$V+<ӡ RcQZᖙ|&nv=+x8eaMuagy, Q)9Or,̮.Klg׸>mƴi ov;_`j% grs*.'WEVEdOcL/|O/.+ :nؠ;ɲ/CV溌Ͻσ290c>?6&mR>L:1Dm8qsjX70ga<2X%T$qܜK*mѳ.Mh/3RʽFQ-( 1l3,"!+j M %A E$R9?K3!S13)F,UO<5]C6mKM5UUճtW Qe9[@FEWW#?O 6%hhGL2[흠(m4Eqq vYuGg9kv/L򥮆ŽuPTvBmAc^=}7drmuo2x-&5Ma1."pE<^ʱ^ff\?~X9,Id~-e4W-YC-r_.k'! endstream endobj 764 0 obj << /Length 7338 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@fM 9!P-KZ6&ٶ? dtXGQ!IX"ql^Gܒ5,qIA} KppHDQ#9RLy%( f)Sd&K<|6dzdOˑ+Q#c-b/2IJмP }37̠U M.ёe7BDi"CQRUSP5DfNLMm?VOMhՕ%6ԃ-u]-aO`R{ZV\WtiJ6 1@ؕ,)ekQp4q*[4"QN=Ct#ݝv^9K]QL>%bӸ\oapjЛ,._K @4r f2àGC("@Ҩ-aA@hz @``dA!hlRoPZ-h\ p02h2`T)x{U)U\1WWtpw;MUpugH%uNW7Eʡ5FW~[x{.WxeX}܁^ǧ_?IH6e_F.W!wwk؟er7{y? 1~P)4ٳ#Ot4 TN !ǂh 2P0vA1a88h qLɡսl Ȅ#p@d2X wY1 ~Q,/DʓB3[$a k'@c~NΙA':\K^i$d`IBDKHD܋J|I"d*9Y5(WDkT>7+HK@JD"e>D%. B[ s*XĖ.vcs$B,A%V!q5pΜl !ѧnZ5+g^ 6Y#b|#wcXܴuK~T֕%kHOJ~PJ hїkMPQ$\ZH>ȓJ&+RXHTq$/M3h?ԼS(˧g M&MKٞȠs&' 3Li#{D$'i'l3Y mDSW8~sXgޫ=H5 wKhT>LىuRs{RfDBaڋ/LʄhM䬴PX?O^PQkvQ#iM>{W$m_-YZ]M\jL$+ΖDnqWs,%=c/ܘt"\HREtJ`im;OEJ`AbT8baVpź }pT )JƗ9z1]165aBVCJhEYZ.`ċC  46h*$0sCV UEz]jN64}],! `g˙b@vvCTۢ~C RmʗZ[KN7Ob9t^N`N:h9,zC܍rYjWNckwI5Fp6cܻY:FQx4JGW((ЀyL>5ݞHx3lMg`/֠_3 E޸ ^~{J=-q0Υo`)ڤR_S]诌jWgty367c͗rdum~y/gru*ɕim\FXUB# pp836jk2tZuSVvt5S¬U:+N(ʷi!p-0%U\~$w.h{{y}pwL-)m V!h˫X( 93Uwe#@玹\݊vW+ATݬ}߁u\6~ko{M_^ȟ7X+E~|V@ ...8N2/@9Gu-Xnu rP 0&Ne.v xP4;BM)8`PWl0B0k0zjŠhe@jzۆtpzgifIm* ϩ枭mͨh.bLof9"p ܜ @I qr$pl\mi1rq\ĢP30[H#M>z\o&MW-p\7OM`-jFUJ5rN^/MVUe*}O^7Hڊ/k #ȃ(6> BhJRhZc lkOc> @1$(H of8by,E<.4;1m:XEK<4\54T6t_p"s@EA bO8H$bH,  6`q' F#F<&ΔlͯrN𾏘3H 4SP$nX11S1>4I>&I'OKl9J--M?$4<UNbW1n543s1ONka qQ 79+dBƫ )1f D lnl  mH6fztU05UhYԸFsW335}LKՇM/M3Wquk3ҁ75M14iCQqx}TOAu[T!\uI]0豫 BCYD ` 6`@ jhFs tuV364bsb4$t5V-X<7Y >icuvFv%Z.eN64PWeS=3?]v?Zyb\f"3]4~Ohe[+sgOU޻D^@CF ` fn G2 UKa,U6:hs}eC cq]rGQ>"rie7j1pW!ɸ%"9@  @ ^  h2+"*Lbe t7AN3VtsTߏ#%yu8v@dvWqeTqg Xr^59QNRs*BQQ4w[y$7zwO Wr+z{Mtw]1Yx@qyg|.'~GxW>O{A{7B`WU O1kiB'Аu7ΏjW"op' FFp &b –JE-ʖ/UX im%n8in^; oVC!7e_C, q0w%rAr0h:"꾁,!z5&/p2!,e; [q3T wMQ(P9twu6Gy%|m}1G}y5~itz?~퀙QwxbxAZ;K8|9tMh2^ 6Wu~y97pxjؚׄIj񸃈<PHXZehkmqmX#yg{-șWVoyQe2x&q7wmJ-vY&Wn :!x㡖J(Z5B?4u:3f:!x}Q*'N7mzi6asZSd4:?  wDU9_Jc/{ȡ-%:Bg3X[w-Mwzv.EzW{bQz>8c;-oCz/ ԬM8 l`: ں p tmbz'zǤK-KBJ;351i-/v2p{e;ii;^׹ݳCɧW˯{˯2Q^{MP!q؞&"%VH+^kI—aL:n17T;He;w_:߁;s5 uՂ"s>\q\l{Ɛ ͥ7럭-̫y pip95;SBWX9𝆶ʜ "n5 ŧC1N';u}Rj9X&ZQ|<$Wo!X-,|g^R_ѽ'w<.{FXؚ\%S<ݪbKٵ˻g[rP(P#1joǥg؝2|AQM}\a< ZPxAid$=#0-E|}}Fm0uߧ])Ý\]ԥ=}^~}Gqiȗ!-yg {?ڏr<Ղ(JLœ֎Ɗy7G$GoQ ̸qy"]3޾"Wa~㥎qξ*z Ǟ&>Y;=~=^ێT-[1ɼM%< ) IfTQSh5Q9O͜Y[xr^"ޯ=8ሜ'o>EA䣉F9 CEs ?`;q#^><}\Žҹ];tQvkC)Q,5 [QUd0A#m!8d8`.$PaK2h30y̞x8h0x.EFΡ1TcL&39o$*ՊdjDuaȺB]v5 * N/VhZRkr]R/* $#PDŽ ,G4/zB*9Ԩwie͆o1NyR- %`0#ApS @R]*9?iR( ,:C:6@& 6 H5!+0 V.+ l<1l )Z9D؉&iToh.(ф}QR*p,r" ˄,.rLY02r7G46IΆzSa6PP' 8 ¶0(K>_LK˗Áil^Fo"i^FkJU',4mKgy^grSC+%kBiSj≱'s9M3lj'oH.ol{쏚Hs{QUY pB"9HH{rt\é2̑%7V}N}Go޻4W $w=%7۶ŷWksx VX :X=lԄj׬&⭖47@Sx :: AaH7e 8<\Ӹ> /Font << /F2 7 0 R /F4 8 0 R /T46 766 0 R /T47 767 0 R >> >> endobj 766 0 obj << /Name /T46 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 146 /Encoding 769 0 R /CharProcs 770 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 0 0 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 0 0 29 14 0 0 25 37 30 30 23 0 27 23 26 0 30 0 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 0 12 33 22 20 21 21 15 16 12 21 20 29 21 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 ] >> endobj 769 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c 47/G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 72/G48 /G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 86/G56 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 146/G92 ] >> endobj 770 0 obj << /G20 771 0 R /G28 772 0 R /G29 773 0 R /G2c 774 0 R /G2f 775 0 R /G30 776 0 R /G31 777 0 R /G32 778 0 R /G33 779 0 R /G34 780 0 R /G35 781 0 R /G36 782 0 R /G37 783 0 R /G38 784 0 R /G39 785 0 R /G41 786 0 R /G42 787 0 R /G43 788 0 R /G44 789 0 R /G45 790 0 R /G48 791 0 R /G49 792 0 R /G4c 793 0 R /G4d 794 0 R /G4e 795 0 R /G4f 796 0 R /G50 797 0 R /G52 798 0 R /G53 799 0 R /G54 800 0 R /G56 801 0 R /G61 802 0 R /G62 803 0 R /G63 804 0 R /G64 805 0 R /G65 806 0 R /G66 807 0 R /G67 808 0 R /G68 809 0 R /G69 810 0 R /G6c 811 0 R /G6d 812 0 R /G6e 813 0 R /G6f 814 0 R /G70 815 0 R /G71 816 0 R /G72 817 0 R /G73 818 0 R /G74 819 0 R /G75 820 0 R /G76 821 0 R /G77 822 0 R /G78 823 0 R /G79 824 0 R /G92 825 0 R >> endobj 771 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 772 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 773 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 774 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 775 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 776 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 777 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 778 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 779 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 780 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 781 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 782 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 783 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 784 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 785 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 786 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 787 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 788 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 789 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 790 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 791 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 792 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 793 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 794 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 796 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 797 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 799 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 800 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 801 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 802 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 803 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 804 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 805 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 806 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 807 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 808 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 809 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 810 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 811 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 812 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 813 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 814 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 815 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 816 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 817 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 818 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 819 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 820 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 821 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 822 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 823 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 824 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 825 0 obj << /Length 85 /Filter /LZWDecode >> stream F10Q$11- B&3lDI HHE*BI&#"I"@z=ȉ@ (` endstream endobj 767 0 obj << /Name /T47 /Type /Font /Subtype /Type3 /FontBBox [0 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 826 0 R /CharProcs 827 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 29 30 0 28 0 33 0 16 0 0 0 40 30 33 25 0 0 23 28 0 0 0 0 0 0 0 0 0 0 0 0 21 23 0 23 19 0 21 0 12 0 0 12 34 23 20 23 23 19 16 14 22 20 0 0 21 ] >> endobj 826 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 51/G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 69/G45 71/G47 73/G49 77/G4d /G4e /G4f /G50 83/G53 /G54 97/G61 /G62 100/G64 /G65 103/G67 105/G69 108/G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 121/G79 ] >> endobj 827 0 obj << /G20 828 0 R /G2e 829 0 R /G33 830 0 R /G34 831 0 R /G35 832 0 R /G36 833 0 R /G37 834 0 R /G38 835 0 R /G39 836 0 R /G41 837 0 R /G42 838 0 R /G43 839 0 R /G45 840 0 R /G47 841 0 R /G49 842 0 R /G4d 843 0 R /G4e 844 0 R /G4f 845 0 R /G50 846 0 R /G53 847 0 R /G54 848 0 R /G61 849 0 R /G62 850 0 R /G64 851 0 R /G65 852 0 R /G67 853 0 R /G69 854 0 R /G6c 855 0 R /G6d 856 0 R /G6e 857 0 R /G6f 858 0 R /G70 859 0 R /G71 860 0 R /G72 861 0 R /G73 862 0 R /G74 863 0 R /G75 864 0 R /G76 865 0 R /G79 866 0 R >> endobj 828 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 829 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 830 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 831 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 832 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 833 0 obj << /Length 147 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH`0v~@pP4 @@Ql ]p+'e٭T 0n&Nq(,Q@endstream endobj 834 0 obj << /Length 133 /Filter /LZWDecode >> stream "eAFCb bd Lf؁I" 89MGS,@D0 P'{?,.Lq*O=2T31?L)fU"Eendstream endobj 835 0 obj << /Length 152 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHp/ @oU0LQ@`0!wcۮp0,6d Fendstream endobj 836 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 837 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 838 0 obj << /Length 141 /Filter /LZWDecode >> stream "c 11C`S"B$ExHDHHEDI&#"I"{ > Mө*=BP^ mlX)*feBQg6"E1endstream endobj 839 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 840 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 841 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 842 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 843 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 845 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 846 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 847 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 848 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 850 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 851 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 852 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 853 0 obj << /Length 153 /Filter /LZWDecode >> stream "d `111* B!b"+#$A22@&IeH}`4p8n.LR x{p1*=pY G wÁpDH QF#endstream endobj 854 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 855 0 obj << /Length 87 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".884MGS,@D}?PhT E1endstream endobj 856 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 857 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 859 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 860 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q11l,2A""B$Ex0DHdD" A $u2I$A0x9Rt`HӪ:V0@4V` fVcd Fendstream endobj 861 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 862 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 863 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 864 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 865 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 866 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 868 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 377 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*̚)lydC+%Rn Ruլ{}ެebe6'kdi,!;c 5gz_IV3E~#e8 p8<-Or9<3t:=-R7]s2/z9ޮg|8.'Ӆ~7߭r6i60#]5pCQ4cE35 2) 2Pn#.Dq" 1CyDb5EMaő.Nlڭ8\+ endstream endobj 869 0 obj << /Length 1021 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@fD9!P-KZ6h@ٶ? dtZSl$O`lq{iFKrH8tE~-\!DIy%(s0rTbLW/!4*̑j?Hc-œ& #c-bhCCDG_MRQ(Ry@O$ CL1RTۈv ps{~VH^H'@C`< : !HZP.`<`fiShWPaJƲM 8<".6a3$[l@77 _A[} > /Font << /F2 7 0 R /F4 8 0 R /T48 871 0 R >> >> endobj 871 0 obj << /Name /T48 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 31 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 872 0 R /CharProcs 873 0 R /Widths [11 0 0 0 0 0 32 0 14 14 0 0 11 0 0 0 21 21 21 21 0 0 21 0 0 0 0 0 0 0 0 0 0 0 0 28 30 0 0 0 0 14 0 0 0 0 0 30 0 0 0 23 0 0 0 0 0 0 0 0 0 0 0 0 0 19 0 0 21 18 13 0 0 12 0 0 12 0 22 20 0 0 15 16 12 0 20 0 21 19 ] >> endobj 872 0 obj << /Type /Encoding /Differences [32/G20 38/G26 40/G28 /G29 44/G2c 48/G30 /G31 /G32 /G33 54/G36 67/G43 /G44 73/G49 79/G4f 83/G53 97/G61 100/G64 /G65 /G66 105/G69 108/G6c 110/G6e /G6f 114/G72 /G73 /G74 118/G76 120/G78 /G79 ] >> endobj 873 0 obj << /G20 874 0 R /G26 875 0 R /G28 876 0 R /G29 877 0 R /G2c 878 0 R /G30 879 0 R /G31 880 0 R /G32 881 0 R /G33 882 0 R /G36 883 0 R /G43 884 0 R /G44 885 0 R /G49 886 0 R /G4f 887 0 R /G53 888 0 R /G61 889 0 R /G64 890 0 R /G65 891 0 R /G66 892 0 R /G69 893 0 R /G6c 894 0 R /G6e 895 0 R /G6f 896 0 R /G72 897 0 R /G73 898 0 R /G74 899 0 R /G76 900 0 R /G78 901 0 R /G79 902 0 R >> endobj 874 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 875 0 obj << /Length 184 /Filter /LZWDecode >> stream Bb pq@dAG EЈ9!bBda#$Id" #$DC$c>~?]*JA2 xC;C>/laox߈w=|ʍ wr l#<D (Ġ endstream endobj 876 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 877 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 878 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 879 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 880 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 881 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 882 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 883 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 884 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 885 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 886 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 887 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 888 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 889 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 890 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 891 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 892 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 893 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 894 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 895 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 896 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 897 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 898 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 899 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 900 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 901 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 902 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 904 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 713 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*_2 b-u5٨7+E@CV]b^$M΁Hw2Wc6+Ww9h9T '.xZ~WP\rhSOho!s<Kv]eh"< >[q^N+B6F˯d"<(Vbev<,s|6玶:$0f ?kKs?)仰 H/ JK ۳,)hd> U<5QbXmE}Kk=.cSQ4mP-`]Dܶ4[lE3nJVM85Bi/\={N6WPVH> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1w_@AzYyz HU{bikw3/l̑-Pʙc.f?&v?$4єKiE&РeKR-RCkQ6 4)u/Y҂N )%Abi1dudRq BY{yr!Jtlσ+@@8R,Siji| 0eԼ m,I~lh4}Ave i6PѸe*NC"A&c=*6\ گ O:LNtPSrÃL:W_0TRdA!C fQI&@@l2QJT71S/ԺjP4* VUf5v4WDWjwZZb )].\ #3-XAB?NZ(df`9[D m#iH)Nhɴ7”s)A!Z b36jh\r=ͶЙtݐCl Uuiڡݧ h9AxņWj a|vږ@7l6e.ټ'ymB1 1 #8ktB,9XB;"5C[he:ٱ3_( T)GƷA cb? zD2N~Z[Fc3b0%,ôBQ+g6rz9,Ug9d5 auAFq,ФrOO+0e 2Qge&'d˫"28rv{xcXv !H݆98[iEq<dxzn"FȶԵ#sofuimĆ47ڽ8H ͝9ۄ2PhseFTj]aI\3»+NreZᗖE*OjI@&q#zT<>mny3X^PDАB endstream endobj 906 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T49 907 0 R /T50 908 0 R /T51 909 0 R >> >> endobj 907 0 obj << /Name /T49 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 122 /Encoding 910 0 R /CharProcs 911 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 14 11 12 21 21 21 21 21 21 21 21 21 21 0 12 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 25 37 0 30 23 0 27 23 26 29 30 0 30 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 19 ] >> endobj 910 0 obj << /Type /Encoding /Differences [32/G20 45/G2d /G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 59/G3b 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 /G54 /G55 /G56 88/G58 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 /G7a ] >> endobj 911 0 obj << /G20 912 0 R /G2d 913 0 R /G2e 914 0 R /G2f 915 0 R /G30 916 0 R /G31 917 0 R /G32 918 0 R /G33 919 0 R /G34 920 0 R /G35 921 0 R /G36 922 0 R /G37 923 0 R /G38 924 0 R /G39 925 0 R /G3b 926 0 R /G41 927 0 R /G42 928 0 R /G43 929 0 R /G44 930 0 R /G45 931 0 R /G46 932 0 R /G47 933 0 R /G48 934 0 R /G49 935 0 R /G4c 936 0 R /G4d 937 0 R /G4f 938 0 R /G50 939 0 R /G52 940 0 R /G53 941 0 R /G54 942 0 R /G55 943 0 R /G56 944 0 R /G58 945 0 R /G61 946 0 R /G62 947 0 R /G63 948 0 R /G64 949 0 R /G65 950 0 R /G66 951 0 R /G67 952 0 R /G68 953 0 R /G69 954 0 R /G6b 955 0 R /G6c 956 0 R /G6d 957 0 R /G6e 958 0 R /G6f 959 0 R /G70 960 0 R /G71 961 0 R /G72 962 0 R /G73 963 0 R /G74 964 0 R /G75 965 0 R /G76 966 0 R /G77 967 0 R /G78 968 0 R /G79 969 0 R /G7a 970 0 R >> endobj 912 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 913 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 914 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 915 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 916 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 917 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 918 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 919 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 920 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 921 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 922 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 923 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 924 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 925 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 926 0 obj << /Length 99 /Filter /LZWDecode >> stream FBf  dAG B!bBm#$CF2@ hr:bD 8 ':%D QF%endstream endobj 927 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 928 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 929 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 930 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 931 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 932 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 933 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 934 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 935 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 936 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 937 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 939 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 941 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 942 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 943 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 944 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 945 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 946 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 947 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 948 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 949 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 950 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 951 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 952 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 953 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 954 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 955 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 956 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 957 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 958 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 959 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 960 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 961 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 962 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 963 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 964 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 965 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 966 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 967 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 968 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 969 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 970 0 obj << /Length 122 /Filter /LZWDecode >> stream G"c 3  8#xDͰ&/+D.>B(I4@t9LI@=hF?TU_m!ߓ<aendstream endobj 908 0 obj << /Name /T50 /Type /Font /Subtype /Type3 /FontBBox [0 -11 39 34 ] /FontMatrix [0.02 0 0 0.02 0 0] /FirstChar 32 /LastChar 118 /Encoding 971 0 R /CharProcs 972 0 R /Widths [13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 34 36 36 33 0 0 0 19 0 0 0 0 0 39 30 0 0 0 33 0 0 0 0 0 0 0 0 0 0 0 0 25 0 0 0 22 16 25 0 14 0 0 13 41 28 25 0 0 21 19 17 0 25 ] >> endobj 971 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 66/G42 /G43 /G44 /G45 73/G49 79/G4f /G50 84/G54 97/G61 101/G65 /G66 /G67 105/G69 108/G6c /G6d /G6e /G6f 114/G72 /G73 /G74 118/G76 ] >> endobj 972 0 obj << /G20 973 0 R /G2e 974 0 R /G31 975 0 R /G42 976 0 R /G43 977 0 R /G44 978 0 R /G45 979 0 R /G49 980 0 R /G4f 981 0 R /G50 982 0 R /G54 983 0 R /G61 984 0 R /G65 985 0 R /G66 986 0 R /G67 987 0 R /G69 988 0 R /G6c 989 0 R /G6d 990 0 R /G6e 991 0 R /G6f 992 0 R /G72 993 0 R /G73 994 0 R /G74 995 0 R /G76 996 0 R >> endobj 973 0 obj << /Length 16 /Filter /LZWDecode >> stream Fb  Tendstream endobj 974 0 obj << /Length 81 /Filter /LZWDecode >> stream Fbd D(@dAG A"$B$ąqDHȈE.DI&#$I"haĈQF%endstream endobj 975 0 obj << /Length 119 /Filter /LZWDecode >> stream i "@dAGo`Ѓ$B$ąqZDH D"K"$DC$z@?( 0>)4*LTj:}VVU tXbDY(endstream endobj 976 0 obj << /Length 156 /Filter /LZWDecode >> stream Ƃc P11 b"B$ExL@/$C$D" "$DC$vzPjU0P*>ST;D?>'pWU\R/˝֕U):->,Q@endstream endobj 977 0 obj << /Length 179 /Filter /LZWDecode >> stream d D1.dAGưhD(m1!y^2!cY!CDABI4@t9L"I@~_8@ w` bX[mr-{͢NQ+WjoAǸ?Md`Jendstream endobj 978 0 obj << /Length 161 /Filter /LZWDecode >> stream c Ɛ116 3lDI "H 84MGS,DD:р}*J~SB*+d٬6[eJVp=ZR?m'xdFendstream endobj 979 0 obj << /Length 156 /Filter /LZWDecode >> stream bc !b b8,VLf؁# "/ !pi$ :X$ NA O;BY},vS* v{}UՀ>>,rgVen(OJġ7xNp,|Q@@endstream endobj 980 0 obj << /Length 106 /Filter /LZWDecode >> stream G"c@c1b b" B f؁É A 84MGS,@D'OP'%BQi4zU6Oh"Eendstream endobj 981 0 obj << /Length 194 /Filter /LZWDecode >> stream "d D1,h 2 ALf5FbhD(m11y^5$B@ hr:bd C|7\@8cD[ Q/{{_W{} tD60UkUbRS_E*E?:L#bpendstream endobj 982 0 obj << /Length 147 /Filter /LZWDecode >> stream  D111 3"B$Ex@DHD"5"$DC$=^:D` (.x(JVUհ=&F|Phse-6kEn[Wcg,Q@endstream endobj 983 0 obj << /Length 135 /Filter /LZWDecode >> stream be !b b8,VLf؁# "/ !bx(^I&# I"?<)UjnZWl5%~g{;~DQF!endstream endobj 984 0 obj << /Length 144 /Filter /LZWDecode >> stream d `H0@dAGxHm1!y^ 2!rB@ hr:bD ~> x/)a1?882-6pз D0QF%endstream endobj 985 0 obj << /Length 135 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A|\``)" ˜j TpU5?0PH0QF#endstream endobj 986 0 obj << /Length 129 /Filter /LZWDecode >> stream Fc 111 3lDIā HHEBI&#"I" ~E~T)TUJv^WV>@,Q@endstream endobj 987 0 obj << /Length 168 /Filter /LZWDecode >> stream d F0af 2 ALeFp<&c6DIIܔP!"@y$ :X$ ~ߏp0(S*L)Ax?7qvt [%сEpQQ}`@{. &E18endstream endobj 988 0 obj << /Length 110 /Filter /LZWDecode >> stream Fe Fb 113lDI(^H $$"!$DC$`G8Aj:E "$Y(endstream endobj 989 0 obj << /Length 97 /Filter /LZWDecode >> stream Fbc 8b b< B f؁P E@I&# I">OT%EQT"Eendstream endobj 990 0 obj << /Length 141 /Filter /LZWDecode >> stream "e 0a11C`S"B$ExHDHHE IeH~| @ K^SjrZl ~gZlT"E1endstream endobj 991 0 obj << /Length 116 /Filter /LZWDecode >> stream e 1b b!" 2A3l@I a 1</$DC$pvr>) <j:}VV0dBendstream endobj 992 0 obj << /Length 131 /Filter /LZWDecode >> stream d `8X11*  ЈQ!b"  ICIhr:b$ |>|@ UիtSӁ qE> stream "e@C1b b" B f؁I" 39MGS,@D?߂8  N?*L RUUH h>cࢌBendstream endobj 994 0 obj << /Length 131 /Filter /LZWDecode >> stream G"d Df 2 ALb5FFCH4"c6D IaP!䤒hr:bd :`h$;/ ~@xFCL$bpendstream endobj 995 0 obj << /Length 120 /Filter /LZWDecode >> stream Fb #a dAGk P9!bBk#$Cb yCH$u2ĉ$A|?#>xT*UNUT?",Q@@endstream endobj 996 0 obj << /Length 139 /Filter /LZWDecode >> stream @-CHP@dAG.  Ё!bB(i#$Br2@ hr:bD @ Es=Mxz~Vߕ?d6DdcD (Ġ endstream endobj 909 0 obj << /Name /T51 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 997 0 R /CharProcs 998 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 0 21 0 0 0 0 0 21 0 0 0 0 0 0 0 30 0 30 30 28 0 33 0 16 0 0 28 40 30 33 25 34 0 23 28 30 0 0 0 0 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 997 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 51/G33 57/G39 65/G41 67/G43 /G44 /G45 71/G47 73/G49 76/G4c /G4d /G4e /G4f /G50 /G51 83/G53 /G54 /G55 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 998 0 obj << /G20 999 0 R /G28 1000 0 R /G29 1001 0 R /G30 1002 0 R /G31 1003 0 R /G33 1004 0 R /G39 1005 0 R /G41 1006 0 R /G43 1007 0 R /G44 1008 0 R /G45 1009 0 R /G47 1010 0 R /G49 1011 0 R /G4c 1012 0 R /G4d 1013 0 R /G4e 1014 0 R /G4f 1015 0 R /G50 1016 0 R /G51 1017 0 R /G53 1018 0 R /G54 1019 0 R /G55 1020 0 R /G5f 1021 0 R /G61 1022 0 R /G63 1023 0 R /G64 1024 0 R /G65 1025 0 R /G66 1026 0 R /G69 1027 0 R /G6e 1028 0 R /G6f 1029 0 R /G72 1030 0 R /G74 1031 0 R /G76 1032 0 R /G78 1033 0 R >> endobj 999 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1000 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1001 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1002 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1003 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1004 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1005 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 1006 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1007 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1008 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 1009 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1010 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1011 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1012 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1013 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 1015 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1016 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1017 0 obj << /Length 184 /Filter /LZWDecode >> stream Ƃd 88r 2 ALgBhD(m11y^0#RB@ hr:bd @??xU ~W@e~Y֋Uln6+ jW8*}EP>?) :О.'(?LșF ( endstream endobj 1018 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1019 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 1021 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1022 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1023 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1024 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1025 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1026 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1027 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1028 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1030 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1031 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1032 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1033 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1035 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 777 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*gP n ]J%hIP즁f+9.^a5 6T`oV5Yưv "<`Xn6rgӔrvSjty<7qm[bm=_iwd\0vo#~5w^?GWeJ1iޙ цm{* |=>7c"?Ȅ8.Br>σҬ>,L?o:6kn4=, $M#|~Ir|3KLҬfKre(jL1l&P ͔dUG0U )+[8< pVLhOHlӍӤJUrCNn$3R̵ Xj)-TY{ B 3RW*JެOTW%%9ܖLc7_L%UꝯWߖ܆0tumkLP 7MqenPW)R-eS5doNt =<spP]^5\U#}<5NwxN5XNb@:4liUQJ/.^?I3WzniT{晿h:+ endstream endobj 1036 0 obj << /Length 2339 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1ZՍg$pKA0s(@.]d.8tX-N#TX?t%VT=#pٍ(:_(;-0LS$4\Ufl]~A^yc霌 25Qe7^:a"ai֡F%K2];&Sof;IvɡoGԱmFqڻF 8gV,/2ciNO1eif@ͱ&lsLC((29vVHOXH1}5zs|7{EC6jҸl+BzIl3(_ |Cf ?DU̧@f:a;6ށP;s-5;q|"Hp>g!d.Ka2faHn*pC(89.0W"Dhao=n b;:XÐi #X Ni J#,#t2g!~T2 xѱ ϝIc0b FHl#dDdCۜmI?'ԣ 64G!!^$ÐyQ> /Font << /F2 7 0 R /F4 8 0 R /T52 1038 0 R /T53 1039 0 R >> >> endobj 1038 0 obj << /Name /T52 /Type /Font /Subtype /Type3 /FontBBox [-4 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 122 /Encoding 1040 0 R /CharProcs 1041 0 R /Widths [11 0 0 21 0 0 0 0 14 14 0 0 0 14 11 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 25 37 30 30 23 0 27 23 26 29 30 0 0 0 25 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 11 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 19 ] >> endobj 1040 0 obj << /Type /Encoding /Differences [32/G20 35/G23 40/G28 /G29 45/G2d /G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 /G56 90/G5a 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 /G6a /G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 /G7a ] >> endobj 1041 0 obj << /G20 1042 0 R /G23 1043 0 R /G28 1044 0 R /G29 1045 0 R /G2d 1046 0 R /G2e 1047 0 R /G2f 1048 0 R /G30 1049 0 R /G31 1050 0 R /G32 1051 0 R /G33 1052 0 R /G34 1053 0 R /G35 1054 0 R /G36 1055 0 R /G37 1056 0 R /G38 1057 0 R /G39 1058 0 R /G41 1059 0 R /G42 1060 0 R /G43 1061 0 R /G44 1062 0 R /G45 1063 0 R /G46 1064 0 R /G47 1065 0 R /G48 1066 0 R /G49 1067 0 R /G4c 1068 0 R /G4d 1069 0 R /G4e 1070 0 R /G4f 1071 0 R /G50 1072 0 R /G52 1073 0 R /G53 1074 0 R /G54 1075 0 R /G55 1076 0 R /G56 1077 0 R /G5a 1078 0 R /G61 1079 0 R /G62 1080 0 R /G63 1081 0 R /G64 1082 0 R /G65 1083 0 R /G66 1084 0 R /G67 1085 0 R /G68 1086 0 R /G69 1087 0 R /G6a 1088 0 R /G6b 1089 0 R /G6c 1090 0 R /G6d 1091 0 R /G6e 1092 0 R /G6f 1093 0 R /G70 1094 0 R /G71 1095 0 R /G72 1096 0 R /G73 1097 0 R /G74 1098 0 R /G75 1099 0 R /G76 1100 0 R /G77 1101 0 R /G78 1102 0 R /G79 1103 0 R /G7a 1104 0 R >> endobj 1042 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1043 0 obj << /Length 132 /Filter /LZWDecode >> stream "B"  80,* Ͱ&/+1@e# E>I&#I">? E~?](O>_. RUn B௹ e SE0endstream endobj 1044 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 1045 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 1046 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1047 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1048 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1049 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1050 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1051 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1052 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1053 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1054 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1055 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1056 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1057 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1058 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1059 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1060 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1061 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1062 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1063 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1064 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1065 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1066 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1067 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1068 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1069 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1071 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1072 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1074 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1075 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1076 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1077 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 1078 0 obj << /Length 143 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"w=8 m[Au6Q_Շi]~6vcBendstream endobj 1079 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1080 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1081 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1082 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1083 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1084 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1085 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1086 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1087 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1088 0 obj << /Length 119 /Filter /LZWDecode >> stream F"Z4DdAGe xHm1!y\A!C $u2ĉ$A P'EBj:V8p@"DY(endstream endobj 1089 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1090 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1091 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1092 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1093 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1094 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1095 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1096 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1097 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1098 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1099 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1100 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1101 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1102 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1103 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1104 0 obj << /Length 122 /Filter /LZWDecode >> stream G"c 3  8#xDͰ&/+D.>B(I4@t9LI@=hF?TU_m!ߓ<aendstream endobj 1039 0 obj << /Name /T53 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1105 0 R /CharProcs 1106 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 21 21 0 21 0 21 0 0 0 0 0 0 0 0 0 30 0 30 30 28 0 33 0 16 0 0 28 40 0 33 25 0 30 23 28 0 0 0 0 31 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1105 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 /G32 /G33 53/G35 55/G37 65/G41 67/G43 /G44 /G45 71/G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 /G54 89/G59 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1106 0 obj << /G20 1107 0 R /G28 1108 0 R /G29 1109 0 R /G30 1110 0 R /G31 1111 0 R /G32 1112 0 R /G33 1113 0 R /G35 1114 0 R /G37 1115 0 R /G41 1116 0 R /G43 1117 0 R /G44 1118 0 R /G45 1119 0 R /G47 1120 0 R /G49 1121 0 R /G4c 1122 0 R /G4d 1123 0 R /G4f 1124 0 R /G50 1125 0 R /G52 1126 0 R /G53 1127 0 R /G54 1128 0 R /G59 1129 0 R /G5f 1130 0 R /G61 1131 0 R /G63 1132 0 R /G64 1133 0 R /G65 1134 0 R /G66 1135 0 R /G69 1136 0 R /G6e 1137 0 R /G6f 1138 0 R /G72 1139 0 R /G74 1140 0 R /G76 1141 0 R /G78 1142 0 R >> endobj 1107 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1108 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1109 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1110 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1111 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1112 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 1113 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1114 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 1115 0 obj << /Length 133 /Filter /LZWDecode >> stream "eAFCb bd Lf؁I" 89MGS,@D0 P'{?,.Lq*O=2T31?L)fU"Eendstream endobj 1116 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1117 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1118 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 1119 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1120 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1121 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1122 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1123 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1125 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1126 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1127 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1128 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream "FCb b!- Ef؁I"F884MGS,@Do>8†8xLJL>7J^u 9}}OX?u]/OctcࢌBendstream endobj 1130 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1131 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1132 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1133 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1134 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1135 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1136 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1137 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1139 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1140 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1141 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1142 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1144 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 743 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*&X`n]A%Q -]kpZR)x^ᗙ.^smh>2_ZpC+*f6C ]o<>o [ŸFM+S͋8.?IZgmz墹nnl9]7v}NvX*ϓ1.OkzոKR#r( ®4A 20:'м<߽$5 FſL(ЬoC#C@R:> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!C A9nd!hbP<.@nqdb=#P; @8 2+ʽFH4'D[Ǒm",x5!s#D%βlm(JCh2K+"*6\F"4;s|C8r ;rd>4 BL3E2-!--;IT'<:7 (^'0@3 QPt,YDU8$Z+!vZOD# 3L32H4uT5MjV%$۳P(Y#u*#-Y-}Nժ) 5 B (@x 7=6@Cx^XegP&!F#RRָQsYY^[7gF"xmoF7dU:7# AJx-ӔVi{5紦ֹ ǰ[8lFUamJɘoz?;O 87Z^kSRz@p*co%mge/kWX:iSxѶV毁ucZr{}B!ܲ7U4W ^'?|tAϽw{l|C cm#PsyOHR}o h84I(|o yP><+g 5_hO2x] !Çf ;FP:DИo#Hc#9@j ͊:UmWl?5gMF Flo]-m2”fp35j|1>Yx ˅^Ⱥ[#h]#j{L^ 4),]h̲Exp EKyIK7 ZIB&(YUQWJCK5brVI8S+IJ7A!#c^`3&?fnQ<9( rf͗I'i %3@ a|$СFɘ\3 o]$*OFk=PxGAL ݼ'20P !2Ι))xRL ZW aM9tOP%F)T 7`LfV6JQj#t4b1qm,^3 ̻C(wpپg&Ik#ɹ2,ǟk~Bx:dey2V^~] 6&}ǰDXauVJBHsu_ \:lllXeJ_ 0v$n%'q+uzSmn*xH,]1Ǚcš-7'WtѓϙV9o I8L>U?U;w :-Goo-Nb쨸6c>OW M.U[IN$l9ʎQJBC@ڬ@HQkJmaT:UJYLfw ގ὇#guJ?W t3` 4$ endstream endobj 1146 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T54 1147 0 R /T55 1148 0 R >> >> endobj 1147 0 obj << /Name /T54 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 33 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1149 0 R /CharProcs 1150 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 14 11 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 25 0 30 30 23 0 27 23 26 29 0 0 30 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 21 12 33 22 20 21 0 15 16 12 21 20 29 21 19 ] >> endobj 1149 0 obj << /Type /Encoding /Differences [32/G20 45/G2d /G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c 78/G4e /G4f /G50 82/G52 /G53 /G54 /G55 88/G58 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1150 0 obj << /G20 1151 0 R /G2d 1152 0 R /G2e 1153 0 R /G2f 1154 0 R /G30 1155 0 R /G31 1156 0 R /G32 1157 0 R /G33 1158 0 R /G34 1159 0 R /G35 1160 0 R /G36 1161 0 R /G37 1162 0 R /G38 1163 0 R /G39 1164 0 R /G41 1165 0 R /G42 1166 0 R /G43 1167 0 R /G44 1168 0 R /G45 1169 0 R /G46 1170 0 R /G47 1171 0 R /G48 1172 0 R /G49 1173 0 R /G4c 1174 0 R /G4e 1175 0 R /G4f 1176 0 R /G50 1177 0 R /G52 1178 0 R /G53 1179 0 R /G54 1180 0 R /G55 1181 0 R /G58 1182 0 R /G61 1183 0 R /G62 1184 0 R /G63 1185 0 R /G64 1186 0 R /G65 1187 0 R /G66 1188 0 R /G67 1189 0 R /G68 1190 0 R /G69 1191 0 R /G6b 1192 0 R /G6c 1193 0 R /G6d 1194 0 R /G6e 1195 0 R /G6f 1196 0 R /G70 1197 0 R /G72 1198 0 R /G73 1199 0 R /G74 1200 0 R /G75 1201 0 R /G76 1202 0 R /G77 1203 0 R /G78 1204 0 R /G79 1205 0 R >> endobj 1151 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1152 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1153 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1154 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1155 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1156 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1157 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1158 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1159 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1160 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1161 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1162 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1163 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1164 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1165 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1166 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1167 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1168 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1169 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1170 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1171 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1172 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1173 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1174 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1175 0 obj << /Length 148 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1176 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1177 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1179 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1180 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1181 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1182 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 1183 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1184 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1185 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1186 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1187 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1188 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1189 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1190 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1191 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1192 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1193 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1194 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1195 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1196 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1197 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1198 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1199 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1200 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1201 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1202 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1203 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1204 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1205 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1148 0 obj << /Name /T55 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1206 0 R /CharProcs 1207 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 21 21 21 0 0 0 0 21 0 0 0 0 0 0 0 30 0 30 0 28 0 33 0 16 0 0 28 40 0 33 25 0 30 23 0 0 0 0 0 0 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1206 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 /G32 /G33 /G34 57/G39 65/G41 67/G43 69/G45 71/G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1207 0 obj << /G20 1208 0 R /G28 1209 0 R /G29 1210 0 R /G30 1211 0 R /G31 1212 0 R /G32 1213 0 R /G33 1214 0 R /G34 1215 0 R /G39 1216 0 R /G41 1217 0 R /G43 1218 0 R /G45 1219 0 R /G47 1220 0 R /G49 1221 0 R /G4c 1222 0 R /G4d 1223 0 R /G4f 1224 0 R /G50 1225 0 R /G52 1226 0 R /G53 1227 0 R /G5f 1228 0 R /G61 1229 0 R /G63 1230 0 R /G64 1231 0 R /G65 1232 0 R /G66 1233 0 R /G69 1234 0 R /G6e 1235 0 R /G6f 1236 0 R /G72 1237 0 R /G74 1238 0 R /G76 1239 0 R /G78 1240 0 R >> endobj 1208 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1209 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1210 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1211 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1212 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1213 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 1214 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1215 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 1216 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 1217 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1218 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1219 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1220 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1221 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1222 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1223 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1225 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1226 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1227 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1228 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1229 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1230 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1231 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1232 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1233 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1234 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1235 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1237 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1238 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1239 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1240 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1242 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 727 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*&X`R%Qie) .W _g˫ҫ]{n\ ؍h^i bqy9z =V5r0[x ˙hY]f13d:\g\Wl~zj5 C}A8;71cuNy^gg~6U_/Sw#˺j=_UVN $-ZĿK.P;حL&+k80CB?kƽ[6$:hoKtB n( fK dR/G骀Lh}Ir4$2S4k+ˍ_ PuOGO 5EL6%bT3 -iݛf614֌KMQ5SUXտֲ4,<5݃r7 qtEwklvRףaXHKVDV݋@5lS~2n[G`U]\\}|TMT/4eu2XռWIbluyS}u7h3QXVzFZh:.+ endstream endobj 1243 0 obj << /Length 2153 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@fAAhd>)l['`!hbP<kq)!FZg@ٌ07 c(@17 `k*#ӼtaFpHl*g;ȲdIr< |4(JܪCd5Ȃ Y4!y6D;k!CԜ9Cp@8 #'#7S+4MTA7|'RdQ>ɱ@*H:H0)4-67QbXD$YuU$ *3C : 4h/6yp͑ ǎ@osرY7ffOx4˃&+Z6]7(d uS?I€4լ)ա8V}dMcseCۘOp@)0C9cCx_:I.WJp1õsfUv!'lHl^7un7m7فm>n;Q}[w+kRx|g%YN}6)QKڐ zg⃘:R &_GG;#001^\Uryn:w?~I~uA81ӑӍ@[{N%rho[o;F]Ku;S1ZiL1[h4X/3`zk<8N /A ; 0DdKTCrV c;Ԏ%0AGazVH99&4_m kRKP@oM@4u蠂/yW-`A2$"S]#,eNMj[jN1^ !f#/N)d^Bq! ($uHO3Fl 0UB ebEΠne B_yH?@8rɷ oFX).j<1*!n ;9aAyrZ'+#x ( = RAr3U#lիW m.R ̅0B! .lt%@GsR 0NL3PLV27Z.UD,=0VmeG^U=|r!9@iP%= dbNMk-luᑛY7,5xT걧&UJQW+(Z5iv!eYCiGT `m HUT4T$kӒOifϩ5TtS؛:S_kd [- .ZEm@EfSv^E ٩&ƒb u_1` TYoA0רBi\ZZ(=Id)u$RUKU MpN$ 4[S^ߪ@ (pRM;Uq78VFBWtݍI!SccE^44M8e9+&SⓓvQ| 0rZC:J)׹턆C.e{9sɔg]3#t=hZ2Wg͜0?̫b]'ʊbag58zvJHU\ۯ57p;YȱpUbZV]D-w>)U 7g7HnH!%wߋmp ctm/UmJ#iUɔ$buykmB$H%"I]9pȸ#y%|)ƛhu' ~;qG'r.Ss=1BA endstream endobj 1244 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T56 1245 0 R /T57 1246 0 R /T58 1247 0 R >> >> endobj 1245 0 obj << /Name /T56 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 122 /Encoding 1248 0 R /CharProcs 1249 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 14 11 0 21 21 21 21 21 21 21 21 21 21 0 12 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 25 37 0 30 23 0 27 23 26 29 30 0 30 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 19 ] >> endobj 1248 0 obj << /Type /Encoding /Differences [32/G20 45/G2d /G2e 48/G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 59/G3b 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 /G54 /G55 /G56 88/G58 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 /G7a ] >> endobj 1249 0 obj << /G20 1250 0 R /G2d 1251 0 R /G2e 1252 0 R /G30 1253 0 R /G31 1254 0 R /G32 1255 0 R /G33 1256 0 R /G34 1257 0 R /G35 1258 0 R /G36 1259 0 R /G37 1260 0 R /G38 1261 0 R /G39 1262 0 R /G3b 1263 0 R /G41 1264 0 R /G42 1265 0 R /G43 1266 0 R /G44 1267 0 R /G45 1268 0 R /G46 1269 0 R /G47 1270 0 R /G48 1271 0 R /G49 1272 0 R /G4c 1273 0 R /G4d 1274 0 R /G4f 1275 0 R /G50 1276 0 R /G52 1277 0 R /G53 1278 0 R /G54 1279 0 R /G55 1280 0 R /G56 1281 0 R /G58 1282 0 R /G61 1283 0 R /G62 1284 0 R /G63 1285 0 R /G64 1286 0 R /G65 1287 0 R /G66 1288 0 R /G67 1289 0 R /G68 1290 0 R /G69 1291 0 R /G6b 1292 0 R /G6c 1293 0 R /G6d 1294 0 R /G6e 1295 0 R /G6f 1296 0 R /G70 1297 0 R /G71 1298 0 R /G72 1299 0 R /G73 1300 0 R /G74 1301 0 R /G75 1302 0 R /G76 1303 0 R /G77 1304 0 R /G78 1305 0 R /G79 1306 0 R /G7a 1307 0 R >> endobj 1250 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1251 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1252 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1253 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1254 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1255 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1256 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1257 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1258 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1259 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1260 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1261 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1262 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1263 0 obj << /Length 99 /Filter /LZWDecode >> stream FBf  dAG B!bBm#$CF2@ hr:bD 8 ':%D QF%endstream endobj 1264 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1265 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1266 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1267 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1268 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1269 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1270 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1271 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1272 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1273 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1274 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1276 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1278 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1279 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1280 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1281 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 1282 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 1283 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1284 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1285 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1286 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1287 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1288 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1289 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1290 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1291 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1292 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1293 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1294 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1295 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1296 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1297 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1298 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1299 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1300 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1301 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1302 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1303 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1304 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1305 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1306 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1307 0 obj << /Length 122 /Filter /LZWDecode >> stream G"c 3  8#xDͰ&/+D.>B(I4@t9LI@=hF?TU_m!ߓ<aendstream endobj 1246 0 obj << /Name /T57 /Type /Font /Subtype /Type3 /FontBBox [0 -11 47 34 ] /FontMatrix [0.02 0 0 0.02 0 0] /FirstChar 32 /LastChar 118 /Encoding 1308 0 R /CharProcs 1309 0 R /Widths [13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 0 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 34 0 36 33 0 0 0 19 0 0 0 48 0 39 30 0 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 0 0 0 22 16 25 0 14 0 0 13 41 28 25 0 0 21 19 17 0 25 ] >> endobj 1308 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 50/G32 66/G42 68/G44 /G45 73/G49 77/G4d 79/G4f /G50 82/G52 97/G61 101/G65 /G66 /G67 105/G69 108/G6c /G6d /G6e /G6f 114/G72 /G73 /G74 118/G76 ] >> endobj 1309 0 obj << /G20 1310 0 R /G2e 1311 0 R /G32 1312 0 R /G42 1313 0 R /G44 1314 0 R /G45 1315 0 R /G49 1316 0 R /G4d 1317 0 R /G4f 1318 0 R /G50 1319 0 R /G52 1320 0 R /G61 1321 0 R /G65 1322 0 R /G66 1323 0 R /G67 1324 0 R /G69 1325 0 R /G6c 1326 0 R /G6d 1327 0 R /G6e 1328 0 R /G6f 1329 0 R /G72 1330 0 R /G73 1331 0 R /G74 1332 0 R /G76 1333 0 R >> endobj 1310 0 obj << /Length 16 /Filter /LZWDecode >> stream Fb  Tendstream endobj 1311 0 obj << /Length 81 /Filter /LZWDecode >> stream Fbd D(@dAG A"$B$ąqDHȈE.DI&#$I"haĈQF%endstream endobj 1312 0 obj << /Length 161 /Filter /LZWDecode >> stream c b,113lDI𑔄 HEBI&#"I"7x`v1>/1U+5<e`XvT=e|?ܔ%RA0dFendstream endobj 1313 0 obj << /Length 156 /Filter /LZWDecode >> stream Ƃc P11 b"B$ExL@/$C$D" "$DC$vzPjU0P*>ST;D?>'pWU\R/˝֕U):->,Q@endstream endobj 1314 0 obj << /Length 161 /Filter /LZWDecode >> stream c Ɛ116 3lDI "H 84MGS,DD:р}*J~SB*+d٬6[eJVp=ZR?m'xdFendstream endobj 1315 0 obj << /Length 156 /Filter /LZWDecode >> stream bc !b b8,VLf؁# "/ !pi$ :X$ NA O;BY},vS* v{}UՀ>>,rgVen(OJġ7xNp,|Q@@endstream endobj 1316 0 obj << /Length 106 /Filter /LZWDecode >> stream G"c@c1b b" B f؁É A 84MGS,@D'OP'%BQi4zU6Oh"Eendstream endobj 1317 0 obj << /Length 210 /Filter /LZWDecode >> stream c @dAGm`Ѓ$B$ąxHDH D" "$DC$ <'z"L~ SjOS,ζ٪v=mϭx  @e0siq={(",Q@@endstream endobj 1318 0 obj << /Length 194 /Filter /LZWDecode >> stream "d D1,h 2 ALf5FbhD(m11y^5$B@ hr:bd C|7\@8cD[ Q/{{_W{} tD60UkUbRS_E*E?:L#bpendstream endobj 1319 0 obj << /Length 147 /Filter /LZWDecode >> stream  D111 3"B$Ex@DHD"5"$DC$=^:D` (.x(JVUհ=&F|Phse-6kEn[Wcg,Q@endstream endobj 1320 0 obj << /Length 169 /Filter /LZWDecode >> stream c 𑘀11" 33lDI𑴄 84MGS,DD:w>߀:U2*>թ=CpS=, m,T6]+E]W*@ pO "Eendstream endobj 1321 0 obj << /Length 144 /Filter /LZWDecode >> stream d `H0@dAGxHm1!y^ 2!rB@ hr:bD ~> x/)a1?882-6pз D0QF%endstream endobj 1322 0 obj << /Length 135 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A|\``)" ˜j TpU5?0PH0QF#endstream endobj 1323 0 obj << /Length 129 /Filter /LZWDecode >> stream Fc 111 3lDIā HHEBI&#"I" ~E~T)TUJv^WV>@,Q@endstream endobj 1324 0 obj << /Length 168 /Filter /LZWDecode >> stream d F0af 2 ALeFp<&c6DIIܔP!"@y$ :X$ ~ߏp0(S*L)Ax?7qvt [%сEpQQ}`@{. &E18endstream endobj 1325 0 obj << /Length 110 /Filter /LZWDecode >> stream Fe Fb 113lDI(^H $$"!$DC$`G8Aj:E "$Y(endstream endobj 1326 0 obj << /Length 97 /Filter /LZWDecode >> stream Fbc 8b b< B f؁P E@I&# I">OT%EQT"Eendstream endobj 1327 0 obj << /Length 141 /Filter /LZWDecode >> stream "e 0a11C`S"B$ExHDHHE IeH~| @ K^SjrZl ~gZlT"E1endstream endobj 1328 0 obj << /Length 116 /Filter /LZWDecode >> stream e 1b b!" 2A3l@I a 1</$DC$pvr>) <j:}VV0dBendstream endobj 1329 0 obj << /Length 131 /Filter /LZWDecode >> stream d `8X11*  ЈQ!b"  ICIhr:b$ |>|@ UիtSӁ qE> stream "e@C1b b" B f؁I" 39MGS,@D?߂8  N?*L RUUH h>cࢌBendstream endobj 1331 0 obj << /Length 131 /Filter /LZWDecode >> stream G"d Df 2 ALb5FFCH4"c6D IaP!䤒hr:bd :`h$;/ ~@xFCL$bpendstream endobj 1332 0 obj << /Length 120 /Filter /LZWDecode >> stream Fb #a dAGk P9!bBk#$Cb yCH$u2ĉ$A|?#>xT*UNUT?",Q@@endstream endobj 1333 0 obj << /Length 139 /Filter /LZWDecode >> stream @-CHP@dAG.  Ё!bB(i#$Br2@ hr:bD @ Es=Mxz~Vߕ?d6DdcD (Ġ endstream endobj 1247 0 obj << /Name /T58 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1334 0 R /CharProcs 1335 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 0 21 0 0 0 0 0 21 0 0 0 0 0 0 0 30 0 30 30 28 0 33 0 16 0 0 28 40 30 33 25 34 0 23 28 30 0 0 0 0 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1334 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 51/G33 57/G39 65/G41 67/G43 /G44 /G45 71/G47 73/G49 76/G4c /G4d /G4e /G4f /G50 /G51 83/G53 /G54 /G55 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1335 0 obj << /G20 1336 0 R /G28 1337 0 R /G29 1338 0 R /G30 1339 0 R /G31 1340 0 R /G33 1341 0 R /G39 1342 0 R /G41 1343 0 R /G43 1344 0 R /G44 1345 0 R /G45 1346 0 R /G47 1347 0 R /G49 1348 0 R /G4c 1349 0 R /G4d 1350 0 R /G4e 1351 0 R /G4f 1352 0 R /G50 1353 0 R /G51 1354 0 R /G53 1355 0 R /G54 1356 0 R /G55 1357 0 R /G5f 1358 0 R /G61 1359 0 R /G63 1360 0 R /G64 1361 0 R /G65 1362 0 R /G66 1363 0 R /G69 1364 0 R /G6e 1365 0 R /G6f 1366 0 R /G72 1367 0 R /G74 1368 0 R /G76 1369 0 R /G78 1370 0 R >> endobj 1336 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1337 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1338 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1339 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1340 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1341 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1342 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 1343 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1344 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1345 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 1346 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1347 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1348 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1349 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1350 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 1352 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1353 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1354 0 obj << /Length 184 /Filter /LZWDecode >> stream Ƃd 88r 2 ALgBhD(m11y^0#RB@ hr:bd @??xU ~W@e~Y֋Uln6+ jW8*}EP>?) :О.'(?LșF ( endstream endobj 1355 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1356 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 1358 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1359 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1360 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1361 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1362 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1363 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1364 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1365 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1367 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1368 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1369 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1370 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1372 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 709 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*g1I22-TkjŻq4%3є5OkT4D*Ʊc}CiE i1Oѵ!r<8ΐLPG(@E5\wY=@TL%AZѨ\uN%;`4C5bV)[gaUG%A/cNYs"eipR=&3N[hBSiX5f #f h7emE4TTjU}[nO(M\x`m 4Iu,UΙNLeO%jF*" endstream endobj 1373 0 obj << /Length 1842 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!C A9n rj!@<4aAP" qdрfGF@0C` ('+ʽF(4'DZgca%!&cx: #87 oQt&!Nal:2#X@)"- 2T`|!N4/NԴ: =AQKT-QS5Tu#uZS!@0J@t>OA6SXLUp7ǡ7rUm&#h37Vk1WR=]41ېgSkk6c4p47)cxσa$ɅbvA#v󥡋4cB3y*3j^Ya9}s&exY&ʣ`<2|Z]ɧ\ѵWLMXbz+ƣ0͵a7,!vV;w@bLUV3_7er-mh6rz<[Bea_J)L77R:َ904]w3UAkut7:xv>xsRj9d5v/f[q\c'e!V<3"d6 ;t}k:VZhP%@*+%;dSI ]`(fd0T#t$aR5omC.: cЄLFlY@4(:wRLI *Cfgl6Qih Je 3B8ԑGR82 bښ$DJ}! qr!hF dvuFa˯#}KZ$vtO!6I'6 -RX?KuAk0 *n_@\1S  (xlM] 4C3^ep}uTOlbN#Q,RH8Y(n1 i ӐAVz O@@JVD˘A{SHT@ z _#bUAE`@ L2p(| JtT=E y*S^x$> /Font << /F2 7 0 R /F4 8 0 R /T59 1375 0 R >> >> endobj 1375 0 obj << /Name /T59 /Type /Font /Subtype /Type3 /FontBBox [-4 -9 37 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1378 0 R /CharProcs 1379 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 0 14 0 0 25 37 0 30 23 0 27 23 26 29 30 0 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 11 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 ] >> endobj 1378 0 obj << /Type /Encoding /Differences [32/G20 46/G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 /G54 /G55 /G56 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 /G6a /G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1379 0 obj << /G20 1380 0 R /G2e 1381 0 R /G2f 1382 0 R /G30 1383 0 R /G31 1384 0 R /G32 1385 0 R /G33 1386 0 R /G34 1387 0 R /G35 1388 0 R /G36 1389 0 R /G37 1390 0 R /G38 1391 0 R /G39 1392 0 R /G41 1393 0 R /G42 1394 0 R /G43 1395 0 R /G44 1396 0 R /G45 1397 0 R /G46 1398 0 R /G47 1399 0 R /G49 1400 0 R /G4c 1401 0 R /G4d 1402 0 R /G4f 1403 0 R /G50 1404 0 R /G52 1405 0 R /G53 1406 0 R /G54 1407 0 R /G55 1408 0 R /G56 1409 0 R /G61 1410 0 R /G62 1411 0 R /G63 1412 0 R /G64 1413 0 R /G65 1414 0 R /G66 1415 0 R /G67 1416 0 R /G68 1417 0 R /G69 1418 0 R /G6a 1419 0 R /G6b 1420 0 R /G6c 1421 0 R /G6d 1422 0 R /G6e 1423 0 R /G6f 1424 0 R /G70 1425 0 R /G71 1426 0 R /G72 1427 0 R /G73 1428 0 R /G74 1429 0 R /G75 1430 0 R /G76 1431 0 R /G77 1432 0 R /G78 1433 0 R /G79 1434 0 R >> endobj 1380 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1381 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1382 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1383 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1384 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1385 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1386 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1387 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1388 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1389 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1390 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1391 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1392 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1393 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1394 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1395 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1396 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1397 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1398 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1399 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1400 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1401 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1402 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1404 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1406 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1407 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1408 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1409 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 1410 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1411 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1412 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1413 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1414 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1415 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1416 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1417 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1418 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1419 0 obj << /Length 119 /Filter /LZWDecode >> stream F"Z4DdAGe xHm1!y\A!C $u2ĉ$A P'EBj:V8p@"DY(endstream endobj 1420 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1421 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1422 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1423 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1424 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1425 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1426 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1427 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1428 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1429 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1430 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1431 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1432 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1433 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1434 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1436 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 733 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*g'B'J C-6Xe&C咻dR_^`;:Tĭw[uBc&Ld/K3,jfo/pr׬ǃۤ9·Ks~ڱi6!`1xK{Þh۞{vx.g]ֵu,k`5}\ufv|{~yK5/ے40cb?-cx@80(0sRBOq-DlK 1k(T8>or -|{0 P G4TI(M3^'" .iD$FJ /,E)G6+3J3cg=ˋ+d,1No:L14+9;QCѨSMAU&}<ʱi_SU k{ *4]dڸ42ҏQKNj?<(}'dGV R]KZ\v4M|A^WML5SWv/M^9eaxU\T##v`Xr)w՗ SV= =X˒MJy\ exzG_t[%Ye.~akzƺ endstream endobj 1437 0 obj << /Length 2013 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1< ՚oԺMj࣫1؇\k MkyK ?λZ;ei4üi80Pw; ljL{0\eKnmvt3ݜFjùtoȵ  endstream endobj 1438 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T60 1439 0 R /T61 1440 0 R >> >> endobj 1439 0 obj << /Name /T60 /Type /Font /Subtype /Type3 /FontBBox [-4 -9 33 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 122 /Encoding 1441 0 R /CharProcs 1442 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 14 0 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 0 0 0 25 0 30 0 23 0 27 23 26 29 30 0 0 0 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 11 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 19 ] >> endobj 1441 0 obj << /Type /Encoding /Differences [32/G20 45/G2d 47/G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 76/G4c 78/G4e 80/G50 82/G52 /G53 /G54 /G55 /G56 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 /G6a /G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 /G7a ] >> endobj 1442 0 obj << /G20 1443 0 R /G2d 1444 0 R /G2f 1445 0 R /G30 1446 0 R /G31 1447 0 R /G32 1448 0 R /G33 1449 0 R /G34 1450 0 R /G35 1451 0 R /G36 1452 0 R /G37 1453 0 R /G38 1454 0 R /G39 1455 0 R /G41 1456 0 R /G42 1457 0 R /G43 1458 0 R /G44 1459 0 R /G45 1460 0 R /G46 1461 0 R /G47 1462 0 R /G48 1463 0 R /G4c 1464 0 R /G4e 1465 0 R /G50 1466 0 R /G52 1467 0 R /G53 1468 0 R /G54 1469 0 R /G55 1470 0 R /G56 1471 0 R /G61 1472 0 R /G62 1473 0 R /G63 1474 0 R /G64 1475 0 R /G65 1476 0 R /G66 1477 0 R /G67 1478 0 R /G68 1479 0 R /G69 1480 0 R /G6a 1481 0 R /G6b 1482 0 R /G6c 1483 0 R /G6d 1484 0 R /G6e 1485 0 R /G6f 1486 0 R /G70 1487 0 R /G71 1488 0 R /G72 1489 0 R /G73 1490 0 R /G74 1491 0 R /G75 1492 0 R /G76 1493 0 R /G77 1494 0 R /G78 1495 0 R /G79 1496 0 R /G7a 1497 0 R >> endobj 1443 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1444 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1445 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1446 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1447 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1448 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1449 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1450 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1451 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1452 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1453 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1454 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1455 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1456 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1457 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1458 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1459 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1460 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1461 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1462 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1463 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1464 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1465 0 obj << /Length 148 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1466 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1468 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1469 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1470 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1471 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 1472 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1473 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1474 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1475 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1476 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1477 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1478 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1479 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1480 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1481 0 obj << /Length 119 /Filter /LZWDecode >> stream F"Z4DdAGe xHm1!y\A!C $u2ĉ$A P'EBj:V8p@"DY(endstream endobj 1482 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1483 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1484 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1485 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1486 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1487 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1488 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1489 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1490 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1491 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1492 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1493 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1494 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1495 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1496 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1497 0 obj << /Length 122 /Filter /LZWDecode >> stream G"c 3  8#xDͰ&/+D.>B(I4@t9LI@=hF?TU_m!ߓ<aendstream endobj 1440 0 obj << /Name /T61 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1498 0 R /CharProcs 1499 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 21 21 0 0 0 0 0 0 0 0 0 0 0 0 0 30 0 30 0 28 0 33 0 16 0 0 28 40 0 33 25 0 30 23 0 0 0 0 0 0 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1498 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 /G32 /G33 65/G41 67/G43 69/G45 71/G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1499 0 obj << /G20 1500 0 R /G28 1501 0 R /G29 1502 0 R /G30 1503 0 R /G31 1504 0 R /G32 1505 0 R /G33 1506 0 R /G41 1507 0 R /G43 1508 0 R /G45 1509 0 R /G47 1510 0 R /G49 1511 0 R /G4c 1512 0 R /G4d 1513 0 R /G4f 1514 0 R /G50 1515 0 R /G52 1516 0 R /G53 1517 0 R /G5f 1518 0 R /G61 1519 0 R /G63 1520 0 R /G64 1521 0 R /G65 1522 0 R /G66 1523 0 R /G69 1524 0 R /G6e 1525 0 R /G6f 1526 0 R /G72 1527 0 R /G74 1528 0 R /G76 1529 0 R /G78 1530 0 R >> endobj 1500 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1501 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1502 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1503 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1504 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1505 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 1506 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1507 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1508 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1509 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1510 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1511 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1512 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1513 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1515 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1516 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1517 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1518 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1519 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1520 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1521 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1522 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1523 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1524 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1525 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1527 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1528 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1529 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1530 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1532 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 758 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*.J3n]Zce2k-*{$V;V>UlV9t?-ֽ?d4Sd= Sot@ ) l0DD: l*P2 kJ>/ho lĢ+ʑ. DfbʑEӣ\D1눐.|PEIғ m|#z;=i8e2ERS].2º Bҍ-GL$?BK3t]SNP+KURnmd>5";U4)UҔ7\mYFBk l/4ͬW\4/,TD𘽔Տ Dpuy/I#F%K^ܰR pWUݱxT4\r ^SR#Ow<ÖCS8ccx$=jIKx*ݶ%kse[U _TV]fuuahW\c%yU\* gQ6UPhū^M$_Y;0V#lӲms۶mN⫊ endstream endobj 1533 0 obj << /Length 2200 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1KK `ɄX\ׇ]jbGض=eHCMeYeUāƝ$#}dŻ(61u>MMÒ #ҥXe7ju^4IZ x]iZ2Q6((H[g֭9c9{*Vը006* ҍ:" , Cu45DUhL3"Hn9'^. y]ٜB@G16CbZ8VFCe.R2 ]v[gtS`4GRs*/F7  J50޿-DW^ ,՞WA pUKnЊtFa:1ML\pnp!~UR r.MCXLioֽ 3v̔#S$N y(WXZ$~2HBajq@'P e$leW1 :_BCW`KEDp#F A0Dì^d*ʋ012<:I!MG#CPCQ)$S*mNIyRY?m9#2XU1l/n P@pgfsBD%9 ]FHݐul!P<.(i$IĢ#(C:tLT8T鈤jVca*p@CzUQ]3*-`(A% paHZm['ecu]+V`TZo=tpy xb b}0SPu A)mcd)d(|$ɼk+*L,vi+;B=։vJ82GGk5-D.*kMh+2[YѪ l-m+Rݘ_vH4RT7HC()H7LNW7~AUP 6`"H'a%aI;{mL,1AG@ÅM@U6Sܬa^!ƗSctFB+saJ-\w&㐥y]@^ҲDlo@|rAV ۠]^k3|Gf.29\ .׳d=a=.) 0O@ AX͈:LjH9mpuxA t1m(;L}4NUFt'P'ƣ[O?@hP~thK1$a)5zúSRXC-hRH>!b +_p\K2^-8<'VISwJ8\E0V0D~$^,T8Sn{cB]DZysbxrAȁb.utt +^9 D _ZF=vuu aӶD{E19MD):읣 9R  endstream endobj 1534 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T62 1535 0 R /T63 1536 0 R >> >> endobj 1535 0 obj << /Name /T62 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1537 0 R /CharProcs 1538 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 14 11 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 0 29 14 0 0 25 37 30 30 23 0 27 23 26 29 0 0 30 30 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 ] >> endobj 1537 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 45/G2d /G2e /G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 72/G48 /G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 88/G58 /G59 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1538 0 obj << /G20 1539 0 R /G28 1540 0 R /G29 1541 0 R /G2d 1542 0 R /G2e 1543 0 R /G2f 1544 0 R /G30 1545 0 R /G31 1546 0 R /G32 1547 0 R /G33 1548 0 R /G34 1549 0 R /G35 1550 0 R /G36 1551 0 R /G37 1552 0 R /G38 1553 0 R /G39 1554 0 R /G41 1555 0 R /G42 1556 0 R /G43 1557 0 R /G44 1558 0 R /G45 1559 0 R /G46 1560 0 R /G48 1561 0 R /G49 1562 0 R /G4c 1563 0 R /G4d 1564 0 R /G4e 1565 0 R /G4f 1566 0 R /G50 1567 0 R /G52 1568 0 R /G53 1569 0 R /G54 1570 0 R /G55 1571 0 R /G58 1572 0 R /G59 1573 0 R /G61 1574 0 R /G62 1575 0 R /G63 1576 0 R /G64 1577 0 R /G65 1578 0 R /G66 1579 0 R /G67 1580 0 R /G68 1581 0 R /G69 1582 0 R /G6b 1583 0 R /G6c 1584 0 R /G6d 1585 0 R /G6e 1586 0 R /G6f 1587 0 R /G70 1588 0 R /G71 1589 0 R /G72 1590 0 R /G73 1591 0 R /G74 1592 0 R /G75 1593 0 R /G76 1594 0 R /G77 1595 0 R /G78 1596 0 R /G79 1597 0 R >> endobj 1539 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1540 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 1541 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 1542 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1543 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1544 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1545 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1546 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1547 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1548 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1549 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1550 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1551 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1552 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1553 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1554 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1555 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1556 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1557 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1558 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1559 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1560 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1561 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1562 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1563 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1564 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1566 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1567 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1569 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1570 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1571 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1572 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 1573 0 obj << /Length 151 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ x ?}oC:Ϫ8l~?Gn\-s?D"E|endstream endobj 1574 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1575 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1576 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1577 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1578 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1579 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1580 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1581 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1582 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1583 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1584 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1585 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1586 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1587 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1588 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1589 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1590 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1591 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1592 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1593 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1594 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1595 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1596 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1597 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1536 0 obj << /Name /T63 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1598 0 R /CharProcs 1599 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 21 21 0 21 0 21 0 21 0 0 0 0 0 0 0 30 0 30 30 28 0 33 0 16 0 0 28 40 0 33 25 0 30 23 28 0 0 0 0 31 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1598 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 /G32 /G33 53/G35 55/G37 57/G39 65/G41 67/G43 /G44 /G45 71/G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 /G54 89/G59 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1599 0 obj << /G20 1600 0 R /G28 1601 0 R /G29 1602 0 R /G30 1603 0 R /G31 1604 0 R /G32 1605 0 R /G33 1606 0 R /G35 1607 0 R /G37 1608 0 R /G39 1609 0 R /G41 1610 0 R /G43 1611 0 R /G44 1612 0 R /G45 1613 0 R /G47 1614 0 R /G49 1615 0 R /G4c 1616 0 R /G4d 1617 0 R /G4f 1618 0 R /G50 1619 0 R /G52 1620 0 R /G53 1621 0 R /G54 1622 0 R /G59 1623 0 R /G5f 1624 0 R /G61 1625 0 R /G63 1626 0 R /G64 1627 0 R /G65 1628 0 R /G66 1629 0 R /G69 1630 0 R /G6e 1631 0 R /G6f 1632 0 R /G72 1633 0 R /G74 1634 0 R /G76 1635 0 R /G78 1636 0 R >> endobj 1600 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1601 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1602 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1603 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1604 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1605 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 1606 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1607 0 obj << /Length 148 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH(* w Sz`O @џ\U eR=HB (` endstream endobj 1608 0 obj << /Length 133 /Filter /LZWDecode >> stream "eAFCb bd Lf؁I" 89MGS,@D0 P'{?,.Lq*O=2T31?L)fU"Eendstream endobj 1609 0 obj << /Length 146 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=c. A$˜Sj5 IP59 ?=`vt|"$Y(endstream endobj 1610 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1611 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1612 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 1613 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1614 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1615 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1616 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1617 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1619 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1620 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1621 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1622 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream "FCb b!- Ef؁I"F884MGS,@Do>8†8xLJL>7J^u 9}}OX?u]/OctcࢌBendstream endobj 1624 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1625 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1626 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1627 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1628 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1629 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1630 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1631 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1633 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1634 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1635 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1636 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1638 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 641 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*g1Xd3 %~]U+p{El&RiTO=' EUfnXe_nW*; gp\N9n ?Ik;?[8>{kصp=MVg:9OHۚ緓lԥ{ڱo2ؾ/R:;Y *.܅zoP7CC$3mݮB0Fc=n[ #0 Q!\t0@߷S\\rLA1LicErK#DɒC9LqlIRn=FJL 9n7#^3L,QmDn1E JoğN;>PS-[ISeM,I/G5[V OQu͓#YqU6MT/-Ze[vI[Mn\o5q\u]w5F}x^uM{^赟|_`h{h6 ahe#|w endstream endobj 1639 0 obj << /Length 1767 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!C A9nd!hbP<DcƫA^!\G; #p0(@1h2@07JƲq2ȬBiUwFlqr | [#H2L(*Lf0|$3)47"(,xL̲7Pd6%1?;Qє b5$IT<:  0@86cPOÝ_4M0W1]w:ٱmEmbὓcYM#Gp' #,l 02P:*6uyx1b_ eHUaaXu _]Fx@-f-xfkceX4>Hχv/kܥ8^Ym_7ܗv: ($74ωW~ٌ!cC킎c8۔m9_Gqەfg8 8*9!d26:_1I{> /Font << /F2 7 0 R /F4 8 0 R /T64 1641 0 R /T65 1642 0 R >> >> endobj 1641 0 obj << /Name /T64 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1643 0 R /CharProcs 1644 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 11 0 11 0 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 29 14 0 0 25 37 0 30 23 0 27 23 0 29 0 39 30 30 25 0 0 0 0 21 0 19 20 18 21 18 13 21 22 12 0 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 ] >> endobj 1643 0 obj << /Type /Encoding /Differences [32/G20 44/G2c 46/G2e 48/G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 /G48 /G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 85/G55 87/G57 /G58 /G59 /G5a 95/G5f 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1644 0 obj << /G20 1645 0 R /G2c 1646 0 R /G2e 1647 0 R /G30 1648 0 R /G31 1649 0 R /G32 1650 0 R /G33 1651 0 R /G34 1652 0 R /G35 1653 0 R /G36 1654 0 R /G37 1655 0 R /G38 1656 0 R /G39 1657 0 R /G41 1658 0 R /G42 1659 0 R /G43 1660 0 R /G44 1661 0 R /G45 1662 0 R /G46 1663 0 R /G47 1664 0 R /G48 1665 0 R /G49 1666 0 R /G4c 1667 0 R /G4d 1668 0 R /G4f 1669 0 R /G50 1670 0 R /G52 1671 0 R /G53 1672 0 R /G55 1673 0 R /G57 1674 0 R /G58 1675 0 R /G59 1676 0 R /G5a 1677 0 R /G5f 1678 0 R /G61 1679 0 R /G62 1680 0 R /G63 1681 0 R /G64 1682 0 R /G65 1683 0 R /G66 1684 0 R /G67 1685 0 R /G68 1686 0 R /G69 1687 0 R /G6b 1688 0 R /G6c 1689 0 R /G6d 1690 0 R /G6e 1691 0 R /G6f 1692 0 R /G70 1693 0 R /G71 1694 0 R /G72 1695 0 R /G73 1696 0 R /G74 1697 0 R /G75 1698 0 R /G76 1699 0 R /G77 1700 0 R /G78 1701 0 R /G79 1702 0 R >> endobj 1645 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1646 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 1647 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1648 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1649 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1650 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1651 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1652 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1653 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1654 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1655 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1656 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1657 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1658 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1659 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1660 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1661 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1662 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1663 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1664 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1665 0 obj << /Length 120 /Filter /LZWDecode >> stream "    8X,* 1a"L<^WcĈHN,tQ@endstream endobj 1666 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1667 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1668 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1670 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1672 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1673 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1674 0 obj << /Length 194 /Filter /LZWDecode >> stream "b B@dAGpxHm1!y\A!!CI $u2ĉ$Ap~;Q*qCӏ yTA՞Y\+ ~,`al~/:| ?-dſw(qOH#bPendstream endobj 1675 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 1676 0 obj << /Length 151 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ x ?}oC:Ϫ8l~?Gn\-s?D"E|endstream endobj 1677 0 obj << /Length 143 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"w=8 m[Au6Q_Շi]~6vcBendstream endobj 1678 0 obj << /Length 78 /Filter /LZWDecode >> stream "@- Pq11\ 2B3lDI(^HHHEJ/$DC$ "E1endstream endobj 1679 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1680 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1681 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1682 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1683 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1684 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1685 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1686 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1687 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1688 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1689 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1690 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1691 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1692 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1693 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1694 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1695 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1696 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1697 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1698 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1699 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1700 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1701 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1702 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1642 0 obj << /Name /T65 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 120 /Encoding 1703 0 R /CharProcs 1704 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 0 0 0 0 21 21 0 21 21 0 0 0 0 0 0 0 0 0 0 0 0 30 0 30 0 28 0 33 0 16 0 0 28 40 0 33 25 0 30 23 0 0 0 0 0 0 0 0 0 0 0 21 0 21 0 19 23 19 14 0 0 12 0 0 0 0 23 20 0 0 19 0 14 0 20 0 20 ] >> endobj 1703 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 48/G30 /G31 51/G33 /G34 65/G41 67/G43 69/G45 71/G47 73/G49 76/G4c /G4d 79/G4f /G50 82/G52 /G53 95/G5f 97/G61 99/G63 /G64 /G65 /G66 105/G69 110/G6e /G6f 114/G72 116/G74 118/G76 120/G78 ] >> endobj 1704 0 obj << /G20 1705 0 R /G28 1706 0 R /G29 1707 0 R /G30 1708 0 R /G31 1709 0 R /G33 1710 0 R /G34 1711 0 R /G41 1712 0 R /G43 1713 0 R /G45 1714 0 R /G47 1715 0 R /G49 1716 0 R /G4c 1717 0 R /G4d 1718 0 R /G4f 1719 0 R /G50 1720 0 R /G52 1721 0 R /G53 1722 0 R /G5f 1723 0 R /G61 1724 0 R /G63 1725 0 R /G64 1726 0 R /G65 1727 0 R /G66 1728 0 R /G69 1729 0 R /G6e 1730 0 R /G6f 1731 0 R /G72 1732 0 R /G74 1733 0 R /G76 1734 0 R /G78 1735 0 R >> endobj 1705 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1706 0 obj << /Length 126 /Filter /LZWDecode >> stream Fd (0@dAGhD(m1!y^2!C $u2ĉ$AH?Ão1x!50;«WVkZvM5@D0QF%endstream endobj 1707 0 obj << /Length 128 /Filter /LZWDecode >> stream F@-#!r 2 ANh@m11y^2!CI $u2$Ax17›M*50WVkZuӎEBOdY(endstream endobj 1708 0 obj << /Length 130 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=s.a)MӀoNTVkv_RitZ;?B (` endstream endobj 1709 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1710 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1711 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 1712 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1713 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1714 0 obj << /Length 144 /Filter /LZWDecode >> stream c Иb b!" B f؁#Y" !pi$ :X$ (g C|432~?/ 1SV*uyrUT@?YCSr@DQF!endstream endobj 1715 0 obj << /Length 168 /Filter /LZWDecode >> stream bd D(0@dAGxHm1!y^ P!%I4@t9L"I@p <π{<U0]dZ,[]j,u^zW.˻\_{$E1(endstream endobj 1716 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1717 0 obj << /Length 128 /Filter /LZWDecode >> stream c b b!" B f؁#i" !pi$ :X$ _>v,  DizU6OTZE՗@YX(endstream endobj 1718 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1720 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1721 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1722 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1723 0 obj << /Length 83 /Filter /LZWDecode >> stream "[D(0@dAGe ЈQ!bB,e#$r2@ hr:bD ?P"DY(endstream endobj 1724 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1725 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1726 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1727 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1728 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1729 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1730 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1732 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1733 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1734 0 obj << /Length 123 /Filter /LZWDecode >> stream  Z1@Ȁ11T f؉D/!p$ :X$ AO |xQ4M|ʓU5{]"E1endstream endobj 1735 0 obj << /Length 124 /Filter /LZWDecode >> stream  #b b<ͱ& /+‡1D>B(QI4@t9LI@ p  |w0S:Qp>.`"ǁEendstream endobj 1737 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 762 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*_@Ogd>gY*eoe63SR$U/0*مʮS d1lM+kroh~#IZi؋E!J-EkLkg:Qqyel~\0.1Ծvl{{'ťNO7׿]n-2C(+޺l;亮kAC D0y .jơp/E%Wı<"ʴM p C1Ld/\!Gl(l .D hc$,Flj4ܵl;,.o(&K3I4%1.2*Jn֬c/ϔ2OseAH %IJ>td=tlnQR۝Fs-9:uGK}4N;YCCCܵ=ov1pT-[U#&Vk㠘H,gdPն2ֶOpTY:Nշs7J{/8]zW-ċ׳[7ـXxDoQ2<xTeP 5dAp4^"8j[WH9{5xGxCqK 2qRMM["1NUlf۷Xe"+ endstream endobj 1738 0 obj << /Length 6317 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@hH2n6)2&h\:" , 1B ٌc9!7 0H7D0`nmZj!iGv2DZ!ȣ$rl(!(@>p6 㚎 0 * L7 3RCp -$8Mr @%.!72CpI4Cf1SED ^6p$ICu&3hZӼS4<'K2 07 4]7"q"+*,娅CQ} HBx/\7fM <6-@lasTBٶVDav#`"CzaAd2"yF9v&dxOQd/Yy#];?"ݠh5֕[Oe:xfʎk>Y9ƽb۶mNN37"{B)"|^=VvHiʆ\qVr\CysL9-._ߊ Y#UĩV]ᚯԎ6Gnn6Ǯf&IhC>˹^z*@'`̠Rd ]kkRIS*z*9J D(ͩd*Ga~t=n A@1t >#EK[GX&3G"C|w|8{kR Wk!L]9<.bVXage~8H 쬶 ӻEKpvUY kf;-06L7SnMirh&e՚CC`oR`??0iP0ЏC@u5-u hvkx:Rj.35<}^EHG$8C]b.Um"|RyÏ7Sϟw5bx}^:esۼu6uq4O\ك1c(%T~1`#L=5&֭P2c|@pDT"CjϯTW /`uNN&9M*ٕ] b̹hpKx |K}#Q[rb{a/\sY!Q[ߔ͢#&AED#l[[u]FyjmYsyrC> S|I\Ss[cIkSY\^|f\y㝽7;־MC]|CҔ[]9a7 Q[WiZ UfLkѭ]wǙ"Z~zQlޫ;1! I~$9E [N{. 3u&F}Q!?HE<B  QU@1c(r.AH42 1Q3xLXi d)$9 M8l5 F1iĦv1 "em!\5 t%|4\p5j]6n> /Font << /F2 7 0 R /F4 8 0 R /T66 1740 0 R /T67 1741 0 R /T68 1742 0 R >> >> endobj 1740 0 obj << /Name /T66 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 37 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1743 0 R /CharProcs 1744 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 14 11 0 21 21 21 21 21 21 0 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 0 14 0 0 25 37 30 30 23 0 27 23 26 29 0 0 30 30 0 0 0 0 0 0 0 19 20 18 21 18 13 21 22 12 0 0 12 33 22 20 21 0 15 16 12 21 20 29 21 19 ] >> endobj 1743 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c /G2d /G2e 48/G30 /G31 /G32 /G33 /G34 /G35 55/G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 73/G49 76/G4c /G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 88/G58 /G59 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 /G68 /G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1744 0 obj << /G20 1745 0 R /G28 1746 0 R /G29 1747 0 R /G2c 1748 0 R /G2d 1749 0 R /G2e 1750 0 R /G30 1751 0 R /G31 1752 0 R /G32 1753 0 R /G33 1754 0 R /G34 1755 0 R /G35 1756 0 R /G37 1757 0 R /G38 1758 0 R /G39 1759 0 R /G41 1760 0 R /G42 1761 0 R /G43 1762 0 R /G44 1763 0 R /G45 1764 0 R /G46 1765 0 R /G47 1766 0 R /G49 1767 0 R /G4c 1768 0 R /G4d 1769 0 R /G4e 1770 0 R /G4f 1771 0 R /G50 1772 0 R /G52 1773 0 R /G53 1774 0 R /G54 1775 0 R /G55 1776 0 R /G58 1777 0 R /G59 1778 0 R /G61 1779 0 R /G62 1780 0 R /G63 1781 0 R /G64 1782 0 R /G65 1783 0 R /G66 1784 0 R /G67 1785 0 R /G68 1786 0 R /G69 1787 0 R /G6c 1788 0 R /G6d 1789 0 R /G6e 1790 0 R /G6f 1791 0 R /G70 1792 0 R /G72 1793 0 R /G73 1794 0 R /G74 1795 0 R /G75 1796 0 R /G76 1797 0 R /G77 1798 0 R /G78 1799 0 R /G79 1800 0 R >> endobj 1745 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1746 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 1747 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 1748 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 1749 0 obj << /Length 79 /Filter /LZWDecode >> stream Fd #8T11 &3lDI(^H $$"!$DC$ y"Eendstream endobj 1750 0 obj << /Length 78 /Filter /LZWDecode >> stream F"f ab bH$V) f؁I"G !bx(^I&# I"B E1endstream endobj 1751 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1752 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1753 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1754 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1755 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1756 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1757 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1758 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1759 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1760 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1761 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1762 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1763 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1764 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1765 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1766 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1767 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1768 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1769 0 obj << /Length 182 /Filter /LZWDecode >> stream     8X,* 1a"L<^W@ĈH> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1771 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1772 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1774 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1775 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1776 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1777 0 obj << /Length 170 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ @}|?yӐzr`9?ϋeӞ@sg9>ç4^~>00`c>endstream endobj 1778 0 obj << /Length 151 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ x ?}oC:Ϫ8l~?Gn\-s?D"E|endstream endobj 1779 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1780 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1781 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1782 0 obj << /Length 140 /Filter /LZWDecode >> stream "-!Ȁ11, `3"B$ExXDHIB@IeHxO(88\ACQ(>_8W6=pAX56 D!b0endstream endobj 1783 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1784 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1785 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1786 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1787 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1788 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1789 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1790 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1791 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1792 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1793 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1794 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1795 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1796 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1797 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1798 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1799 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1800 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1741 0 obj << /Name /T67 /Type /Font /Subtype /Type3 /FontBBox [0 -11 47 23 ] /FontMatrix [0.02 0 0 0.02 0 0] /FirstChar 32 /LastChar 121 /Encoding 1801 0 R /CharProcs 1802 0 R /Widths [13 0 0 0 0 0 0 0 0 0 0 0 0 0 13 0 0 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 36 36 0 0 0 0 19 0 0 0 48 0 39 0 0 36 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 0 22 0 22 16 0 0 14 0 0 0 41 28 25 0 0 21 0 17 0 0 0 0 25 ] >> endobj 1801 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 67/G43 /G44 73/G49 77/G4d 79/G4f 82/G52 97/G61 99/G63 101/G65 /G66 105/G69 109/G6d /G6e /G6f 114/G72 116/G74 121/G79 ] >> endobj 1802 0 obj << /G20 1803 0 R /G2e 1804 0 R /G31 1805 0 R /G43 1806 0 R /G44 1807 0 R /G49 1808 0 R /G4d 1809 0 R /G4f 1810 0 R /G52 1811 0 R /G61 1812 0 R /G63 1813 0 R /G65 1814 0 R /G66 1815 0 R /G69 1816 0 R /G6d 1817 0 R /G6e 1818 0 R /G6f 1819 0 R /G72 1820 0 R /G74 1821 0 R /G79 1822 0 R >> endobj 1803 0 obj << /Length 16 /Filter /LZWDecode >> stream Fb  Tendstream endobj 1804 0 obj << /Length 81 /Filter /LZWDecode >> stream Fbd D(@dAG A"$B$ąqDHȈE.DI&#$I"haĈQF%endstream endobj 1805 0 obj << /Length 119 /Filter /LZWDecode >> stream i "@dAGo`Ѓ$B$ąqZDH D"K"$DC$z@?( 0>)4*LTj:}VVU tXbDY(endstream endobj 1806 0 obj << /Length 179 /Filter /LZWDecode >> stream d D1.dAGưhD(m1!y^2!cY!CDABI4@t9L"I@~_8@ w` bX[mr-{͢NQ+WjoAǸ?Md`Jendstream endobj 1807 0 obj << /Length 161 /Filter /LZWDecode >> stream c Ɛ116 3lDI "H 84MGS,DD:р}*J~SB*+d٬6[eJVp=ZR?m'xdFendstream endobj 1808 0 obj << /Length 106 /Filter /LZWDecode >> stream G"c@c1b b" B f؁É A 84MGS,@D'OP'%BQi4zU6Oh"Eendstream endobj 1809 0 obj << /Length 210 /Filter /LZWDecode >> stream c @dAGm`Ѓ$B$ąxHDH D" "$DC$ <'z"L~ SjOS,ζ٪v=mϭx  @e0siq={(",Q@@endstream endobj 1810 0 obj << /Length 194 /Filter /LZWDecode >> stream "d D1,h 2 ALf5FbhD(m11y^5$B@ hr:bd C|7\@8cD[ Q/{{_W{} tD60UkUbRS_E*E?:L#bpendstream endobj 1811 0 obj << /Length 169 /Filter /LZWDecode >> stream c 𑘀11" 33lDI𑴄 84MGS,DD:w>߀:U2*>թ=CpS=, m,T6]+E]W*@ pO "Eendstream endobj 1812 0 obj << /Length 144 /Filter /LZWDecode >> stream d `H0@dAGxHm1!y^ 2!rB@ hr:bD ~> x/)a1?882-6pз D0QF%endstream endobj 1813 0 obj << /Length 140 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A| `< R0 _ NTV\ q?P8wD#b0endstream endobj 1814 0 obj << /Length 135 /Filter /LZWDecode >> stream B-#!11  @m1y\A I!C $u2I$A|\``)" ˜j TpU5?0PH0QF#endstream endobj 1815 0 obj << /Length 129 /Filter /LZWDecode >> stream Fc 111 3lDIā HHEBI&#"I" ~E~T)TUJv^WV>@,Q@endstream endobj 1816 0 obj << /Length 110 /Filter /LZWDecode >> stream Fe Fb 113lDI(^H $$"!$DC$`G8Aj:E "$Y(endstream endobj 1817 0 obj << /Length 141 /Filter /LZWDecode >> stream "e 0a11C`S"B$ExHDHHE IeH~| @ K^SjrZl ~gZlT"E1endstream endobj 1818 0 obj << /Length 116 /Filter /LZWDecode >> stream e 1b b!" 2A3l@I a 1</$DC$pvr>) <j:}VV0dBendstream endobj 1819 0 obj << /Length 131 /Filter /LZWDecode >> stream d `8X11*  ЈQ!b"  ICIhr:b$ |>|@ UիtSӁ qE> stream "e@C1b b" B f؁I" 39MGS,@D?߂8  N?*L RUUH h>cࢌBendstream endobj 1821 0 obj << /Length 120 /Filter /LZWDecode >> stream Fb #a dAGk P9!bBk#$Cb yCH$u2ĉ$A|?#>xT*UNUT?",Q@@endstream endobj 1822 0 obj << /Length 160 /Filter /LZWDecode >> stream @-D!,d 2 ANphD(m11y^4!CDBI4@t9L2I@ ~p Sϧ3>?cm6 8;Cwa?{,, #"Eendstream endobj 1742 0 obj << /Name /T68 /Type /Font /Subtype /Type3 /FontBBox [0 -9 39 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1823 0 R /CharProcs 1824 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 21 21 21 0 0 0 0 0 0 0 0 0 0 0 0 0 30 29 30 30 0 0 0 0 16 0 33 0 40 30 33 25 0 30 23 28 30 0 0 0 0 0 0 0 0 0 0 0 21 23 19 23 19 14 21 0 12 0 0 12 34 23 20 23 0 19 16 14 22 0 0 0 21 ] >> endobj 1823 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 /G32 /G33 65/G41 /G42 /G43 /G44 73/G49 75/G4b 77/G4d /G4e /G4f /G50 82/G52 /G53 /G54 /G55 97/G61 /G62 /G63 /G64 /G65 /G66 /G67 105/G69 108/G6c /G6d /G6e /G6f /G70 114/G72 /G73 /G74 /G75 121/G79 ] >> endobj 1824 0 obj << /G20 1825 0 R /G2e 1826 0 R /G31 1827 0 R /G32 1828 0 R /G33 1829 0 R /G41 1830 0 R /G42 1831 0 R /G43 1832 0 R /G44 1833 0 R /G49 1834 0 R /G4b 1835 0 R /G4d 1836 0 R /G4e 1837 0 R /G4f 1838 0 R /G50 1839 0 R /G52 1840 0 R /G53 1841 0 R /G54 1842 0 R /G55 1843 0 R /G61 1844 0 R /G62 1845 0 R /G63 1846 0 R /G64 1847 0 R /G65 1848 0 R /G66 1849 0 R /G67 1850 0 R /G69 1851 0 R /G6c 1852 0 R /G6d 1853 0 R /G6e 1854 0 R /G6f 1855 0 R /G70 1856 0 R /G72 1857 0 R /G73 1858 0 R /G74 1859 0 R /G75 1860 0 R /G79 1861 0 R >> endobj 1825 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1826 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 1827 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1828 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=E? QV5 aXc>@>_-K."E1endstream endobj 1829 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1830 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1831 0 obj << /Length 141 /Filter /LZWDecode >> stream "c 11C`S"B$ExHDHHEDI&#"I"{ > Mө*=BP^ mlX)*feBQg6"E1endstream endobj 1832 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1833 0 obj << /Length 137 /Filter /LZWDecode >> stream  !  8Cx< @fyy" !q$ :Xy$ xt* *N{Kj_U*:FM)tj {?X(endstream endobj 1834 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1835 0 obj << /Length 167 /Filter /LZWDecode >> stream bc Ƃ@dAG 3lHI 𑜄 HHEBI&#$I"}Q\u+G@ez{%N)tV"@=@NH bPendstream endobj 1836 0 obj << /Length 180 /Filter /LZWDecode >> stream  D!116 3"B$Ex@DHɁB@IeH8'†> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 1838 0 obj << /Length 164 /Filter /LZWDecode >> stream bd D8Pr 2 AN xHm11y^ 9!c !CDBI4@t9L2I@` _ x*`+@>-{MmY+~[VGRSPdY(endstream endobj 1839 0 obj << /Length 126 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"xoF(M.M(t'??5j\Xk.{QF!endstream endobj 1840 0 obj << /Length 150 /Filter /LZWDecode >> stream  Cb b(< @f؁  A 8ĀMGS,@D~< }|QN.STjE@x1[xt`=X@TUO`)w  E1endstream endobj 1841 0 obj << /Length 162 /Filter /LZWDecode >> stream bd `С@dAGxHm1!y\A!C $u2ĉ$A>`p 70#߆{N:l`\`~Sُͤ~] /~Ĉ0QF%endstream endobj 1842 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream @- "@dAG6  Ё!bB0q#$C22@IeHp}452Mj:}VWU5w}|/=d"DY(endstream endobj 1844 0 obj << /Length 126 /Filter /LZWDecode >> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1845 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 1846 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 8:R *MV*$ I^?@'D (Ġ endstream endobj 1847 0 obj << /Length 134 /Filter /LZWDecode >> stream bd `q@dAG `ЈQ!bBa#$Fdd" A $u2ĉ$A0(* E@`p 5 0Ur[Wj$ 0`Od@Jendstream endobj 1848 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1849 0 obj << /Length 104 /Filter /LZWDecode >> stream F 1Cb b4ͱ& /+!D(s!r($ :X$ ߆>08.4zeMS@bendstream endobj 1850 0 obj << /Length 153 /Filter /LZWDecode >> stream "d `111* B!b"+#$A22@&IeH}`4p8n.LR x{p1*=pY G wÁpDH QF#endstream endobj 1851 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1852 0 obj << /Length 87 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".884MGS,@D}?PhT E1endstream endobj 1853 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 1854 0 obj << /Length 107 /Filter /LZWDecode >> stream bc P)b b(< 2D3l@I x(^HHHEBI&# I"x`  bF> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1856 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 1857 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1858 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 1859 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1860 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 1861 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 1863 0 obj << /Filter /LZWDecode /Width 77 /Height 99 /BitsPerComponent 8 /ColorSpace [/Indexed /DeviceRGB 255 2 0 R] /Length 583 >> stream 5P8$ BaPRDbPhtN-5ⱘv+y KIe29W)KJlVNg2nV Jq6Ph-J'yFQ*nLa4bY(Y՚oO!]T\L&\pX:{d]0daIbqf>hn%3bx>llMO)r3v'a+"ܦgO7Gɷm7k.ۇg} K[O㹴*Ƿ̸/swO Q\ZfL6>b֑<$=ﳪ L?ɋmJͬ*Jv̓N0>? DD&9DC/ª:ѓ/ ?\J,$b-҄6đܛ JɄHT/;;,\GҚ!*'M5,47Lm܇Nˌv- %:DJF} T/Jȝ1L48 =P"Q=QTeOWUYVu[V1UUa_UYaU6%McTECeSe9gS6-iRv %XHFUCt%ʴ  endstream endobj 1864 0 obj << /Length 4478 /Filter /LZWDecode >> stream P4 DC4b0 d.pH@7GƃxĄmEJ)HFSagx!lPG"0A4d4&y9 ӘWVkUp(PBT:;aq䰹@*e4]pAALG'D2y8O*xR1/!@hШH[|H!t\ #h32SBHnA|mnH\xA(M$2L5T/L#-I)!m2Z% 5L2ܒ5β 7-/EO%@͔|m=)%Q]) Y+}/QQ +/PUm VeTuMH45c's8-K(dWuϖz ZVhrA&`%2fJ= 4̇ A8J 1Q7 "4TyG!βCu'QUݮ|Rm`zFHH魇"o^vtivB+%['S)]4F1je$ZqEYo5#πu~Xl)(*pemmz5umB3o3xYr"egtjϻ be OA螺>[E~~<_rHՄlV\1~mM8S9q^7T ~ю쵨Rec4@Xia@W8nmV{7Ҧz:^J $.;i.|f$ݮw4*#;GY_GNˇݮ;Eъ'L7ks}㫆S?'"0NKį*Wȕbu*։ZHx)Ѱ}Hl^oXem.yr;#>`#k,nmM"6扏T]C6iwv9g-ۼm+{DOw{,a8zT.DB 90ӑb+dp{2f~~p ]ff孋gڸ[ 9bkۛ}S^{Y5we&M.)<:oTޑuvs]zul_'hT'pm HGwImmv (37vLN^qH 5]z3NmMxoy/&t>sѝ7=z.o!1ac)2η:֯)- 8/'Dxy,Py(?y|/@cXΐhSv#tuLiZ؏OO. :pH.J"pU\ꂸom"} @Gʪ@ kK#,2h vIp"&ϮY xv=HOP( Pn~URpp1fOhKkeH$\C31 4&V8tfh_C # ~. "##;aAB,eDd/, -z/cM呹0O5/nPkQ 1.Pu/sQ"JM d-Oa!pY1 !dxZ4\FT "* @d(!( !F 5ę ķ' *0no(벙Ir)ϯa*1*&08L{Ne,5"Q/2..0d 9\w'V G P2hqlG,(%(c %)031S3p4!΍4g5N5s32~.O=B7Q&V8'8 }-93u9e]/r64VUqb @Ee@єEQ#Q'X 2/6Ÿ92|Mą3(o3gE?5+8"C@UA85A&RS51DT!6lEiдpTCĴOpdtW-c.gE, FS'@='  تdH)>*S$4@$Y2ѾBG5 =Ko4T4ҋ]L(MT#=MT*4-<]CLs-1!@48DJ]O5 .JsOI.tols:be2FrT̐G ؙj! QH԰D[ sKVr#WU Wud"iLBuLONyMCOL.XQY?[Z55.\ծRԐz_sV @ @HgTf#5R[UAv k5DE͆a N\FA3W:56;NtczdThlT KW%ecȥviXf0Mg5eVjZ5 xwG_4OE$roSb rE)/ԥ>?QChTKV1-Tl 'mVK[#CW67Mb՗N6dUng'nV?Me@fi MpEE4qEj#WPFw!R^UR^L/itG@ < h *Tj``2uZrbFA Twt.aVյxVy=yAmN7b-p7o "57e4Av|R8En79V>C/|g;}W{ 7Ʒ;/tSkI;6t`cuD"39b ŠcD endstream endobj 1865 0 obj << /ProcSet [/PDF /Text /ImageB] /ColorSpace <> /Font << /F2 7 0 R /F4 8 0 R /T69 1866 0 R /T70 1867 0 R >> >> endobj 1866 0 obj << /Name /T69 /Type /Font /Subtype /Type3 /FontBBox [-1 -9 33 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1868 0 R /CharProcs 1869 0 R /Widths [11 0 0 0 0 0 0 0 14 14 0 0 11 0 0 12 21 21 21 21 21 21 21 21 21 21 0 0 0 0 0 0 0 30 27 28 30 26 23 30 0 14 0 30 25 0 30 30 23 0 27 23 26 29 30 0 0 0 0 0 0 0 0 0 0 19 20 18 0 18 13 21 22 12 0 21 12 33 22 20 21 21 15 16 12 21 20 29 21 19 ] >> endobj 1868 0 obj << /Type /Encoding /Differences [32/G20 40/G28 /G29 44/G2c 47/G2f /G30 /G31 /G32 /G33 /G34 /G35 /G36 /G37 /G38 /G39 65/G41 /G42 /G43 /G44 /G45 /G46 /G47 73/G49 75/G4b /G4c 78/G4e /G4f /G50 82/G52 /G53 /G54 /G55 /G56 97/G61 /G62 /G63 101/G65 /G66 /G67 /G68 /G69 107/G6b /G6c /G6d /G6e /G6f /G70 /G71 /G72 /G73 /G74 /G75 /G76 /G77 /G78 /G79 ] >> endobj 1869 0 obj << /G20 1870 0 R /G28 1871 0 R /G29 1872 0 R /G2c 1873 0 R /G2f 1874 0 R /G30 1875 0 R /G31 1876 0 R /G32 1877 0 R /G33 1878 0 R /G34 1879 0 R /G35 1880 0 R /G36 1881 0 R /G37 1882 0 R /G38 1883 0 R /G39 1884 0 R /G41 1885 0 R /G42 1886 0 R /G43 1887 0 R /G44 1888 0 R /G45 1889 0 R /G46 1890 0 R /G47 1891 0 R /G49 1892 0 R /G4b 1893 0 R /G4c 1894 0 R /G4e 1895 0 R /G4f 1896 0 R /G50 1897 0 R /G52 1898 0 R /G53 1899 0 R /G54 1900 0 R /G55 1901 0 R /G56 1902 0 R /G61 1903 0 R /G62 1904 0 R /G63 1905 0 R /G65 1906 0 R /G66 1907 0 R /G67 1908 0 R /G68 1909 0 R /G69 1910 0 R /G6b 1911 0 R /G6c 1912 0 R /G6d 1913 0 R /G6e 1914 0 R /G6f 1915 0 R /G70 1916 0 R /G71 1917 0 R /G72 1918 0 R /G73 1919 0 R /G74 1920 0 R /G75 1921 0 R /G76 1922 0 R /G77 1923 0 R /G78 1924 0 R /G79 1925 0 R >> endobj 1870 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1871 0 obj << /Length 129 /Filter /LZWDecode >> stream Fd dAGc hD(m1!y^ ԐP!䄒hr:bD ytCVUURQStEg",Q@@endstream endobj 1872 0 obj << /Length 129 /Filter /LZWDecode >> stream Fb  !dAGhD(m1!y^  ԐP!hr:bD O;xN~*/VUUV_)tB8'",Q@@endstream endobj 1873 0 obj << /Length 87 /Filter /LZWDecode >> stream F"d @dAG `!bBm#$#22@*IeH`|>$E1(endstream endobj 1874 0 obj << /Length 132 /Filter /LZWDecode >> stream FB@- B@dAG. $B$ąxPDHFdD""$DC$HPŠ?y7c'=$E1(endstream endobj 1875 0 obj << /Length 132 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}so6c);~{Uիv_Ԫ4&E"E1endstream endobj 1876 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 1877 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 1878 0 obj << /Length 129 /Filter /LZWDecode >> stream "e Fa11C`S"B$ExHDHHE&DI&#"I"9G#F9yg  ^<'0}O ,Q@endstream endobj 1879 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa/![AtR[rXl4 gb$Y(endstream endobj 1880 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 1881 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 1882 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 1883 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 1884 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 1885 0 obj << /Length 166 /Filter /LZWDecode >> stream  Cb b<)ͱ& /+‡1D>B(PMGS,@D?߯oxs0< ( endstream endobj 1886 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" 8h ]6L)4d V*ts6 fTh^wYcBendstream endobj 1887 0 obj << /Length 168 /Filter /LZWDecode >> stream d `x0@dAGk xHm1!y^ 5!CDABI4@t9L"I@`PePP :]w65mnju@Ktp{l$E1(endstream endobj 1888 0 obj << /Length 140 /Filter /LZWDecode >> stream @2A&A4qB 8m0y^$GB@ $u2É$A?x8w|Q/B}M'S՚bNXD>pPX3r&8aendstream endobj 1889 0 obj << /Length 134 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I" <@< E~(]N4>VT5 >Lk (Iw=cBendstream endobj 1890 0 obj << /Length 126 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"`O)~הS/9V4rI~DQF!endstream endobj 1891 0 obj << /Length 169 /Filter /LZWDecode >> stream @- B@dAGqBЁ!bB0q#$B2@ hr:bD <s`<488Ã80j\<=xYP +ae\*B`V BPxS8P4'="ȁEendstream endobj 1892 0 obj << /Length 92 /Filter /LZWDecode >> stream Fc !11A3lDIa HHEBI&#"I"?PhT:%@$Y(endstream endobj 1893 0 obj << /Length 152 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ x?4Ϣ'@~?UgQP=UU`\W=<h h #Ax'dc>endstream endobj 1894 0 obj << /Length 117 /Filter /LZWDecode >> stream  2Cb b4ͱ& /+†D(q!bx(^I&# I"x?* EQ)Tz]&LP_AxDQF!endstream endobj 1895 0 obj << /Length 148 /Filter /LZWDecode >> stream A   8hT,c6DxGItP!!y$ :Xy$ xEMAX5*kA>h~^|ȃ0l ]d|YG)Cy8 ( endstream endobj 1896 0 obj << /Length 159 /Filter /LZWDecode >> stream @- "P11  Ё!b"0q#$ R2@IeH~}|/ 1|5  {YlEeXZS| )m"DP(T "E1endstream endobj 1897 0 obj << /Length 131 /Filter /LZWDecode >> stream b 2Cb b4ͱ& /+†QD(q!bx(^I&# I"> stream  2!  8pX,N c6Dx GICCBI4@t9LI@?}~= CAtcIT( <~? Tzu.'vCȱQFendstream endobj 1899 0 obj << /Length 156 /Filter /LZWDecode >> stream b-P@dAGq xI!bB-#$A2@&IeHy0=O-N`wU@UX =@ S~[#5vp,1",Q@@endstream endobj 1900 0 obj << /Length 116 /Filter /LZWDecode >> stream c 11C`S"B$ExHDHHEDI&#"I"}ߔ?7.LTj:}VVUH?d Fendstream endobj 1901 0 obj << /Length 135 /Filter /LZWDecode >> stream "@- P11\ Lf؉C"G !b(^I&#"I"~EѨE.OTi:eVTm]?_a=?|DHQF#endstream endobj 1902 0 obj << /Length 169 /Filter /LZWDecode >> stream @- !11,2 Ё!b"#$#2@IeH}`xQ.||S?~VC}==m }uS}T5F?/"ȁEendstream endobj 1903 0 obj << /Length 129 /Filter /LZWDecode >> stream G"b B11 Bs"B$Ex0DHDB@IeH=C6Eј ;`?,Gcp}r@pX,Q@endstream endobj 1904 0 obj << /Length 137 /Filter /LZWDecode >> stream b #dAG `$B$ąxhDHFdD""$DC$=\4*#‡F\4d8A?:_X_k^T8<"DY(endstream endobj 1905 0 obj << /Length 119 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A,w=|']6L;@X7 ~DY(endstream endobj 1906 0 obj << /Length 116 /Filter /LZWDecode >> stream Gb #q dAGm P9!bBm#$Cb yCH$u2ĉ$A>l =xRO/;@X>d@Jendstream endobj 1907 0 obj << /Length 116 /Filter /LZWDecode >> stream Fb 1Cb b4ͱ& /+!D(s!r($ :X$ p>R(`LiU:RURcBendstream endobj 1908 0 obj << /Length 144 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@q 7DR\/}@N^oVx0x0x`=O)`s~xDQF!endstream endobj 1909 0 obj << /Length 124 /Filter /LZWDecode >> stream Bc@#b b(< 2ED3l@I x(^HHEBI&# I"/x8h47 Dph (|/NTVkv^0"Eendstream endobj 1910 0 obj << /Length 105 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D<t`P(T`ge.MS  E1endstream endobj 1911 0 obj << /Length 139 /Filter /LZWDecode >> stream "  "  8X,* 1a"L<^WÁBD$s!b MGS,}CtJcDxQ) y: ( endstream endobj 1912 0 obj << /Length 97 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D>X h:GRi "Eendstream endobj 1913 0 obj << /Length 120 /Filter /LZWDecode >> stream bc@()b b1" B f؁#)"084MGS,@D>\pGp{Cg](LӪ:VW},|Q@@endstream endobj 1914 0 obj << /Length 108 /Filter /LZWDecode >> stream Bc@0S  8Px,e fy0P 84MGS,\/ ÅEѩU6OT)L`0CȱQFendstream endobj 1915 0 obj << /Length 126 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=,ExU0S5 V!߄ `0l=`DHQF#endstream endobj 1916 0 obj << /Length 137 /Filter /LZWDecode >> stream "@-PXX11l 3lDI𡄄 HE IeH˅@ 0p?-p*NU?Cx88@ j6C"$Y(endstream endobj 1917 0 obj << /Length 133 /Filter /LZWDecode >> stream "- P)b b(,V Lf؁D^@B(I4@t9LI@Cpa wEG(]4LӀZ9E 1:<[gX"Eendstream endobj 1918 0 obj << /Length 97 /Filter /LZWDecode >> stream Fc@PS  8#HDͰ&/+@!ph$ :Xy$ |(dp?4*%EQly"ǁE|endstream endobj 1919 0 obj << /Length 123 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD h` >裃w<0C$E1(endstream endobj 1920 0 obj << /Length 107 /Filter /LZWDecode >> stream FB@- B@dAG.  Ё!bB(e#$C2@ IeH>x<^Q)T:e.MR Apd@Jendstream endobj 1921 0 obj << /Length 107 /Filter /LZWDecode >> stream "@-Ȁ11T 2Af3lDI$HHE.BI&#"I".8p(* EQ)Tz]"~rxdFendstream endobj 1922 0 obj << /Length 129 /Filter /LZWDecode >> stream b  B@dAG0$B$ąxTD/$B"@ IeH4p<-~|?:U]]g",Q@@endstream endobj 1923 0 obj << /Length 143 /Filter /LZWDecode >> stream "@- B 11\ 2A&3lDĪ HHE2BI&#"I"x/aD?뇅*Nx?A=37е{٭ ;8}ۮ=dFendstream endobj 1924 0 obj << /Length 131 /Filter /LZWDecode >> stream "A"  8PX* Ͱ&/+"qD*>B(I4@t9LI@x/Ãp?^7x?x?=9~^ x;{| n<aendstream endobj 1925 0 obj << /Length 140 /Filter /LZWDecode >> stream G"Z1@BYb b(,T@2A!b\a#$E2@ IeH/󃂈𣿞.* Uz!HG) C~H_-Lsx n ;@"bendstream endobj 1867 0 obj << /Name /T70 /Type /Font /Subtype /Type3 /FontBBox [0 -9 34 29 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 32 /LastChar 121 /Encoding 1926 0 R /CharProcs 1927 0 R /Widths [11 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 21 0 21 21 0 0 0 0 0 0 0 0 0 0 0 0 30 0 30 0 0 0 0 0 16 0 33 0 0 30 0 0 0 0 0 28 0 0 0 0 0 0 0 0 0 0 0 0 21 23 0 0 19 0 21 0 12 0 0 0 34 0 20 23 0 19 16 14 22 0 0 0 21 ] >> endobj 1926 0 obj << /Type /Encoding /Differences [32/G20 46/G2e 49/G31 51/G33 /G34 65/G41 67/G43 73/G49 75/G4b 78/G4e 84/G54 97/G61 /G62 101/G65 103/G67 105/G69 109/G6d 111/G6f /G70 114/G72 /G73 /G74 /G75 121/G79 ] >> endobj 1927 0 obj << /G20 1928 0 R /G2e 1929 0 R /G31 1930 0 R /G33 1931 0 R /G34 1932 0 R /G41 1933 0 R /G43 1934 0 R /G49 1935 0 R /G4b 1936 0 R /G4e 1937 0 R /G54 1938 0 R /G61 1939 0 R /G62 1940 0 R /G65 1941 0 R /G67 1942 0 R /G69 1943 0 R /G6d 1944 0 R /G6f 1945 0 R /G70 1946 0 R /G72 1947 0 R /G73 1948 0 R /G74 1949 0 R /G75 1950 0 R /G79 1951 0 R >> endobj 1928 0 obj << /Length 16 /Filter /LZWDecode >> stream F"  Tendstream endobj 1929 0 obj << /Length 77 /Filter /LZWDecode >> stream F"-ab bx$V)ͱ& /+D>B(I4@t9LI@A'H&4@bendstream endobj 1930 0 obj << /Length 102 /Filter /LZWDecode >> stream "g G,113lDI𑬄 HHE&BI&#"I"yp?QTe.,Q@endstream endobj 1931 0 obj << /Length 149 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH@o M`?/#N }d/ ]l @OD!b0endstream endobj 1932 0 obj << /Length 128 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHŀp/ FX;G}*;߫s_Xk=fQ$Y(endstream endobj 1933 0 obj << /Length 165 /Filter /LZWDecode >> stream A "  8hT,c6DxGItP!!y$ :Xy$ '3:|OE-M0EMΓp:WlNtwۀx㧇0 ,pQ@endstream endobj 1934 0 obj << /Length 168 /Filter /LZWDecode >> stream @-CP@dAGm@PxI!bB(m#$ R2@ hr:bD p~S~0 [@P 65mm]g >) $E1(endstream endobj 1935 0 obj << /Length 94 /Filter /LZWDecode >> stream Fc@Cb b" B f؁Y A 84MGS,@Ds=>OT%EQ> ( endstream endobj 1936 0 obj << /Length 167 /Filter /LZWDecode >> stream bc Ƃ@dAG 3lHI 𑜄 HHEBI&#$I"}Q\u+G@ez{%N)tV"@=@NH bPendstream endobj 1937 0 obj << /Length 168 /Filter /LZWDecode >> stream @- "@dAG6  Ё!bB0q#$C22@IeH`=xQz;MUG*U]yW2U_UU_@Uoz<_$E1(endstream endobj 1938 0 obj << /Length 121 /Filter /LZWDecode >> stream e 3  8CXDͰ&/+D.>B(PMGS,> stream "d Ph)b b- D3l@I "I!$DC${p14EѨz++ƊV\:-q xpDQF!endstream endobj 1940 0 obj << /Length 133 /Filter /LZWDecode >> stream bb P11, `Ј9!b"`a#$F22@IeHP'%p`<`?+n]U5Z{x81x "ȁEendstream endobj 1941 0 obj << /Length 130 /Filter /LZWDecode >> stream G"b # dAGo P9!bBo#$Cb yCH$u2ĉ$Ax0 RtMT@oNT+H@'D (Ġ endstream endobj 1942 0 obj << /Length 153 /Filter /LZWDecode >> stream "d `111* B!b"+#$A22@&IeH}`4p8n.LR x{p1*=pY G wÁpDH QF#endstream endobj 1943 0 obj << /Length 95 /Filter /LZWDecode >> stream FBcACb b,TA B f؁ ".984MGS,@D8Xt`P(LGRh@bendstream endobj 1944 0 obj << /Length 120 /Filter /LZWDecode >> stream Ƃc b113lDI𑔄 HHEBI&#"I"|<0S?M) JSUkt,Q@endstream endobj 1945 0 obj << /Length 120 /Filter /LZWDecode >> stream @- G"11 B`c"B$EqVDHDB@%IeH=a.a:U0x**NUӫTM @`OX,Q@endstream endobj 1946 0 obj << /Length 130 /Filter /LZWDecode >> stream bb D(8 dAG: B!bB,a#$B22@IeH` E|SNUBM>(z `?+ eD (Ġ endstream endobj 1947 0 obj << /Length 107 /Filter /LZWDecode >> stream G"e@S  8#xDeͰ&/+FD.>B(1I4@t9LI@x =0?4*LTjj@~c>endstream endobj 1948 0 obj << /Length 122 /Filter /LZWDecode >> stream Fd D dAGg PxHm1!y^3 PP!䄒hr:bD cd]p 0C˜?}DH#bPendstream endobj 1949 0 obj << /Length 114 /Filter /LZWDecode >> stream F@-#1l 2 ANh@m11y^3!y!CI $u2$Axp e&HSU`x&E18endstream endobj 1950 0 obj << /Length 111 /Filter /LZWDecode >> stream B@-P@dAG. A`!bB*" !C CIhr:bD 0 GRhE2@b|",Q@@endstream endobj 1951 0 obj << /Length 151 /Filter /LZWDecode >> stream "-PQ$11l,2A!b"s#$B2@IeH? ?8˜5/X {Z5p!3? "ȁEendstream endobj 7 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /Encoding 1952 0 R /BaseFont /Helvetica >> endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /Encoding 1952 0 R /BaseFont /Helvetica-Bold >> endobj 19 0 obj << /Type /Font /Subtype /Type1 /Name /F7 /FirstChar 0 /LastChar 255 /Widths [ 400 400 500 480 460 500 320 500 340 360 440 320 500 360 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 340 360 420 660 660 940 800 240 320 320 460 600 340 360 340 600 660 660 660 660 660 660 660 660 660 660 340 340 600 600 600 660 820 720 720 740 780 720 680 780 820 400 640 800 640 940 740 800 660 800 780 660 700 740 720 940 780 700 640 300 600 300 600 500 400 580 600 580 640 580 380 580 680 360 340 660 340 1000 680 620 640 620 460 520 460 660 600 800 600 620 560 320 600 320 600 460 460 460 320 660 540 1000 440 380 500 1360 660 220 1220 460 460 460 460 320 320 540 540 460 500 1000 480 980 520 220 940 460 460 700 340 360 660 660 660 660 600 600 500 740 400 400 600 360 740 460 400 600 396 396 400 660 800 340 360 396 400 400 990 990 990 660 720 720 720 720 720 720 1140 740 720 720 720 720 400 400 400 400 780 740 800 800 800 800 800 600 800 740 740 740 740 700 660 660 580 580 580 580 580 580 880 580 580 580 580 580 360 360 360 360 620 680 620 620 620 620 620 600 620 660 660 660 660 620 640 620 ] /Encoding 1952 0 R /BaseFont /Bookman-Demi /FontDescriptor 1953 0 R >> endobj 221 0 obj << /Type /Font /Subtype /Type1 /Name /F23 /BaseFont /Symbol >> endobj 232 0 obj << /Type /Font /Subtype /Type1 /Name /F25 /FirstChar 0 /LastChar 255 /Widths [ 333 333 333 333 333 333 333 333 333 333 333 333 333 315 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 278 296 389 556 556 833 815 204 333 333 500 606 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 606 606 606 444 737 722 722 722 778 722 667 778 833 407 556 778 667 944 815 778 667 778 722 630 667 815 722 981 704 704 611 333 606 333 606 500 333 556 556 444 574 500 333 537 611 315 296 593 315 889 611 500 574 556 444 463 389 611 537 778 537 537 481 333 606 333 606 606 606 606 204 556 389 1000 500 500 333 1000 630 259 1000 606 606 606 606 204 204 389 389 606 556 1000 333 1000 463 259 833 606 606 704 278 296 556 556 556 556 606 500 333 737 334 426 606 333 737 333 400 606 333 333 333 611 606 278 333 333 300 426 834 834 834 444 722 722 722 722 722 722 1000 722 722 722 722 722 407 407 407 407 778 815 778 778 778 778 778 606 778 815 815 815 815 704 667 574 556 556 556 556 556 556 796 444 500 500 500 500 315 315 315 315 500 611 500 500 500 500 500 606 500 611 611 611 611 537 574 537 ] /Encoding 1952 0 R /BaseFont /NewCenturySchlbk-Roman /FontDescriptor 1954 0 R >> endobj 1952 0 obj << /Type /Encoding /Differences [ 0/grave/acute/circumflex/tilde/macron/breve/dotaccent/dieresis /ring/cedilla/hungarumlaut/ogonek/caron/dotlessi 39/quotesingle 96/grave 127/bullet/bullet/bullet/quotesinglbase/florin/quotedblbase/ellipsis/dagger /daggerdbl/circumflex/perthousand/Scaron/guilsinglleft/OE/bullet/bullet /bullet/bullet/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash /emdash/tilde/trademark/scaron/guilsinglright/oe/bullet/bullet /Ydieresis/space 164/currency 166/brokenbar 168/dieresis/copyright/ordfeminine 172/logicalnot /hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior/acute /mu 183/periodcentered/cedilla/onesuperior/ordmasculine 188/onequarter/onehalf/threequarters 192/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis /Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply /Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls /agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla /egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis /eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide /oslash/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis ] >> endobj 1953 0 obj << /Type /FontDescriptor /Ascent 725 /CapHeight 681 /Descent -212 /Flags 278578 /FontBBox [-194 -250 1346 934 ] /FontName /Bookman-Demi /ItalicAngle 0 /StemV 167 /XHeight 502 >> endobj 1954 0 obj << /Type /FontDescriptor /Ascent 737 /CapHeight 722 /Descent -205 /Flags 50 /FontBBox [-195 -250 1000 965 ] /FontName /NewCenturySchlbk-Roman /ItalicAngle 0 /StemV 92 /XHeight 464 >> endobj 14 0 obj << /Name /T1 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 50 /LastChar 50 /Encoding 1955 0 R /CharProcs 1956 0 R /Widths [21 ] >> endobj 1955 0 obj << /Type /Encoding /Differences [50/G32 ] >> endobj 1956 0 obj << /G32 1957 0 R >> endobj 1957 0 obj << /Length 150 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH}6g ~T*V:qǫR_?`?<; "E1endstream endobj 222 0 obj << /Name /T8 /Type /Font /Subtype /Type3 /FontBBox [2 0 18 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 53 /LastChar 53 /Encoding 1958 0 R /CharProcs 1959 0 R /Widths [21 ] >> endobj 1958 0 obj << /Type /Encoding /Differences [53/G35 ] >> endobj 1959 0 obj << /G35 1960 0 R >> endobj 1960 0 obj << /Length 123 /Filter /LZWDecode >> stream "e GhYb b" 23l@I 𑴄 8LMGS,@D? x ?`w hQkqs C E1endstream endobj 227 0 obj << /Name /T9 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 54 /LastChar 54 /Encoding 1961 0 R /CharProcs 1962 0 R /Widths [21 ] >> endobj 1961 0 obj << /Type /Encoding /Differences [54/G36 ] >> endobj 1962 0 obj << /G36 1963 0 R >> endobj 1963 0 obj << /Length 151 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeHa~~MqXt=5MC >~Y6Pi qX""AEendstream endobj 233 0 obj << /Name /T10 /Type /Font /Subtype /Type3 /FontBBox [1 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 55 /LastChar 55 /Encoding 1964 0 R /CharProcs 1965 0 R /Widths [21 ] >> endobj 1964 0 obj << /Type /Encoding /Differences [55/G37 ] >> endobj 1965 0 obj << /G37 1966 0 R >> endobj 1966 0 obj << /Length 139 /Filter /LZWDecode >> stream "D!116 A"B$Ex0DHɁB@IeH=0'"}@_M)Ϛ9Ny):sNcә*r>ӃB (` endstream endobj 240 0 obj << /Name /T15 /Type /Font /Subtype /Type3 /FontBBox [2 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 56 /LastChar 56 /Encoding 1967 0 R /CharProcs 1968 0 R /Widths [21 ] >> endobj 1967 0 obj << /Type /Encoding /Differences [56/G38 ] >> endobj 1968 0 obj << /G38 1969 0 R >> endobj 1969 0 obj << /Length 143 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"cPR(= B@ xऀ(Ų܇|P(d Fendstream endobj 245 0 obj << /Name /T16 /Type /Font /Subtype /Type3 /FontBBox [2 0 19 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 57 /LastChar 57 /Encoding 1970 0 R /CharProcs 1971 0 R /Widths [21 ] >> endobj 1970 0 obj << /Type /Encoding /Differences [57/G39 ] >> endobj 1971 0 obj << /G39 1972 0 R >> endobj 1972 0 obj << /Length 144 /Filter /LZWDecode >> stream "e G0a11C`S"B$ExHDHHE&DI&#"I"͎s wEG-T)V0@?O+a?v=ODHQF#endstream endobj 259 0 obj << /Name /T18 /Type /Font /Subtype /Type3 /FontBBox [5 0 17 28 ] /FontMatrix [0.02381 0 0 0.02381 0 0] /FirstChar 49 /LastChar 49 /Encoding 1973 0 R /CharProcs 1974 0 R /Widths [21 ] >> endobj 1973 0 obj << /Type /Encoding /Differences [49/G31 ] >> endobj 1974 0 obj << /G31 1975 0 R >> endobj 1975 0 obj << /Length 96 /Filter /LZWDecode >> stream "k F@dAGe`Ѓ$B$ąxHDH$D""$DC$ xPhT:%GP\$E1(endstream endobj 3 0 obj << /Type /Page /Parent 9 0 R /Resources 6 0 R /Contents 5 0 R /Thumb 4 0 R >> endobj 10 0 obj << /Type /Page /Parent 9 0 R /Resources 13 0 R /Contents 12 0 R /Thumb 11 0 R >> endobj 15 0 obj << /Type /Page /Parent 9 0 R /Resources 18 0 R /Contents 17 0 R /Thumb 16 0 R >> endobj 127 0 obj << /Type /Page /Parent 9 0 R /Resources 130 0 R /Contents 129 0 R /Thumb 128 0 R >> endobj 217 0 obj << /Type /Page /Parent 9 0 R /Resources 220 0 R /Contents 219 0 R /Thumb 218 0 R >> endobj 223 0 obj << /Type /Page /Parent 9 0 R /Resources 226 0 R /Contents 225 0 R /Thumb 224 0 R >> endobj 228 0 obj << /Type /Page /Parent 235 0 R /Resources 231 0 R /Contents 230 0 R /Thumb 229 0 R >> endobj 236 0 obj << /Type /Page /Parent 235 0 R /Resources 239 0 R /Contents 238 0 R /Thumb 237 0 R >> endobj 241 0 obj << /Type /Page /Parent 235 0 R /Resources 244 0 R /Contents 243 0 R /Thumb 242 0 R >> endobj 246 0 obj << /Type /Page /Parent 235 0 R /Resources 249 0 R /Contents 248 0 R /Thumb 247 0 R >> endobj 255 0 obj << /Type /Page /Parent 235 0 R /Resources 258 0 R /Contents 257 0 R /Thumb 256 0 R >> endobj 260 0 obj << /Type /Page /Parent 235 0 R /Resources 263 0 R /Contents 262 0 R /Thumb 261 0 R >> endobj 269 0 obj << /Type /Page /Parent 274 0 R /Resources 272 0 R /Contents 271 0 R /Thumb 270 0 R >> endobj 279 0 obj << /Type /Page /Parent 274 0 R /Resources 282 0 R /Contents 281 0 R /Thumb 280 0 R >> endobj 288 0 obj << /Type /Page /Parent 274 0 R /Resources 291 0 R /Contents 290 0 R /Thumb 289 0 R >> endobj 297 0 obj << /Type /Page /Parent 274 0 R /Resources 300 0 R /Contents 299 0 R /Thumb 298 0 R >> endobj 306 0 obj << /Type /Page /Parent 274 0 R /Resources 309 0 R /Contents 308 0 R /Thumb 307 0 R >> endobj 315 0 obj << /Type /Page /Parent 274 0 R /Resources 318 0 R /Contents 317 0 R /Thumb 316 0 R >> endobj 324 0 obj << /Type /Page /Parent 329 0 R /Resources 327 0 R /Contents 326 0 R /Thumb 325 0 R >> endobj 346 0 obj << /Type /Page /Parent 329 0 R /Resources 349 0 R /Contents 348 0 R /Thumb 347 0 R >> endobj 355 0 obj << /Type /Page /Parent 329 0 R /Resources 358 0 R /Contents 357 0 R /Thumb 356 0 R >> endobj 364 0 obj << /Type /Page /Parent 329 0 R /Resources 367 0 R /Contents 366 0 R /Thumb 365 0 R >> endobj 368 0 obj << /Type /Page /Parent 329 0 R /Resources 371 0 R /Contents 370 0 R /Thumb 369 0 R >> endobj 377 0 obj << /Type /Page /Parent 329 0 R /Resources 380 0 R /Contents 379 0 R /Thumb 378 0 R >> endobj 386 0 obj << /Type /Page /Parent 391 0 R /Resources 389 0 R /Contents 388 0 R /Thumb 387 0 R >> endobj 396 0 obj << /Type /Page /Parent 391 0 R /Resources 399 0 R /Contents 398 0 R /Thumb 397 0 R >> endobj 405 0 obj << /Type /Page /Parent 391 0 R /Resources 408 0 R /Contents 407 0 R /Thumb 406 0 R >> endobj 414 0 obj << /Type /Page /Parent 391 0 R /Resources 417 0 R /Contents 416 0 R /Thumb 415 0 R >> endobj 457 0 obj << /Type /Page /Parent 391 0 R /Resources 460 0 R /Contents 459 0 R /Thumb 458 0 R >> endobj 619 0 obj << /Type /Page /Parent 391 0 R /Resources 622 0 R /Contents 621 0 R /Thumb 620 0 R >> endobj 762 0 obj << /Type /Page /Parent 768 0 R /Resources 765 0 R /Contents 764 0 R /Thumb 763 0 R >> endobj 867 0 obj << /Type /Page /Parent 768 0 R /Resources 870 0 R /Contents 869 0 R /Thumb 868 0 R >> endobj 903 0 obj << /Type /Page /Parent 768 0 R /Resources 906 0 R /Contents 905 0 R /Thumb 904 0 R >> endobj 1034 0 obj << /Type /Page /Parent 768 0 R /Resources 1037 0 R /Contents 1036 0 R /Thumb 1035 0 R >> endobj 1143 0 obj << /Type /Page /Parent 768 0 R /Resources 1146 0 R /Contents 1145 0 R /Thumb 1144 0 R >> endobj 1241 0 obj << /Type /Page /Parent 768 0 R /Resources 1244 0 R /Contents 1243 0 R /Thumb 1242 0 R >> endobj 1371 0 obj << /Type /Page /Parent 1377 0 R /Resources 1374 0 R /Contents 1373 0 R /Thumb 1372 0 R >> endobj 1435 0 obj << /Type /Page /Parent 1377 0 R /Resources 1438 0 R /Contents 1437 0 R /Thumb 1436 0 R >> endobj 1531 0 obj << /Type /Page /Parent 1377 0 R /Resources 1534 0 R /Contents 1533 0 R /Thumb 1532 0 R >> endobj 1637 0 obj << /Type /Page /Parent 1377 0 R /Resources 1640 0 R /Contents 1639 0 R /Thumb 1638 0 R >> endobj 1736 0 obj << /Type /Page /Parent 1377 0 R /Resources 1739 0 R /Contents 1738 0 R /Thumb 1737 0 R >> endobj 1862 0 obj << /Type /Page /Parent 1377 0 R /Resources 1865 0 R /Contents 1864 0 R /Thumb 1863 0 R >> endobj 9 0 obj << /Type /Pages /Kids [3 0 R 10 0 R 15 0 R 127 0 R 217 0 R 223 0 R ] /Count 6 /Parent 234 0 R >> endobj 235 0 obj << /Type /Pages /Kids [228 0 R 236 0 R 241 0 R 246 0 R 255 0 R 260 0 R ] /Count 6 /Parent 234 0 R >> endobj 274 0 obj << /Type /Pages /Kids [269 0 R 279 0 R 288 0 R 297 0 R 306 0 R 315 0 R ] /Count 6 /Parent 234 0 R >> endobj 329 0 obj << /Type /Pages /Kids [324 0 R 346 0 R 355 0 R 364 0 R 368 0 R 377 0 R ] /Count 6 /Parent 234 0 R >> endobj 391 0 obj << /Type /Pages /Kids [386 0 R 396 0 R 405 0 R 414 0 R 457 0 R 619 0 R ] /Count 6 /Parent 234 0 R >> endobj 768 0 obj << /Type /Pages /Kids [762 0 R 867 0 R 903 0 R 1034 0 R 1143 0 R 1241 0 R ] /Count 6 /Parent 234 0 R >> endobj 234 0 obj << /Type /Pages /Kids [9 0 R 235 0 R 274 0 R 329 0 R 391 0 R 768 0 R ] /Count 36 /Parent 1376 0 R >> endobj 1377 0 obj << /Type /Pages /Kids [1371 0 R 1435 0 R 1531 0 R 1637 0 R 1736 0 R 1862 0 R ] /Count 6 /Parent 1376 0 R >> endobj 1376 0 obj << /Type /Pages /Kids [234 0 R 1377 0 R ] /Count 42 /MediaBox [0 0 612 792 ] >> endobj 1976 0 obj << /Type /Catalog /Pages 1376 0 R >> endobj 1977 0 obj << /CreationDate (D:19970228151434) /Producer (Acrobat Distiller 2.0 for Windows) >> endobj xref 0 1978 0000000000 65535 f 0000000017 00000 n 0000000182 00000 n 0000577851 00000 n 0000000736 00000 n 0000001368 00000 n 0000001999 00000 n 0000569583 00000 n 0000569691 00000 n 0000582569 00000 n 0000577953 00000 n 0000002118 00000 n 0000002613 00000 n 0000003062 00000 n 0000574047 00000 n 0000578059 00000 n 0000003202 00000 n 0000003966 00000 n 0000006043 00000 n 0000569804 00000 n 0000006207 00000 n 0000019459 00000 n 0000006718 00000 n 0000007088 00000 n 0000007842 00000 n 0000007935 00000 n 0000008142 00000 n 0000008349 00000 n 0000008505 00000 n 0000008660 00000 n 0000008870 00000 n 0000009043 00000 n 0000009271 00000 n 0000009478 00000 n 0000009684 00000 n 0000009885 00000 n 0000010114 00000 n 0000010331 00000 n 0000010552 00000 n 0000010774 00000 n 0000011018 00000 n 0000011264 00000 n 0000011482 00000 n 0000011694 00000 n 0000011898 00000 n 0000012145 00000 n 0000012314 00000 n 0000012574 00000 n 0000012800 00000 n 0000013037 00000 n 0000013246 00000 n 0000013508 00000 n 0000013730 00000 n 0000013964 00000 n 0000014158 00000 n 0000014405 00000 n 0000014677 00000 n 0000014884 00000 n 0000015099 00000 n 0000015296 00000 n 0000015514 00000 n 0000015708 00000 n 0000015902 00000 n 0000016124 00000 n 0000016326 00000 n 0000016509 00000 n 0000016683 00000 n 0000016881 00000 n 0000017067 00000 n 0000017271 00000 n 0000017486 00000 n 0000017697 00000 n 0000017871 00000 n 0000018072 00000 n 0000018257 00000 n 0000018442 00000 n 0000018649 00000 n 0000018870 00000 n 0000019079 00000 n 0000019297 00000 n 0000019904 00000 n 0000020227 00000 n 0000020865 00000 n 0000020958 00000 n 0000021112 00000 n 0000021320 00000 n 0000021500 00000 n 0000021728 00000 n 0000021955 00000 n 0000022181 00000 n 0000022406 00000 n 0000022617 00000 n 0000022841 00000 n 0000023084 00000 n 0000023330 00000 n 0000023545 00000 n 0000023767 00000 n 0000023973 00000 n 0000024144 00000 n 0000024350 00000 n 0000024608 00000 n 0000024855 00000 n 0000025098 00000 n 0000025303 00000 n 0000025532 00000 n 0000025773 00000 n 0000025973 00000 n 0000026189 00000 n 0000026394 00000 n 0000026606 00000 n 0000026815 00000 n 0000027028 00000 n 0000027237 00000 n 0000027420 00000 n 0000027593 00000 n 0000027805 00000 n 0000027970 00000 n 0000028169 00000 n 0000028355 00000 n 0000028554 00000 n 0000028763 00000 n 0000028949 00000 n 0000029150 00000 n 0000029343 00000 n 0000029533 00000 n 0000029735 00000 n 0000029962 00000 n 0000578165 00000 n 0000030192 00000 n 0000030866 00000 n 0000032170 00000 n 0000032325 00000 n 0000044032 00000 n 0000032833 00000 n 0000033181 00000 n 0000033908 00000 n 0000034002 00000 n 0000034210 00000 n 0000034418 00000 n 0000034583 00000 n 0000034740 00000 n 0000034896 00000 n 0000035107 00000 n 0000035281 00000 n 0000035510 00000 n 0000035718 00000 n 0000035925 00000 n 0000036127 00000 n 0000036357 00000 n 0000036575 00000 n 0000036797 00000 n 0000037020 00000 n 0000037265 00000 n 0000037512 00000 n 0000037731 00000 n 0000037944 00000 n 0000038114 00000 n 0000038375 00000 n 0000038613 00000 n 0000038823 00000 n 0000039058 00000 n 0000039253 00000 n 0000039461 00000 n 0000039677 00000 n 0000039875 00000 n 0000040094 00000 n 0000040289 00000 n 0000040484 00000 n 0000040707 00000 n 0000040910 00000 n 0000041094 00000 n 0000041292 00000 n 0000041510 00000 n 0000041685 00000 n 0000041884 00000 n 0000042071 00000 n 0000042276 00000 n 0000042492 00000 n 0000042667 00000 n 0000042869 00000 n 0000043055 00000 n 0000043241 00000 n 0000043449 00000 n 0000043668 00000 n 0000043869 00000 n 0000044398 00000 n 0000044633 00000 n 0000045080 00000 n 0000045174 00000 n 0000045329 00000 n 0000045484 00000 n 0000045682 00000 n 0000045911 00000 n 0000046139 00000 n 0000046346 00000 n 0000046573 00000 n 0000046799 00000 n 0000047030 00000 n 0000047274 00000 n 0000047521 00000 n 0000047737 00000 n 0000047960 00000 n 0000048167 00000 n 0000048414 00000 n 0000048614 00000 n 0000048786 00000 n 0000048993 00000 n 0000049252 00000 n 0000049499 00000 n 0000049742 00000 n 0000049947 00000 n 0000050176 00000 n 0000050417 00000 n 0000050617 00000 n 0000050833 00000 n 0000051082 00000 n 0000051331 00000 n 0000578275 00000 n 0000051570 00000 n 0000052212 00000 n 0000053505 00000 n 0000571011 00000 n 0000574604 00000 n 0000578385 00000 n 0000053661 00000 n 0000054266 00000 n 0000055392 00000 n 0000575135 00000 n 0000578495 00000 n 0000055534 00000 n 0000056366 00000 n 0000061179 00000 n 0000571099 00000 n 0000575694 00000 n 0000583322 00000 n 0000582689 00000 n 0000578607 00000 n 0000061348 00000 n 0000062202 00000 n 0000064862 00000 n 0000576242 00000 n 0000578719 00000 n 0000065005 00000 n 0000065849 00000 n 0000068832 00000 n 0000576794 00000 n 0000578831 00000 n 0000068975 00000 n 0000070029 00000 n 0000074302 00000 n 0000074459 00000 n 0000074677 00000 n 0000074750 00000 n 0000074805 00000 n 0000075016 00000 n 0000578943 00000 n 0000075190 00000 n 0000075835 00000 n 0000077276 00000 n 0000577347 00000 n 0000579055 00000 n 0000077419 00000 n 0000078133 00000 n 0000079489 00000 n 0000079632 00000 n 0000079850 00000 n 0000079923 00000 n 0000079978 00000 n 0000080152 00000 n 0000579167 00000 n 0000080381 00000 n 0000081391 00000 n 0000087622 00000 n 0000087765 00000 n 0000582815 00000 n 0000087985 00000 n 0000088060 00000 n 0000088115 00000 n 0000088289 00000 n 0000579279 00000 n 0000088497 00000 n 0000089511 00000 n 0000093742 00000 n 0000093899 00000 n 0000094121 00000 n 0000094196 00000 n 0000094251 00000 n 0000094425 00000 n 0000579391 00000 n 0000094632 00000 n 0000095513 00000 n 0000099384 00000 n 0000099527 00000 n 0000099751 00000 n 0000099826 00000 n 0000099881 00000 n 0000100055 00000 n 0000579503 00000 n 0000100257 00000 n 0000101057 00000 n 0000103230 00000 n 0000103387 00000 n 0000103613 00000 n 0000103688 00000 n 0000103743 00000 n 0000103917 00000 n 0000579615 00000 n 0000104147 00000 n 0000105119 00000 n 0000110918 00000 n 0000111061 00000 n 0000111289 00000 n 0000111364 00000 n 0000111419 00000 n 0000111593 00000 n 0000579727 00000 n 0000111811 00000 n 0000112726 00000 n 0000115502 00000 n 0000115645 00000 n 0000115875 00000 n 0000115950 00000 n 0000116005 00000 n 0000116179 00000 n 0000579839 00000 n 0000116401 00000 n 0000117337 00000 n 0000122950 00000 n 0000123107 00000 n 0000582941 00000 n 0000123520 00000 n 0000123679 00000 n 0000123902 00000 n 0000123996 00000 n 0000124170 00000 n 0000124393 00000 n 0000124603 00000 n 0000124811 00000 n 0000125030 00000 n 0000125225 00000 n 0000125409 00000 n 0000125584 00000 n 0000125783 00000 n 0000125970 00000 n 0000126145 00000 n 0000126331 00000 n 0000579951 00000 n 0000126539 00000 n 0000127571 00000 n 0000137446 00000 n 0000137589 00000 n 0000137809 00000 n 0000137884 00000 n 0000137939 00000 n 0000138150 00000 n 0000580063 00000 n 0000138379 00000 n 0000139216 00000 n 0000141674 00000 n 0000141817 00000 n 0000142035 00000 n 0000142108 00000 n 0000142163 00000 n 0000142337 00000 n 0000580175 00000 n 0000142566 00000 n 0000143486 00000 n 0000149177 00000 n 0000580287 00000 n 0000149332 00000 n 0000150218 00000 n 0000152558 00000 n 0000152715 00000 n 0000152933 00000 n 0000153006 00000 n 0000153061 00000 n 0000153290 00000 n 0000580399 00000 n 0000153498 00000 n 0000154225 00000 n 0000156737 00000 n 0000156880 00000 n 0000157100 00000 n 0000157175 00000 n 0000157230 00000 n 0000157459 00000 n 0000580511 00000 n 0000157666 00000 n 0000158263 00000 n 0000159224 00000 n 0000159367 00000 n 0000583067 00000 n 0000159589 00000 n 0000159664 00000 n 0000159719 00000 n 0000159948 00000 n 0000580623 00000 n 0000160150 00000 n 0000160876 00000 n 0000162162 00000 n 0000162305 00000 n 0000162529 00000 n 0000162604 00000 n 0000162659 00000 n 0000162888 00000 n 0000580735 00000 n 0000163118 00000 n 0000164027 00000 n 0000169110 00000 n 0000169253 00000 n 0000169479 00000 n 0000169554 00000 n 0000169609 00000 n 0000169838 00000 n 0000580847 00000 n 0000170056 00000 n 0000170776 00000 n 0000172558 00000 n 0000172729 00000 n 0000178430 00000 n 0000173154 00000 n 0000173357 00000 n 0000173720 00000 n 0000173814 00000 n 0000173970 00000 n 0000174199 00000 n 0000174421 00000 n 0000174644 00000 n 0000174852 00000 n 0000175050 00000 n 0000175269 00000 n 0000175464 00000 n 0000175659 00000 n 0000175882 00000 n 0000176085 00000 n 0000176269 00000 n 0000176444 00000 n 0000176643 00000 n 0000176830 00000 n 0000177035 00000 n 0000177251 00000 n 0000177426 00000 n 0000177628 00000 n 0000177814 00000 n 0000178000 00000 n 0000178208 00000 n 0000178842 00000 n 0000178967 00000 n 0000179120 00000 n 0000179214 00000 n 0000179450 00000 n 0000179696 00000 n 0000179925 00000 n 0000180129 00000 n 0000180332 00000 n 0000180535 00000 n 0000180778 00000 n 0000580959 00000 n 0000180988 00000 n 0000181838 00000 n 0000188222 00000 n 0000188407 00000 n 0000202141 00000 n 0000210705 00000 n 0000216235 00000 n 0000188924 00000 n 0000189310 00000 n 0000190149 00000 n 0000190243 00000 n 0000190451 00000 n 0000190659 00000 n 0000190824 00000 n 0000190980 00000 n 0000191191 00000 n 0000191402 00000 n 0000191576 00000 n 0000191805 00000 n 0000192013 00000 n 0000192220 00000 n 0000192422 00000 n 0000192652 00000 n 0000192870 00000 n 0000193092 00000 n 0000193315 00000 n 0000193560 00000 n 0000193773 00000 n 0000194020 00000 n 0000194239 00000 n 0000194452 00000 n 0000194657 00000 n 0000194905 00000 n 0000195104 00000 n 0000195274 00000 n 0000195505 00000 n 0000195766 00000 n 0000195993 00000 n 0000196231 00000 n 0000196441 00000 n 0000196664 00000 n 0000196899 00000 n 0000197094 00000 n 0000197308 00000 n 0000197557 00000 n 0000197765 00000 n 0000197981 00000 n 0000198179 00000 n 0000198398 00000 n 0000198593 00000 n 0000198788 00000 n 0000199011 00000 n 0000199214 00000 n 0000199398 00000 n 0000199573 00000 n 0000199772 00000 n 0000199959 00000 n 0000200164 00000 n 0000200380 00000 n 0000200592 00000 n 0000200767 00000 n 0000200969 00000 n 0000201155 00000 n 0000201341 00000 n 0000201549 00000 n 0000201759 00000 n 0000201978 00000 n 0000202580 00000 n 0000202849 00000 n 0000203352 00000 n 0000203446 00000 n 0000203699 00000 n 0000203849 00000 n 0000204060 00000 n 0000204287 00000 n 0000204462 00000 n 0000204698 00000 n 0000204937 00000 n 0000205184 00000 n 0000205433 00000 n 0000205650 00000 n 0000205940 00000 n 0000206206 00000 n 0000206460 00000 n 0000206696 00000 n 0000206900 00000 n 0000207125 00000 n 0000207326 00000 n 0000207555 00000 n 0000207759 00000 n 0000207986 00000 n 0000208189 00000 n 0000208392 00000 n 0000208622 00000 n 0000208825 00000 n 0000209029 00000 n 0000209272 00000 n 0000209456 00000 n 0000209646 00000 n 0000209842 00000 n 0000210047 00000 n 0000210270 00000 n 0000210480 00000 n 0000211115 00000 n 0000211317 00000 n 0000211638 00000 n 0000211732 00000 n 0000211891 00000 n 0000212089 00000 n 0000212329 00000 n 0000212609 00000 n 0000212867 00000 n 0000213107 00000 n 0000213292 00000 n 0000213581 00000 n 0000213854 00000 n 0000214102 00000 n 0000214360 00000 n 0000214574 00000 n 0000214793 00000 n 0000215007 00000 n 0000215215 00000 n 0000215404 00000 n 0000215599 00000 n 0000215809 00000 n 0000216036 00000 n 0000216672 00000 n 0000216934 00000 n 0000217423 00000 n 0000217517 00000 n 0000217672 00000 n 0000217853 00000 n 0000218082 00000 n 0000218326 00000 n 0000218573 00000 n 0000218789 00000 n 0000219012 00000 n 0000219184 00000 n 0000219443 00000 n 0000219690 00000 n 0000219933 00000 n 0000220162 00000 n 0000220362 00000 n 0000220578 00000 n 0000220783 00000 n 0000220995 00000 n 0000221204 00000 n 0000221417 00000 n 0000221626 00000 n 0000221809 00000 n 0000222041 00000 n 0000222214 00000 n 0000222379 00000 n 0000222578 00000 n 0000222764 00000 n 0000222963 00000 n 0000223172 00000 n 0000223358 00000 n 0000223559 00000 n 0000223752 00000 n 0000223942 00000 n 0000581071 00000 n 0000224172 00000 n 0000225062 00000 n 0000231950 00000 n 0000232121 00000 n 0000245900 00000 n 0000254062 00000 n 0000232638 00000 n 0000233017 00000 n 0000233856 00000 n 0000233950 00000 n 0000234158 00000 n 0000234366 00000 n 0000234531 00000 n 0000234687 00000 n 0000234898 00000 n 0000235072 00000 n 0000235301 00000 n 0000235509 00000 n 0000235716 00000 n 0000235918 00000 n 0000236148 00000 n 0000236366 00000 n 0000236588 00000 n 0000236811 00000 n 0000237056 00000 n 0000237269 00000 n 0000237516 00000 n 0000237735 00000 n 0000237948 00000 n 0000238153 00000 n 0000238401 00000 n 0000238600 00000 n 0000238770 00000 n 0000239031 00000 n 0000239258 00000 n 0000239496 00000 n 0000239706 00000 n 0000239929 00000 n 0000240164 00000 n 0000240359 00000 n 0000240573 00000 n 0000240821 00000 n 0000241094 00000 n 0000241302 00000 n 0000241518 00000 n 0000241716 00000 n 0000241935 00000 n 0000242130 00000 n 0000242325 00000 n 0000242548 00000 n 0000242751 00000 n 0000242935 00000 n 0000243110 00000 n 0000243309 00000 n 0000243496 00000 n 0000243701 00000 n 0000243917 00000 n 0000244129 00000 n 0000244304 00000 n 0000244506 00000 n 0000244692 00000 n 0000244878 00000 n 0000245086 00000 n 0000245308 00000 n 0000245518 00000 n 0000245737 00000 n 0000246331 00000 n 0000246593 00000 n 0000247068 00000 n 0000247162 00000 n 0000247321 00000 n 0000247535 00000 n 0000247773 00000 n 0000247987 00000 n 0000248223 00000 n 0000248503 00000 n 0000248761 00000 n 0000249001 00000 n 0000249186 00000 n 0000249475 00000 n 0000249748 00000 n 0000249996 00000 n 0000250254 00000 n 0000250468 00000 n 0000250691 00000 n 0000250916 00000 n 0000251135 00000 n 0000251363 00000 n 0000251577 00000 n 0000251785 00000 n 0000251974 00000 n 0000252149 00000 n 0000252369 00000 n 0000252564 00000 n 0000252774 00000 n 0000253001 00000 n 0000253196 00000 n 0000253406 00000 n 0000253605 00000 n 0000253823 00000 n 0000254506 00000 n 0000254803 00000 n 0000255390 00000 n 0000255484 00000 n 0000255639 00000 n 0000255820 00000 n 0000256049 00000 n 0000256277 00000 n 0000256484 00000 n 0000256711 00000 n 0000256955 00000 n 0000257202 00000 n 0000257418 00000 n 0000257641 00000 n 0000257888 00000 n 0000258060 00000 n 0000258319 00000 n 0000258566 00000 n 0000258809 00000 n 0000259014 00000 n 0000259243 00000 n 0000259484 00000 n 0000259684 00000 n 0000259900 00000 n 0000260105 00000 n 0000260317 00000 n 0000260526 00000 n 0000260739 00000 n 0000260948 00000 n 0000261131 00000 n 0000261363 00000 n 0000261536 00000 n 0000261701 00000 n 0000261900 00000 n 0000262086 00000 n 0000262285 00000 n 0000262494 00000 n 0000262680 00000 n 0000262881 00000 n 0000263074 00000 n 0000263264 00000 n 0000263466 00000 n 0000581183 00000 n 0000263696 00000 n 0000264598 00000 n 0000272016 00000 n 0000272173 00000 n 0000285207 00000 n 0000583193 00000 n 0000272687 00000 n 0000273055 00000 n 0000273852 00000 n 0000273946 00000 n 0000274154 00000 n 0000274362 00000 n 0000274527 00000 n 0000274738 00000 n 0000274949 00000 n 0000275123 00000 n 0000275352 00000 n 0000275560 00000 n 0000275767 00000 n 0000275969 00000 n 0000276199 00000 n 0000276417 00000 n 0000276639 00000 n 0000276862 00000 n 0000277107 00000 n 0000277320 00000 n 0000277567 00000 n 0000277786 00000 n 0000277999 00000 n 0000278198 00000 n 0000278368 00000 n 0000278564 00000 n 0000278825 00000 n 0000279052 00000 n 0000279290 00000 n 0000279500 00000 n 0000279723 00000 n 0000279958 00000 n 0000280153 00000 n 0000280401 00000 n 0000280609 00000 n 0000280825 00000 n 0000281023 00000 n 0000281242 00000 n 0000281437 00000 n 0000281632 00000 n 0000281855 00000 n 0000282058 00000 n 0000282242 00000 n 0000282417 00000 n 0000282616 00000 n 0000282803 00000 n 0000283008 00000 n 0000283224 00000 n 0000283436 00000 n 0000283611 00000 n 0000283813 00000 n 0000283999 00000 n 0000284185 00000 n 0000284393 00000 n 0000284615 00000 n 0000284825 00000 n 0000285044 00000 n 0000285650 00000 n 0000285945 00000 n 0000286518 00000 n 0000286612 00000 n 0000286767 00000 n 0000286995 00000 n 0000287202 00000 n 0000287429 00000 n 0000287655 00000 n 0000287867 00000 n 0000288098 00000 n 0000288323 00000 n 0000288567 00000 n 0000288787 00000 n 0000289034 00000 n 0000289257 00000 n 0000289504 00000 n 0000289676 00000 n 0000289935 00000 n 0000290182 00000 n 0000290425 00000 n 0000290630 00000 n 0000290871 00000 n 0000291071 00000 n 0000291276 00000 n 0000291488 00000 n 0000291701 00000 n 0000291910 00000 n 0000292142 00000 n 0000292315 00000 n 0000292480 00000 n 0000292679 00000 n 0000292865 00000 n 0000293064 00000 n 0000293273 00000 n 0000293486 00000 n 0000293672 00000 n 0000293873 00000 n 0000294066 00000 n 0000294256 00000 n 0000294458 00000 n 0000581295 00000 n 0000294688 00000 n 0000295233 00000 n 0000296334 00000 n 0000296477 00000 n 0000296911 00000 n 0000297164 00000 n 0000297597 00000 n 0000297691 00000 n 0000297954 00000 n 0000298162 00000 n 0000298370 00000 n 0000298535 00000 n 0000298746 00000 n 0000298920 00000 n 0000299149 00000 n 0000299357 00000 n 0000299587 00000 n 0000299834 00000 n 0000300053 00000 n 0000300223 00000 n 0000300461 00000 n 0000300696 00000 n 0000300904 00000 n 0000301123 00000 n 0000301318 00000 n 0000301513 00000 n 0000301697 00000 n 0000301872 00000 n 0000302059 00000 n 0000302264 00000 n 0000302439 00000 n 0000302641 00000 n 0000302827 00000 n 0000303035 00000 n 0000303245 00000 n 0000581407 00000 n 0000303464 00000 n 0000304345 00000 n 0000306553 00000 n 0000306724 00000 n 0000320635 00000 n 0000326651 00000 n 0000307190 00000 n 0000307573 00000 n 0000308426 00000 n 0000308520 00000 n 0000308677 00000 n 0000308833 00000 n 0000309044 00000 n 0000309255 00000 n 0000309429 00000 n 0000309658 00000 n 0000309866 00000 n 0000310073 00000 n 0000310275 00000 n 0000310505 00000 n 0000310723 00000 n 0000310945 00000 n 0000311168 00000 n 0000311345 00000 n 0000311590 00000 n 0000311803 00000 n 0000312050 00000 n 0000312269 00000 n 0000312482 00000 n 0000312687 00000 n 0000312935 00000 n 0000313134 00000 n 0000313304 00000 n 0000313500 00000 n 0000313761 00000 n 0000313999 00000 n 0000314209 00000 n 0000314432 00000 n 0000314667 00000 n 0000314862 00000 n 0000315076 00000 n 0000315324 00000 n 0000315573 00000 n 0000315781 00000 n 0000315997 00000 n 0000316195 00000 n 0000316414 00000 n 0000316609 00000 n 0000316804 00000 n 0000317027 00000 n 0000317230 00000 n 0000317414 00000 n 0000317632 00000 n 0000317807 00000 n 0000318006 00000 n 0000318193 00000 n 0000318398 00000 n 0000318614 00000 n 0000318826 00000 n 0000319001 00000 n 0000319203 00000 n 0000319389 00000 n 0000319575 00000 n 0000319783 00000 n 0000320005 00000 n 0000320215 00000 n 0000320434 00000 n 0000321052 00000 n 0000321268 00000 n 0000321631 00000 n 0000321725 00000 n 0000321884 00000 n 0000322082 00000 n 0000322317 00000 n 0000322575 00000 n 0000322815 00000 n 0000323050 00000 n 0000323235 00000 n 0000323508 00000 n 0000323734 00000 n 0000323948 00000 n 0000324171 00000 n 0000324385 00000 n 0000324593 00000 n 0000324840 00000 n 0000325029 00000 n 0000325204 00000 n 0000325424 00000 n 0000325619 00000 n 0000325829 00000 n 0000326024 00000 n 0000326234 00000 n 0000326433 00000 n 0000327089 00000 n 0000327377 00000 n 0000327928 00000 n 0000328022 00000 n 0000328228 00000 n 0000328436 00000 n 0000328646 00000 n 0000328828 00000 n 0000329057 00000 n 0000329283 00000 n 0000329528 00000 n 0000329776 00000 n 0000329993 00000 n 0000330217 00000 n 0000330465 00000 n 0000330638 00000 n 0000330846 00000 n 0000331106 00000 n 0000331354 00000 n 0000331598 00000 n 0000331804 00000 n 0000332068 00000 n 0000332310 00000 n 0000332511 00000 n 0000332728 00000 n 0000332890 00000 n 0000333096 00000 n 0000333306 00000 n 0000333520 00000 n 0000333730 00000 n 0000333914 00000 n 0000334088 00000 n 0000334275 00000 n 0000334475 00000 n 0000334662 00000 n 0000334856 00000 n 0000335059 00000 n 0000581519 00000 n 0000335263 00000 n 0000336209 00000 n 0000338629 00000 n 0000338789 00000 n 0000353756 00000 n 0000339262 00000 n 0000339663 00000 n 0000340636 00000 n 0000340731 00000 n 0000340943 00000 n 0000341152 00000 n 0000341361 00000 n 0000341519 00000 n 0000341676 00000 n 0000341888 00000 n 0000342100 00000 n 0000342275 00000 n 0000342505 00000 n 0000342714 00000 n 0000342922 00000 n 0000343125 00000 n 0000343356 00000 n 0000343575 00000 n 0000343798 00000 n 0000344022 00000 n 0000344268 00000 n 0000344482 00000 n 0000344730 00000 n 0000344950 00000 n 0000345164 00000 n 0000345370 00000 n 0000345619 00000 n 0000345819 00000 n 0000345990 00000 n 0000346187 00000 n 0000346449 00000 n 0000346677 00000 n 0000346916 00000 n 0000347127 00000 n 0000347351 00000 n 0000347587 00000 n 0000347783 00000 n 0000347998 00000 n 0000348247 00000 n 0000348470 00000 n 0000348679 00000 n 0000348896 00000 n 0000349095 00000 n 0000349315 00000 n 0000349511 00000 n 0000349707 00000 n 0000349931 00000 n 0000350135 00000 n 0000350320 00000 n 0000350519 00000 n 0000350738 00000 n 0000350914 00000 n 0000351114 00000 n 0000351302 00000 n 0000351508 00000 n 0000351725 00000 n 0000351938 00000 n 0000352114 00000 n 0000352317 00000 n 0000352504 00000 n 0000352691 00000 n 0000352900 00000 n 0000353123 00000 n 0000353334 00000 n 0000353554 00000 n 0000354198 00000 n 0000354496 00000 n 0000355064 00000 n 0000355159 00000 n 0000355365 00000 n 0000355573 00000 n 0000355783 00000 n 0000355965 00000 n 0000356195 00000 n 0000356424 00000 n 0000356652 00000 n 0000356865 00000 n 0000357110 00000 n 0000357358 00000 n 0000357575 00000 n 0000357799 00000 n 0000358047 00000 n 0000358220 00000 n 0000358428 00000 n 0000358688 00000 n 0000358932 00000 n 0000359138 00000 n 0000359368 00000 n 0000359610 00000 n 0000359811 00000 n 0000360043 00000 n 0000360205 00000 n 0000360411 00000 n 0000360621 00000 n 0000360835 00000 n 0000361045 00000 n 0000361229 00000 n 0000361403 00000 n 0000361590 00000 n 0000361790 00000 n 0000361977 00000 n 0000362171 00000 n 0000362374 00000 n 0000581635 00000 n 0000362578 00000 n 0000363490 00000 n 0000365741 00000 n 0000365901 00000 n 0000378974 00000 n 0000366364 00000 n 0000366729 00000 n 0000367582 00000 n 0000367677 00000 n 0000367835 00000 n 0000367992 00000 n 0000368204 00000 n 0000368416 00000 n 0000368591 00000 n 0000368821 00000 n 0000369030 00000 n 0000369238 00000 n 0000369441 00000 n 0000369672 00000 n 0000369891 00000 n 0000370114 00000 n 0000370338 00000 n 0000370584 00000 n 0000370798 00000 n 0000371046 00000 n 0000371266 00000 n 0000371480 00000 n 0000371686 00000 n 0000371935 00000 n 0000372135 00000 n 0000372306 00000 n 0000372503 00000 n 0000372731 00000 n 0000372970 00000 n 0000373181 00000 n 0000373405 00000 n 0000373641 00000 n 0000373837 00000 n 0000374052 00000 n 0000374302 00000 n 0000374511 00000 n 0000374728 00000 n 0000374927 00000 n 0000375147 00000 n 0000375343 00000 n 0000375539 00000 n 0000375763 00000 n 0000375967 00000 n 0000376152 00000 n 0000376371 00000 n 0000376547 00000 n 0000376747 00000 n 0000376935 00000 n 0000377141 00000 n 0000377358 00000 n 0000377534 00000 n 0000377737 00000 n 0000377924 00000 n 0000378111 00000 n 0000378320 00000 n 0000378543 00000 n 0000378754 00000 n 0000379413 00000 n 0000379694 00000 n 0000380217 00000 n 0000380312 00000 n 0000380518 00000 n 0000380726 00000 n 0000380936 00000 n 0000381118 00000 n 0000381348 00000 n 0000381577 00000 n 0000381785 00000 n 0000382011 00000 n 0000382256 00000 n 0000382504 00000 n 0000382728 00000 n 0000382976 00000 n 0000383149 00000 n 0000383357 00000 n 0000383617 00000 n 0000383861 00000 n 0000384067 00000 n 0000384297 00000 n 0000384539 00000 n 0000384701 00000 n 0000384907 00000 n 0000385117 00000 n 0000385331 00000 n 0000385541 00000 n 0000385725 00000 n 0000385899 00000 n 0000386086 00000 n 0000386286 00000 n 0000386473 00000 n 0000386667 00000 n 0000386870 00000 n 0000581751 00000 n 0000387074 00000 n 0000387970 00000 n 0000390204 00000 n 0000390379 00000 n 0000404180 00000 n 0000410360 00000 n 0000390847 00000 n 0000391226 00000 n 0000392124 00000 n 0000392219 00000 n 0000392377 00000 n 0000392534 00000 n 0000392746 00000 n 0000392921 00000 n 0000393151 00000 n 0000393360 00000 n 0000393568 00000 n 0000393771 00000 n 0000394002 00000 n 0000394221 00000 n 0000394444 00000 n 0000394668 00000 n 0000394846 00000 n 0000395092 00000 n 0000395306 00000 n 0000395554 00000 n 0000395774 00000 n 0000395988 00000 n 0000396194 00000 n 0000396443 00000 n 0000396643 00000 n 0000396814 00000 n 0000397011 00000 n 0000397273 00000 n 0000397512 00000 n 0000397723 00000 n 0000397947 00000 n 0000398183 00000 n 0000398379 00000 n 0000398594 00000 n 0000398843 00000 n 0000399093 00000 n 0000399302 00000 n 0000399519 00000 n 0000399718 00000 n 0000399938 00000 n 0000400134 00000 n 0000400330 00000 n 0000400554 00000 n 0000400758 00000 n 0000400943 00000 n 0000401162 00000 n 0000401338 00000 n 0000401538 00000 n 0000401726 00000 n 0000401932 00000 n 0000402149 00000 n 0000402362 00000 n 0000402538 00000 n 0000402741 00000 n 0000402928 00000 n 0000403115 00000 n 0000403324 00000 n 0000403547 00000 n 0000403758 00000 n 0000403978 00000 n 0000404600 00000 n 0000404821 00000 n 0000405209 00000 n 0000405304 00000 n 0000405464 00000 n 0000405705 00000 n 0000405941 00000 n 0000406182 00000 n 0000406418 00000 n 0000406604 00000 n 0000406894 00000 n 0000407168 00000 n 0000407395 00000 n 0000407644 00000 n 0000407868 00000 n 0000408083 00000 n 0000408292 00000 n 0000408540 00000 n 0000408730 00000 n 0000408906 00000 n 0000409127 00000 n 0000409323 00000 n 0000409534 00000 n 0000409730 00000 n 0000409941 00000 n 0000410141 00000 n 0000410801 00000 n 0000411090 00000 n 0000411643 00000 n 0000411738 00000 n 0000411944 00000 n 0000412152 00000 n 0000412362 00000 n 0000412544 00000 n 0000412773 00000 n 0000412999 00000 n 0000413244 00000 n 0000413492 00000 n 0000413709 00000 n 0000413933 00000 n 0000414181 00000 n 0000414354 00000 n 0000414562 00000 n 0000414822 00000 n 0000415070 00000 n 0000415314 00000 n 0000415520 00000 n 0000415784 00000 n 0000416026 00000 n 0000416227 00000 n 0000416444 00000 n 0000416606 00000 n 0000416812 00000 n 0000417022 00000 n 0000417236 00000 n 0000417446 00000 n 0000417630 00000 n 0000417804 00000 n 0000417991 00000 n 0000418191 00000 n 0000418378 00000 n 0000418572 00000 n 0000418775 00000 n 0000581867 00000 n 0000418979 00000 n 0000419857 00000 n 0000421780 00000 n 0000421925 00000 n 0000583582 00000 n 0000583448 00000 n 0000422388 00000 n 0000422747 00000 n 0000423600 00000 n 0000423695 00000 n 0000423852 00000 n 0000424064 00000 n 0000424276 00000 n 0000424451 00000 n 0000424681 00000 n 0000424890 00000 n 0000425098 00000 n 0000425301 00000 n 0000425532 00000 n 0000425751 00000 n 0000425974 00000 n 0000426198 00000 n 0000426444 00000 n 0000426658 00000 n 0000426906 00000 n 0000427126 00000 n 0000427340 00000 n 0000427546 00000 n 0000427795 00000 n 0000427966 00000 n 0000428163 00000 n 0000428425 00000 n 0000428664 00000 n 0000428875 00000 n 0000429099 00000 n 0000429335 00000 n 0000429531 00000 n 0000429746 00000 n 0000429995 00000 n 0000430204 00000 n 0000430421 00000 n 0000430620 00000 n 0000430840 00000 n 0000431036 00000 n 0000431232 00000 n 0000431456 00000 n 0000431660 00000 n 0000431845 00000 n 0000432044 00000 n 0000432263 00000 n 0000432439 00000 n 0000432639 00000 n 0000432827 00000 n 0000433033 00000 n 0000433250 00000 n 0000433463 00000 n 0000433639 00000 n 0000433842 00000 n 0000434029 00000 n 0000434216 00000 n 0000434425 00000 n 0000434648 00000 n 0000434859 00000 n 0000581984 00000 n 0000435079 00000 n 0000435981 00000 n 0000438075 00000 n 0000438235 00000 n 0000451350 00000 n 0000438700 00000 n 0000439059 00000 n 0000439912 00000 n 0000440007 00000 n 0000440165 00000 n 0000440377 00000 n 0000440589 00000 n 0000440764 00000 n 0000440994 00000 n 0000441203 00000 n 0000441411 00000 n 0000441614 00000 n 0000441845 00000 n 0000442064 00000 n 0000442287 00000 n 0000442511 00000 n 0000442757 00000 n 0000442971 00000 n 0000443219 00000 n 0000443439 00000 n 0000443653 00000 n 0000443859 00000 n 0000444108 00000 n 0000444308 00000 n 0000444505 00000 n 0000444733 00000 n 0000444944 00000 n 0000445168 00000 n 0000445404 00000 n 0000445600 00000 n 0000445815 00000 n 0000446064 00000 n 0000446273 00000 n 0000446490 00000 n 0000446689 00000 n 0000446909 00000 n 0000447105 00000 n 0000447301 00000 n 0000447525 00000 n 0000447729 00000 n 0000447914 00000 n 0000448113 00000 n 0000448332 00000 n 0000448508 00000 n 0000448708 00000 n 0000448896 00000 n 0000449102 00000 n 0000449319 00000 n 0000449532 00000 n 0000449708 00000 n 0000449911 00000 n 0000450098 00000 n 0000450285 00000 n 0000450494 00000 n 0000450717 00000 n 0000450928 00000 n 0000451148 00000 n 0000451787 00000 n 0000452056 00000 n 0000452549 00000 n 0000452644 00000 n 0000452850 00000 n 0000453058 00000 n 0000453268 00000 n 0000453450 00000 n 0000453680 00000 n 0000453909 00000 n 0000454154 00000 n 0000454402 00000 n 0000454626 00000 n 0000454874 00000 n 0000455047 00000 n 0000455255 00000 n 0000455515 00000 n 0000455759 00000 n 0000455965 00000 n 0000456195 00000 n 0000456437 00000 n 0000456599 00000 n 0000456805 00000 n 0000457015 00000 n 0000457229 00000 n 0000457439 00000 n 0000457623 00000 n 0000457797 00000 n 0000457984 00000 n 0000458184 00000 n 0000458371 00000 n 0000458565 00000 n 0000458768 00000 n 0000582101 00000 n 0000458972 00000 n 0000459899 00000 n 0000462180 00000 n 0000462340 00000 n 0000476371 00000 n 0000462807 00000 n 0000463191 00000 n 0000464104 00000 n 0000464199 00000 n 0000464408 00000 n 0000464617 00000 n 0000464775 00000 n 0000464932 00000 n 0000465144 00000 n 0000465356 00000 n 0000465531 00000 n 0000465761 00000 n 0000465970 00000 n 0000466178 00000 n 0000466381 00000 n 0000466612 00000 n 0000466831 00000 n 0000467054 00000 n 0000467278 00000 n 0000467524 00000 n 0000467738 00000 n 0000467986 00000 n 0000468206 00000 n 0000468420 00000 n 0000468626 00000 n 0000468826 00000 n 0000468997 00000 n 0000469194 00000 n 0000469456 00000 n 0000469684 00000 n 0000469923 00000 n 0000470134 00000 n 0000470358 00000 n 0000470594 00000 n 0000470790 00000 n 0000471005 00000 n 0000471255 00000 n 0000471486 00000 n 0000471695 00000 n 0000471912 00000 n 0000472111 00000 n 0000472331 00000 n 0000472527 00000 n 0000472723 00000 n 0000472947 00000 n 0000473151 00000 n 0000473336 00000 n 0000473555 00000 n 0000473731 00000 n 0000473931 00000 n 0000474119 00000 n 0000474325 00000 n 0000474542 00000 n 0000474755 00000 n 0000474931 00000 n 0000475134 00000 n 0000475321 00000 n 0000475508 00000 n 0000475717 00000 n 0000475940 00000 n 0000476151 00000 n 0000476814 00000 n 0000477119 00000 n 0000477702 00000 n 0000477797 00000 n 0000478003 00000 n 0000478211 00000 n 0000478421 00000 n 0000478603 00000 n 0000478833 00000 n 0000479062 00000 n 0000479290 00000 n 0000479503 00000 n 0000479729 00000 n 0000479974 00000 n 0000480222 00000 n 0000480439 00000 n 0000480663 00000 n 0000480911 00000 n 0000481084 00000 n 0000481292 00000 n 0000481552 00000 n 0000481796 00000 n 0000482002 00000 n 0000482232 00000 n 0000482474 00000 n 0000482675 00000 n 0000482907 00000 n 0000483069 00000 n 0000483275 00000 n 0000483485 00000 n 0000483699 00000 n 0000483909 00000 n 0000484093 00000 n 0000484267 00000 n 0000484454 00000 n 0000484654 00000 n 0000484841 00000 n 0000485035 00000 n 0000485238 00000 n 0000582218 00000 n 0000485442 00000 n 0000486252 00000 n 0000488100 00000 n 0000488260 00000 n 0000502133 00000 n 0000488726 00000 n 0000489111 00000 n 0000490009 00000 n 0000490104 00000 n 0000490270 00000 n 0000490427 00000 n 0000490639 00000 n 0000490814 00000 n 0000491044 00000 n 0000491253 00000 n 0000491461 00000 n 0000491664 00000 n 0000491895 00000 n 0000492114 00000 n 0000492337 00000 n 0000492561 00000 n 0000492807 00000 n 0000493021 00000 n 0000493269 00000 n 0000493489 00000 n 0000493703 00000 n 0000493909 00000 n 0000494158 00000 n 0000494358 00000 n 0000494529 00000 n 0000494726 00000 n 0000494988 00000 n 0000495227 00000 n 0000495438 00000 n 0000495662 00000 n 0000495898 00000 n 0000496113 00000 n 0000496387 00000 n 0000496637 00000 n 0000496868 00000 n 0000497091 00000 n 0000497248 00000 n 0000497457 00000 n 0000497674 00000 n 0000497873 00000 n 0000498093 00000 n 0000498289 00000 n 0000498485 00000 n 0000498709 00000 n 0000498913 00000 n 0000499098 00000 n 0000499317 00000 n 0000499493 00000 n 0000499693 00000 n 0000499881 00000 n 0000500087 00000 n 0000500304 00000 n 0000500517 00000 n 0000500693 00000 n 0000500896 00000 n 0000501083 00000 n 0000501270 00000 n 0000501479 00000 n 0000501702 00000 n 0000501913 00000 n 0000502570 00000 n 0000502841 00000 n 0000503334 00000 n 0000503429 00000 n 0000503635 00000 n 0000503843 00000 n 0000504053 00000 n 0000504235 00000 n 0000504464 00000 n 0000504672 00000 n 0000504917 00000 n 0000505165 00000 n 0000505389 00000 n 0000505637 00000 n 0000505810 00000 n 0000506018 00000 n 0000506278 00000 n 0000506522 00000 n 0000506728 00000 n 0000506958 00000 n 0000507200 00000 n 0000507362 00000 n 0000507568 00000 n 0000507778 00000 n 0000507992 00000 n 0000508202 00000 n 0000508386 00000 n 0000508560 00000 n 0000508747 00000 n 0000508947 00000 n 0000509134 00000 n 0000509328 00000 n 0000509531 00000 n 0000582335 00000 n 0000509735 00000 n 0000510666 00000 n 0000517064 00000 n 0000517239 00000 n 0000530552 00000 n 0000535781 00000 n 0000517703 00000 n 0000518077 00000 n 0000518945 00000 n 0000519040 00000 n 0000519249 00000 n 0000519458 00000 n 0000519624 00000 n 0000519782 00000 n 0000519939 00000 n 0000520151 00000 n 0000520326 00000 n 0000520556 00000 n 0000520765 00000 n 0000520973 00000 n 0000521176 00000 n 0000521395 00000 n 0000521618 00000 n 0000521842 00000 n 0000522088 00000 n 0000522302 00000 n 0000522550 00000 n 0000522770 00000 n 0000522984 00000 n 0000523190 00000 n 0000523439 00000 n 0000523610 00000 n 0000523807 00000 n 0000524069 00000 n 0000524297 00000 n 0000524536 00000 n 0000524747 00000 n 0000524971 00000 n 0000525207 00000 n 0000525403 00000 n 0000525618 00000 n 0000525868 00000 n 0000526099 00000 n 0000526308 00000 n 0000526525 00000 n 0000526724 00000 n 0000526944 00000 n 0000527140 00000 n 0000527336 00000 n 0000527560 00000 n 0000527764 00000 n 0000527949 00000 n 0000528125 00000 n 0000528325 00000 n 0000528513 00000 n 0000528719 00000 n 0000528936 00000 n 0000529112 00000 n 0000529315 00000 n 0000529502 00000 n 0000529689 00000 n 0000529898 00000 n 0000530121 00000 n 0000530332 00000 n 0000530974 00000 n 0000531178 00000 n 0000531506 00000 n 0000531601 00000 n 0000531761 00000 n 0000531960 00000 n 0000532219 00000 n 0000532460 00000 n 0000532646 00000 n 0000532936 00000 n 0000533210 00000 n 0000533459 00000 n 0000533683 00000 n 0000533903 00000 n 0000534118 00000 n 0000534327 00000 n 0000534517 00000 n 0000534738 00000 n 0000534934 00000 n 0000535145 00000 n 0000535341 00000 n 0000535541 00000 n 0000536225 00000 n 0000536506 00000 n 0000537089 00000 n 0000537184 00000 n 0000537340 00000 n 0000537522 00000 n 0000537752 00000 n 0000537981 00000 n 0000538226 00000 n 0000538447 00000 n 0000538695 00000 n 0000538912 00000 n 0000539085 00000 n 0000539332 00000 n 0000539592 00000 n 0000539840 00000 n 0000540084 00000 n 0000540290 00000 n 0000540520 00000 n 0000540762 00000 n 0000540963 00000 n 0000541180 00000 n 0000541386 00000 n 0000541599 00000 n 0000541809 00000 n 0000542023 00000 n 0000542233 00000 n 0000542417 00000 n 0000542650 00000 n 0000542824 00000 n 0000542990 00000 n 0000543190 00000 n 0000543377 00000 n 0000543577 00000 n 0000543787 00000 n 0000543974 00000 n 0000544176 00000 n 0000544370 00000 n 0000544561 00000 n 0000582452 00000 n 0000544792 00000 n 0000545544 00000 n 0000550103 00000 n 0000550263 00000 n 0000563654 00000 n 0000550727 00000 n 0000551101 00000 n 0000551969 00000 n 0000552064 00000 n 0000552273 00000 n 0000552482 00000 n 0000552648 00000 n 0000552860 00000 n 0000553072 00000 n 0000553247 00000 n 0000553477 00000 n 0000553686 00000 n 0000553894 00000 n 0000554097 00000 n 0000554328 00000 n 0000554547 00000 n 0000554770 00000 n 0000554994 00000 n 0000555240 00000 n 0000555454 00000 n 0000555702 00000 n 0000555922 00000 n 0000556136 00000 n 0000556342 00000 n 0000556591 00000 n 0000556762 00000 n 0000556994 00000 n 0000557191 00000 n 0000557419 00000 n 0000557658 00000 n 0000557869 00000 n 0000558093 00000 n 0000558329 00000 n 0000558525 00000 n 0000558740 00000 n 0000558989 00000 n 0000559198 00000 n 0000559415 00000 n 0000559614 00000 n 0000559810 00000 n 0000560006 00000 n 0000560230 00000 n 0000560434 00000 n 0000560619 00000 n 0000560838 00000 n 0000561014 00000 n 0000561214 00000 n 0000561402 00000 n 0000561608 00000 n 0000561825 00000 n 0000562038 00000 n 0000562214 00000 n 0000562417 00000 n 0000562604 00000 n 0000562791 00000 n 0000563000 00000 n 0000563223 00000 n 0000563434 00000 n 0000564085 00000 n 0000564312 00000 n 0000564700 00000 n 0000564795 00000 n 0000564951 00000 n 0000565133 00000 n 0000565362 00000 n 0000565570 00000 n 0000565815 00000 n 0000566063 00000 n 0000566236 00000 n 0000566483 00000 n 0000566731 00000 n 0000566932 00000 n 0000567138 00000 n 0000567351 00000 n 0000567561 00000 n 0000567794 00000 n 0000567968 00000 n 0000568168 00000 n 0000568368 00000 n 0000568578 00000 n 0000568765 00000 n 0000568967 00000 n 0000569161 00000 n 0000569352 00000 n 0000572318 00000 n 0000573622 00000 n 0000573832 00000 n 0000574262 00000 n 0000574331 00000 n 0000574374 00000 n 0000574820 00000 n 0000574889 00000 n 0000574932 00000 n 0000575351 00000 n 0000575420 00000 n 0000575463 00000 n 0000575911 00000 n 0000575980 00000 n 0000576023 00000 n 0000576459 00000 n 0000576528 00000 n 0000576571 00000 n 0000577011 00000 n 0000577080 00000 n 0000577123 00000 n 0000577564 00000 n 0000577633 00000 n 0000577676 00000 n 0000583688 00000 n 0000583749 00000 n trailer << /Size 1978 /Root 1976 0 R /Info 1977 0 R /ID [<86f1a4557d86839079c0732552169230><86f1a4557d86839079c0732552169230>] >> startxref 583858 %%EOF minc-tools-2.3.00+dfsg/conversion/dcm2mnc/doc/ge_signa_r3.pdf0000644000175000000620000172005512574624760022764 0ustar stevestaff%PDF-1.3 % 267 0 obj << /Linearized 1 /O 269 /H [ 1068 1221 ] /L 499757 /E 156316 /N 82 /T 494298 >> endobj xref 267 31 0000000016 00000 n 0000000971 00000 n 0000002289 00000 n 0000002447 00000 n 0000002666 00000 n 0000003154 00000 n 0000003750 00000 n 0000004327 00000 n 0000004569 00000 n 0000004610 00000 n 0000004840 00000 n 0000005064 00000 n 0000007726 00000 n 0000007882 00000 n 0000008185 00000 n 0000008421 00000 n 0000008985 00000 n 0000009582 00000 n 0000009812 00000 n 0000033913 00000 n 0000055894 00000 n 0000081198 00000 n 0000081426 00000 n 0000084104 00000 n 0000084183 00000 n 0000119274 00000 n 0000119481 00000 n 0000126143 00000 n 0000156086 00000 n 0000001068 00000 n 0000002266 00000 n trailer << /Size 298 /Info 265 0 R /Root 268 0 R /Prev 494287 /ID[<134609d62d82bb98ce90bd9224cf4b58>] >> startxref 0 %%EOF 268 0 obj << /Type /Catalog /Pages 256 0 R /Metadata 266 0 R /PageLabels 254 0 R >> endobj 296 0 obj << /S 2333 /L 2434 /Filter /FlateDecode /Length 297 0 R >> stream Hb```f``a`2@(qt0AAgS)CWIn5I k+8$m7dP!0ɧaA/㾆W^22LRaq >)cVCf"SCg &ְ3~q( [c@V@;aF1+zba=]'R0tx>;0eWg~:K\[C"K1 $WnVvr ~0n>&>[giHm&%ڛsY|Cc[eA.ghi8.bj"1/!d>tY>Pxٖ.kYÐ (wGqVBnSlG>+@-3 y0x_%4PE,#{x2|bvO ]l)'il*8Z7&$GD3 !PRALgb`Ik;QC-tKu@ZU>d`(eĸII$Pd/v.[bF6LL: {cnIZϰ˗A2ɻ%TsSf8;?(^  iȤ endstream endobj 297 0 obj 1102 endobj 269 0 obj << /Type /Page /Parent 255 0 R /Resources 270 0 R /Contents 278 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 270 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R /GS2 295 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 271 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 121 /Widths [ 278 0 0 556 0 0 0 0 333 333 0 0 278 333 278 278 556 556 556 0 556 0 0 556 556 0 333 0 0 0 0 0 0 722 722 722 722 667 611 778 0 278 556 0 611 833 722 778 667 0 722 667 611 0 667 0 667 667 0 0 0 0 0 0 0 556 611 556 611 556 333 611 611 278 0 0 278 889 611 611 611 0 389 556 333 611 556 778 556 556 ] /Encoding /WinAnsiEncoding /BaseFont /DBPGGD+Arial,BoldItalic /FontDescriptor 274 0 R >> endobj 272 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 150 /Widths [ 278 0 355 556 0 0 0 191 333 333 389 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 0 584 584 0 0 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 0 278 0 0 556 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 0 334 0 0 0 0 0 0 0 1000 0 0 0 0 0 0 0 0 0 0 0 0 222 333 333 0 556 ] /Encoding /WinAnsiEncoding /BaseFont /DBPFLK+Arial /FontDescriptor 277 0 R >> endobj 273 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 148 /Widths [ 278 0 474 0 0 0 0 238 333 333 0 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 0 584 0 584 0 0 722 722 722 722 667 611 778 722 278 0 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 0 0 0 0 556 0 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 278 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /DBPIKM+Arial,Bold /FontDescriptor 276 0 R >> endobj 274 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 718 /Descent -211 /Flags 96 /FontBBox [ -560 -376 1157 1031 ] /FontName /DBPGGD+Arial,BoldItalic /ItalicAngle -15 /StemV 143.849 /XHeight 515 /FontFile2 286 0 R >> endobj 275 0 obj [ /ICCBased 289 0 R ] endobj 276 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 718 /Descent -211 /Flags 32 /FontBBox [ -628 -376 2034 1048 ] /FontName /DBPIKM+Arial,Bold /ItalicAngle 0 /StemV 144 /XHeight 515 /FontFile2 285 0 R >> endobj 277 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 718 /Descent -211 /Flags 32 /FontBBox [ -665 -325 2028 1037 ] /FontName /DBPFLK+Arial /ItalicAngle 0 /StemV 94 /XHeight 515 /FontFile2 287 0 R >> endobj 278 0 obj << /Length 2586 /Filter /FlateDecode >> stream HWrWp$Qv,th8f zY"|P@ 3*|f\Go^9;Xo,էTV٬ښzݙ3+k5:0xцЀ0e3$9bK-a?a~|sֆK@zZ6gw7wi}=/N~s6ONOޏ{|R1E! !;"`*i'2DTe_p#Kxٗۛ's{w~IFQAJ%O֖ yzu .Axt;9C8|Dž zw~xҼT *O_\~vas;mPCg!05{̅Y0d(3Ga*>No+a3w)]9X Еloab@T~1;l-Y-x^M$p6 uCmpFQv5ʒWÖa,}.+-mr*]@ܟ-۳| ۡ8T /ݣXp7-r SeNM%m1*ifZȞOxH "UHxNQщC_Lo ]*b~8ęx%Wb-;~>:> }DDMQhLj8>i>BOb͏IhEVd|J0! mtr<"3O0PS\˒Jvap8ʫ(Gٹy: J2 V19lEeR)87# kfb'NVv;ԒЂ IUN IOXǾ.(F&'*UrEԼFvgRhBWAtk3_kCIG[gMk-2[MEtx)l64 'l۝H͏?_;H(r@qvt(HED\-8 W%1)\f3}$`D+H<0#α-hV$IjnG}6q8\$""%dJi(.8MYVInӺi"ab. ݒBǹډbK|aX2 *#˝e~ط"]Y9 A@WR se1Um*YJ="oԠɈ[R{tw#VwMXdyS 0\PlchJD]*ⴄ > endobj 280 0 obj << /Filter /FlateDecode /Length 228 >> stream HT=o w~ō2@htXC?T; b%޻}kK( [9,d.8:{ ֙E63ܭs©Ca;'D+]=! >~mv8Gm!ċ*Uz?䍺 檉=Pl)T,[UR~xwel*bev9OIm, endstream endobj 281 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 656 /Descent -216 /Flags 34 /FontBBox [ -558 -307 2034 1026 ] /FontName /DBPJOA+TimesNewRoman,Bold /ItalicAngle 0 /StemV 160 /XHeight 0 /FontFile2 294 0 R >> endobj 282 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 146 /Widths [ 250 0 0 0 0 0 0 0 333 333 0 0 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 0 0 0 0 0 0 722 667 722 722 667 611 778 778 389 0 778 667 944 722 778 611 0 722 556 667 722 722 1000 722 722 667 0 0 0 0 0 0 500 556 444 556 444 333 500 556 278 0 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 333 ] /Encoding /WinAnsiEncoding /BaseFont /DBPJOA+TimesNewRoman,Bold /FontDescriptor 281 0 R >> endobj 283 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 150 /Widths [ 250 0 408 500 0 0 778 180 333 333 0 0 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 0 0 564 564 0 0 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 0 0 0 469 500 0 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 333 333 444 444 0 500 ] /Encoding /WinAnsiEncoding /BaseFont /DBPLEG+TimesNewRoman /FontDescriptor 284 0 R >> endobj 284 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 656 /Descent -216 /Flags 34 /FontBBox [ -568 -307 2028 1007 ] /FontName /DBPLEG+TimesNewRoman /ItalicAngle 0 /StemV 94 /XHeight 0 /FontFile2 291 0 R >> endobj 285 0 obj << /Filter /FlateDecode /Length 24009 /Length1 39936 >> stream H|U xMWy#$7 sKj&SA$HZM<:hBBZR)ƠJ=:'dJ=C? |ԠSTFgֽaozw]{5Hd4tY`tf+ΆKpZ~|v&Ox^^ ՆP6c¸C?$uF^))veń|w.aFnb]J+ؗ_M)tӼS|T{g{zE y'ˋ ,cԳbJ2?yK;VwbvevD޵Vqsy=9W\bm؈Q7ұ FȄ-'[3XB,2ILŗ{qSj2V/UҐg[TF$iC\mAۧ]X,OlJɑXzF!觌78 CO0Ah8=Gcޤ}V/\ŢFԊE#8!NVF.2^&L9ZهJ9Kpd<&/d+g-\uV/jjTZ9zm֤5kwaD[zZ&ZYZ-loǭ*(3r<=YaZщBxC.wu Ku6{Ӓ _I;Ћ"$OUՂtF|NdSkd퐈F N(R(B:z4bR?zP2jb Em(`*чY+\x8*GO2Q4'~@5W/\ԶsE$)U+7f3; J0;thwow3Й񸌡%SYrHޗL}(Iz RˮGv}N{n>zb:&/讈nP22Թ !?ݞ^@L H>B51(#t6Ӌ]]"#{d0(>ހ= gưV B1 >^gHwFx4gKϫ}4Jֹ"JuﶮV7F=z+fea:[s &u'^Wlwy9u=^R7SYg gFԏDg1Ơ({[lGChmy@=J?q/5,adɋȞa:#ɳOO/נ3?5Pʼn.a*XxT2lli( MF&vFN"xEDk&&`aL6MiIH36I*6mPN{Y˗?~y{93( OZ*а7ӹ~HK& Hp~p;EO^"NRC&Nd9XSqO^%`>*oegIIMa?U"Z[Xj}T93BrDbYDW$Cɇ!Ojo7ad!)sZy K} ۷yEbVh )UvH\rq0GJBU! /V̳%%ݰNT Z\f268>kP|WMʃAy@` V͵>orey7"YаJӘP`C&=|R_@7W?GU6Vޠj};L;J_i1j@rgQ1ʯDv<Ӽ%ipE`0u#)66lJh+3;VqYa~ } oJLh&i'M : [1cs<:w9 f e祝6?a4&{8h,p$jXC}$O<%)4+E RC<;N=MhTc7úerᲭ#UNv w=ug5ir_cH+-EwNZ941M'^)`-IZ=5R(&/>.ZOٚƓ~JAz~8w7R/5c0)Y9}~>1:9mY3|j^]͟I{bnY8AWBTǨǎ>]:ҧ֥绸[O1fg|>g$s|96Vp7zh}t)a"m˘cv).}Qc21 ;5besj>DY/q *o^}cho<}W=I1KVLTF:tοE(S:awҗt_q[8M+]$LOV<yy{>ѳv 6s`G)u}^/FB^1OnD?\"}bفB:jN4hy R@mAP ; 0:~F.X? ^8yp_KE kY&Lk)c83OSFjH3v5ޤLC M ޏ>_~OE  &v6c+?wcFZ?he2t1NܽCK8:p0(qO0:k,]W3N<()S\qK+y:Ƌ*=wĠL_Kٯ뒳>Rrƛ6oBK8%]e{{Fm?s_ŧ ^>'{#3|4!Ρɰ/"T؏(-m]`:ߕϵ^yn/Ūf_v";ʇP i` ^@F8ehn2wiqcE31SSRj,wExnk5t"5v}R;`ic1, )YT*v.}?Hj6^]h JXjцjkXy5B)x FKâN6WCm4asxm-Γv,@;6XE{Ba%+X;;J-rf>ѱU|X&?Q]V,%-mX퍽%බSۥVGmqq ee5:Or]7|j%b׭%Wc+lD,Bu~^? HY.ylMkVxYݬttŬ5D̪d )&O߰Uyه9/v9[ҖS4^UAv ^YAMawiYN:{".;Y`fKZ4D4#`DdX^6O6aeWQƉpz Z`}12j9yU#([TLF!>ihH(C77Օ h:GDDD^yyyyE蓐$r r r rȁȁȁ)B7"444" ҊHHH+a0A0A L"L&SQQQQEDADADADUa0a0@ E " *«gDDDD]uuuuEAAٹV>RRRSH H H HM!5 5 O`86S44I ت:^dm6 a+aQQQQPDDDDAup'!I|C]>\ S>M+"$Eo%%I\9TO=VKYhZ@݅mGeiYu.:wu'3nVQjHH~ 7s7˂C +^`'D3n;$:oPP(>!&1_C=i ɍNQIJЃ 8s f+_ a UPl-&_ w ?OHم0Y52an3<'"kY~/p]|UNdiݸ𛸋jGC*חh Ob}ÁLSv I6is߶7ݑ"a@o)[O)ͮ)˔醼t8FrZwPk,u%:<|Lk|_?Iؿ1YɦǍ 0Hڥe3GyF|2=inG!&y2,cأF.Ґku[*2W\7FEP\WJ'l ߠ&֧:Wu#N`G@3adK2oo|&F#98Ϟ9mũI`Z0w+Oۓ=OŁ4HF25s<)́d8gهce?ò+a=ْ 9%DzX 3'Pӽ͇Ѿ¾}QFGaOSPI?BDeW >z :gIMl[;w*dzM?<=]=Ě춖PyTTGYt`FIDk2&&FnL¼ Bdu5UOWVoASVZ!4-?3vMg C{iN8;j7*vqܤ9ʵ7s<m;{J$U[FV}؏K1GaHœHi&2c1zMN2̈́H%._ skׯ_L4}~j[hp_sr9b\ f&|8ȍ'!;dw>#cc!L G$|&}%,ajGg0TFVkERuFaB2stz}5{[fUOq+gKk="$pb[i h *J1!иwPh27X|' ƹ LXd{>{ơ7s.9/̔y"6_G,$y0!\e6"!݈E3OTk1Qޝ,#hO{^Kn=O]A]d={/7EFUϔ2,S-Y rG6RYq$ ŻۼJ߶=F6B6RtZ6Z@Jð~vϬJO]%TexA"ß|ogHœq]܂R /ϻk75M]e310 [lmpL8%Ak1Fb|D(_"ݧ H$y!.h++\s8À;G?Xzo uГ J$py_45^Su=\F4eoo{7 ?{+;n'暑_M8mes_J 5R;JZYhWs%NYBވ L!AeݶxiUiO%`j(hP FLyt-]2 0B X}5y-mEEEX[Wmrd:牼ǏQRUAW0zv=w{,WD^y7{wx{{E/Ջ]%R& 0#@Gh8LGO.Lu';9i\2938tc}(Q qkPf3bÐ 5Ɗj5tjohl>r<8SWZpGlB D2ʊ0H0\? < |Xf֖uo}px؜_ǜy,}xfU}ο^ylԃETq\7%d'B65<ϼ d0,m6sz2!^( G11>g8)y\'U.rfAy uu|oUY"/ 'qwUR3J3tӆ&ijq.3626pZ~c&D-$a2:4 ?ǪqeϦF|4/]k7ӹGz~ۼw= OeĬ_Ïظ\g;ϙ_~OR#&$吼\n)Is#ʀrJyK0T2ͮHebx6KdVaڐ`(cEyE"lPN h7ڰ!S gF8jvB;4^jyD$8-uؤՔAHhd!~:j@d,8 4a-}tHsȲO*pT.$Z"Jv%U\}()$:uB!.Z?; 2 $Cp'rD@Dψi[,TWL'/6o\]^WN"fN mNݹTDxwǹρuxEQ5BbA_@>6';]-5g?HWs܊ęL(nJ8@ tjU~>;ރ"Hx'B,h63"7=x&ר{3ЎO?DC0)t 3-5mVw͕?_]L,\TTu]աՓwvn O1#w)v9bwR bpF,, %.ʅ@ 䁅b#dAKLA&h u22+@u)cPJCmسwrr|&i:GM<]=xc]MM(6g"}#hIX\/7ٛ&)Dt4IoI:i}o}YfLغΜI~WRmU[mL;v@,+V6Ʃ8dĂit|&/RъF/dW>ymZߟU5$,l&jz+V+rj'7šDM,ѐ[7_y?7Uڋ3 ylnf͝bKӽb[c|V#bΖ *a{]n;U*J՗YbBeJbbHuL)HukG, OT`r 4$K͜fy;#O6o<8|o^`%u-ΩބwMk{'謟m-E*_0~2Rq1 1_>d$AlRcIP:/\<=QT3.Gk"bh}XDfox. epGoinc12rNAP@ja`1Mщ*=zBBBGNJVh#{p9R K*J&GQbAQxH1"дaV'TJ)kr{WrdL3YNp+~42 j|tF d:8-iA*Pf"U(h)RQܺZ+U敖yXgL.c@rx]؈m?j $}aU+,\73>1v=m~Q@iCLKꃍy<(tUفᷱ?u~V!LdZ%n(@~sƜ8qbӑf#Q8pa"X=^=zW CZ*Zj uP:#O0X>SǢ\ o|\6N|>kH3$xɮJ2L\4;T,~<ւ9.lbtu h90zP͡%kӿm(xHڊy>;YK@{+F3#,_;wx6կo^."Z:rgQ^VowlG.4ADČ&oe&r.D\D糦 [bh/_?LhD; A\vG{Ds'3fQX#24;3*-z@-1a`j⺢>33fc cXu`@\E@1%|&J  icLڒ*AE"Bg@8MTii-p* a7k}ov4Z9瞛UPw(^G^sݠ|P])UPtFjH :Q\脳)+n) } :{ ʽGv/?/߃ EKіiZ{ت;>--r+J / pUxJzU7[M=@aYL@E+JRM@m$[Q Jzg~NIlw^yQWncXp\]}ޢwzToЗ7uڢpbqL.!MۯA/Rr:ߨ(fS*j5n56A*MD쫁8~Fl[J`BU )#n)323,,,$piGk3_( ?\{.;p0'9 V%fÍbt?lupQ;>`"c|'vC!S"?$=I&ʧnCJu7tf8zH34̆%i' :rS{( J_$B,}; fy"V{?YNJ~|o5_iNлMt#3B>qG&1؃R)$LKiNwQzcW@{]$pP`H+ǥ3~B?ݕ? bV7$x7)3p>DT##FEx1](x%%CS}2nm񩼀/ҿKWu\!V5x]#6͘6%tX,(kdQ+ωْѡ8ObLj!p4 nxor! $bSr8GC(t;N`:m/ͥJ I(mtI ׻ c?s琞ӻunS= b\MRk JTjwG@Bq?'cF*aY8 $]&NY.a..b@j< W1A+sa~#drZCցhb!g8d4 |:%idONq35UTƱ[y0.cG-,lsE8 t1c9H\& v,&2qf;3| kl]m`{t xWX"Fޅ(l9WCNܛpmiyگmӷ@krB<+l"}ezo 7fnސ>`6T8eG2#9YQedHtR^ubnAC)Y v UԑѠ}mk{J|a2ܣCʼk(S`L*h2Z5{'c^3_\>ruRo{,oGw7qaݳ]]J+ZV v)0$f sqJ)L1\(\! !BSR 3P'4%B(h!tRV`ډ=gΎ~~wy]8~ ێR+2Gգ~z42 * lGD^]gޏ ;jC;8A>)x /aǏߙ̉??b昢VC _޿}ۛzQԊv]ݩ\+|KfٱƵF]ِ Ր֣#~1•|ڻVɌI(jr/ ǿ\>UᆟZ#>|!u~;$wi3gʄQb9jw=V <+xMxVSu r rv.q4$J˄e59 *N$K͎a((S*>-qZmU)UEFNdHi/anE ֹ`n+{=Ξe Ć7)3ϑ.,]T=4ap'wV9yawZ4әi (tB-gM'OT=u3 Yc{oSܖ VN+`32c{77kQw{Hީp^05ye⼋r)WSS"ɒWS_:>4LL LmWќo>V\)ID ,8JalOGF92 83:I p,f() +mX"8VUG7@QMP:|7ߣ Cˡ~Caai04mad`̮/RdW׽6Ά:9sAFn0ݡq~e%E1G%Cvi NV`S2`kx1:ۅN̽.4znDK. 2#9npYƦ΋ӋjǍc3mGO>ocJ]zw7t~,}јlJ|'. ceBm#[](Ψp#DGD(qP mC)@K]{̾ ]*,-H G-Ww<('J149Ю<9+ #1th +r 9@P{ ,]>7AFߋ 9yPuP4H`pN9l#b``;PBd ( >(6opTWpAh%r$@^hӶ0,-vl!i`MH )n>;sݷ?]Ӳےڎdccׇ푟>ZkMMm;-MD7YnRUڒ]c[2iR<j;nl!V3˽Ց5Lſ&r;.=T%* A/2%poŕrLTIho,٨V'q0EKfZrYp=TqZQZf84xݶ ?TsK{aץc];ٓx~;t=wkSU r^kw; +K}'iֿԿ4N]5YO-SkZ'˯sڨ: ÿ)9+`J/Y1ce齄 >`+e>*sXx T!U*dM Gdw@܆d:80NNX:GV5.: .2={ں(ӄ}i׿zf[^9Pġs7[s9j-Rl֝Ul5PcZzq6=˂^Y\mCƒDNPM!Yo$y#Q t-hp27y6M&fB$_0;G>6l?BFAX^YT]cXp>83|w 7q$Nxws cޑ0P3~mr{ءMg/Lsި Z$)?A J!%M꒰ eT%L>Y^EEGkɐ,'KTU#:D$ITOř-ls.MRMApN65?Р[qH:NP$w\;T1IK>:5'.ZG>z0DGJrUqBs% PK%zԻ7śq8P<8E'ObcC?H}< |1T`чv>߾'N_|6p!mߴY3_$yCtɈUUàLBIL♫:j>P5)E#JE)ΊX$p'khVC+hp oi~Rab,O@'vMy3H3 RfrS9c!Lr}5PWUN!gHɁAz;/Wu˾{I~噒?*uZn kط71]>@UV׾@ ooao&;~wgo; @Hl!.Mh5u@TҎ* dTe%奄mںIcn-*5" @/G:ߣEy8Jq)@"6eOR5تmZYPX:ʚT&g)i آX-EUyYV|ϘoM3[Eɜ 2W92c LQ'nvl, -|Q(H~ ]J2SrS죎R)Fu M[wJLn7 6D>Cw$ǫ&v4uad専$. "E);!I&y$?߇[}c>|݇|+qw17r mn;iL+P Ю1Sxz#+l)5F@ӷuGwz7.9V[|3%  qdyu`eqM'n %DRM(U|^d,.ʏq~qudJ+8MQ']4 1Ywrở@V|'g+(7SSb#{;$|+AG()⋄"ZU $KLd9 xXDAfe 'X, HXoŖ0jgPWM_oдd:^4li:ПM`_ͤ[AD~{Ɵɹqiqt'DhGAr\"  `0b{rݷzoJ3F}] 0~o-f":x=J1_,/%\8R-5D#}wcQ F\|1 |J$ٱ8o7?$B9TNB9NP5M2 ] ;DmY^q• { + ^F (.u O2n%)Nωn'QGy,VIv=,sIsfxb x[=MjP|(dxsS (:CnfzLB}nڊخ Ty~B`}傦Ȍ܅M.gVzt ]kqڙ?MO {a>u֊M"7!-5)&_7 ZlV^XTc0&A3hl +6ժiQxMEՙ1㡇 K#+˗ tGR׻1NҚ!4R~]/`0Ş]^{v֮xH*W`OԱ#4U=5&91kU'Il5rD7")Yk3MjrGoK[.EclY 8-]}g8"1aMd1XmS4fm7HMQ^ M%ޭ?y'b/lwlsp\rp #Q?ȯŇQv&0=0.4 mLՠjleGC'̩x ә^6:ݻ;wwvlkGb;vRih[Iv  UHQi6Z CkM@RE k2TV*PE!R8|9 Nr|?CxI9u[.T g,BGscw䠜e g2rT1 !C}5q+/&&_?Rτ'5eKmؚ~4Hz44jӨa]@ dOoe|{Q+Cnn[z#*`@+@낾mp6[vOik43f>V O٨Bfqx7_󆄌#n bsOx&id\8wX`GB!#<:牀\ ^L-44*Lm';(gMҳ}_׮ɵ>m)[y5@4oӒH!t{SAF3 Q9lMq0Gn@\qy`YT}>&6hS%QzUՠڸÐ(6vuQ12J˰e1ZuT^/Nt˲kX' gJ(**$H@OpI1 KL>wg&`l׶C[C8Tơ2'yJDж- ReμnDlu&(葍% r#3$ LHPֱb^Ne,w8&S??HvJDʈ$"4LMK,H-42z&m]h]VvhF;dT:IOQAI vMn9ifs]t,JK"ij}iwH Z<=o{2X]6 '*S+}2t?$"-ʢ!MPSI"'Wce NPGqE"OqL2꠷c}vtyَ-GDuJ.p z 0#ē4+p҅{1uI^Rh#I+9v ~J|;>2!G~LN+ok&t( e'9J:Y&vf%.[$TNOO%Z)>GͦgE|" !TUuykjIB,I|\\[),;[?.Bo!\'㨈:.]\vPE;0Һ|~8?ԕuqS-ᣘViKx?=1\!%BwT.qW"{!{aL#FGFF=H=`$DWr2 DOxx~ ϢY4LqcWin1CHYt1ixqyenpx@!WVgdo9c߲2}5 s  վ UFtHUew_lSUǿ׾x{mٺ&A'趖QY){ dL`dV1h(%B2h"1$!0a?w_?m9{F[l-m)J既TƔud9ܚK+ħux;HX";D:m(`=Vܿx{ږzc]8[;+t^z5ν_Hg} %Fe*Z9l*Yl]v&  =G\)7E7NX`c(٣5MZ4ISr+KYmAMDFgDQTZF=YNN)KW964-4xx)ON7J7znL?ݦM/1܎|s1޲>\C Z_: m6|Y*`5 eSuA6kم`W-_|%P;+4{Mgh/0dW[UbD.ʼWdv `%"]$Y%s8e&16>\ &:_CVPz[CYE>Z$c?OwvvuL^u #btڎQn6ȣ0Ts= ڬL#<َOPnUS낓'^6uw(=7˥dLdYhgތ!dnN0pO0#1B0euuţ8Ly89j$n;x~<\,k ` B:8uq)֫sO\݀,-ׁO#{vf|DG߷@bd:Oml' C5 UP`>QX 0P7D6e@=a=H67ͤ j2_-?si L%60c"]TrOzLųJJfcnr^&_ ^ K746=߼+VZfm{uNM^>t[٨E фlחOtR546{5RP ȱ#G{԰`>'LIWF6T#-YN!,պ,M,.ItIſ(ѳ<{XBn#5~rN=^rV ۩ -'y#=Zihp4ܢm,@Canx'azbS@}Q{ J3∅s% z4lP#w,^KZˑ|6_RKuy|#1BS endstream endobj 286 0 obj << /Filter /FlateDecode /Length 21889 /Length1 34120 >> stream H\V tW9$B^7"#E㕄3 R%"Hxբ6TxZeG0CQUxP5}0g^ |$ _ɫFkVsr=bdom ],kG 0N ^ hq`;HO!c/f>ok?[ տ=ޱfO-4ſ/_?3pZPS}7p qZb&P!":i Ȋ\?3 ]E7Ɂp9bQCs0o eM.\h$ x.UlS<WdԖ~:'HLϤn-62|o9J2 b`>%c*->N\]Wdm$739P`j&vԅabW0RX>1vn|dn LEaa+8__Oi'%WJ3sIˀ\QWI۰ifa8.1vXCOUob gB>׆qυ>=LA/7UZ?>y^zՀ){.e}} Tæ kuB>|%Z~t g2`M*QT) ӫ0SXwvqv|M_SLK_z| M=J]FĹB_HiֶG]D;H?k{ڏV}rAm3^_KL?eR/1g/.W߇|+hGE7\n y=*=n'3Ԩiq$v 1e$[Eu {LXHrK'I-9]^Ӽ4NmŽLA#ʄ|mȻ0,)&UǞBtsɧ"!z\X})ǡS>3N5s[_:2Ϣ> GUFj7BW-ڇ^`Lb_CR=J>ERd@+mf_pOz} (jc[Wu.qJ-1kux[b>k~Bs#(M>TNUr[ zzLOi?G) \iZkiF]Ot@ytArݮl<7ˋiRB=ޑi2S^gd5(3pƝԠ)t:.)1]_ Z?Zy}k>6rEX&r.Ym!jw) 1Ԧ&SoZ0~:C]M.W /|DGMط׸t}O ?x\؉> Dz;i 4О@L uA\C[7Ԅ1?"B[<'^jA6 |d>~A~#d4*0.o ~}]#䊁[n P o5h+-1t]Ͱ9m"ՐuMf Ȋh:5g)NiOo2֤;y~[<M}֑PK?fZo>*#lo2yRHQѬZ,Yʴ4 j(ө(o,* 22-"e|Rv9ɘ4RX]@)E*@,TidzFtpfdb0O)HWʠ*<{x*#Pqi*eo|e"ٕWI -f%dG}z-6 Ƚ' $,Y*?g pvj^AT!]'| >KYw iI67qGb/;9# 0rqMKC4rhUM*bm Tk`kV$FġVAUh6!ZUck5F};?>wwFz:[%2#HdLȈ8"NS".UZ4,P( o>eV6(9 4`( [^_F` vB< B<H!^o5׹F[ i9V#=to]ۀ(ay!*m-a4@FABUH'zS+pS+II?֋2x`=KE7udtv, k`Df3Gt =N-c">WN;_$:V[+ t3Luf`o&0ygƭY6GCc4jИA2MTEck%)k|OH{ )\ʇ%UBve,M&2yUGWTMmfi*1K(D݇܆GȐLwhJdf11M{1~9kZGPYw˺446ْ`-iq/JͶ3ڇ`Z*}.ELܾwMOO81!ѵ47y3ݽpXmVٌq;P4 _G4흶M@fnhf3-DPIQԡHl(Q7uq@8ΆM@}<4XC3sT55\u LŎ*#a i , F5-I$Z侔,nKt.N.I I鎁4lQ:gg3UΠs'<5bi<%PS(ZfsǬ|nĪ@l ) 3HDg `z2h* Hcw\Y76;x\2iA$P4ST$`dM LMaT%)"e4H=Hm"ZނbU}2Z 8Э0{ѿP6 'r/~dn# IaFv lw~*ph)4*~YvTp44 _ty;BWmWiE0-# d] ɒ e+.BtvtϹ {}UTjP OPYN<~~]o%+ PHv8}>wx~jg.qp(J\F¼xG(2Q|_Q(,)>ޢ9OPT("Y=Hz=ؿ:%_o&Ւ*lI%RF7,U1lM wõp{:ZUĒ Ah0, 1OLa09x:u;;/mũ;?x// k M\Z .b vBp2,\m#QQ}d+ a9+Al8lW+pn}8vIq7ml YuO_?]+f/G+7y \d=T/d˲nrZc@׵/-+YaSE?!\ Vh,B|Hhdw$8{"+ l~"(3<%,'"%"еh[C '+ q=x MΆh*"a}45h~n6 p+k|h| wl _O:N fYd$ Q!tyM `Sv`P!FmX'bhj"XZ.fہVڇOR|b983Y,A%LJvoBr_WNIORPkͳF 뺅[MS^fv/8Vd%W"gNBl1G.O5G z S"v6%x_?r'2q)<*8Yţ"I@D(84zE$VS,zn81 TϫT 0IVa4E#ez>QW*oey@)e&g& g%gawȁdl~'L%d̈́jO4f4)lcvj0QNc*me3C4V3aq$-CZ?/4iu|6\nnvZ= Cߩ&a:i9 "<81CFm%CyQ[3iF)p?hGIa]쩼XRՋM셷sot^/ѱriv5$L!3)Jv5z)J5 OAOȨ- 5v4g /m=[WinX8;ڹ+Vײ=̒v廡ʷl=9|סKNoA x +NލH"i_'0Q3z׫` f@,󨟢xai}M%p?YL';T-V#Ut-7WH1+`F ("E.n@9|l:7Ujd.n'jD+;LE IY(/EcJ^JQLhs@6]|QKBtėE@.pDm6|ZJWѫLɔVI%.׎A.(3_u# (C1nшxBgLv(4iSX?g[N#BtdLbD1vɌ֡q$ҩh;Gr>߃TrקoFF݋4$nRij48yF"kj6R: Ҏ;P;WZib8g,sibgs * vvY}KG!dAmg+b7gɍF: <$&[FH rZ0 M\`9 [u"Vc Z22C 4Hȟf2.W) M|~-xޓ^ɻbu> AIhd0<{4шxބ`GnF:F9zY#C&:^ܞ|@488Px) Ƚ#2uJzpOXu-u̬W9Go[D:=*IJ[3U$& G1ů,.,ٙb&63pfK&Ll),Hwru(0G: wnDxpJ݈ЉCDcr}JY) .D&? ֿ~s3 i[;}ֿ1r:\~Grk?u.Ȑ&$dv4\Fc ?x6lbzu:wVC2ctMZO,|΄ql)q?(M8 ƍH{SuCJإ L^^\ԷV|||i R/:o-RM} b()(F%JRTeMlȘMV"& I! ~$ r<(3Y,q<[gH-+JjlO %vr4$~0aQz$S[=-^ۃt@(y0WRwALN"\fݽ>*)ra`w:ƹ:aPg>;]ƍQd GjP#GE#b(Ix*Vqc4}W7x=lG :o{0.c3TW$1M՛E(RA4Iw3/7}+*+F}KΡC[_q\V\7?l[ijr kWrN2ܫs #abɹldl;̊e]'oeb $,^3r\/4 QG[҄99A8{ ul/AQ?al{=t!:G2bӸ^P 6s!;_pΜxr=3:!+'3:UzIj+aILdNQ!7W=d($1Qp7T4dk%)n12O^V NtnFF9fv2$?D\9Ut}PYQIa~bxK9-NPas/IﴀEdP*,f KBVlM}wQ7$;OG1?kaQv]`"K0D־ #a g4R聶brsj0pg8s=z"U^պ_H]VElZ?EbVsRm&J >#1 KR@SMxU-X6%J!ԨI 즑 uz mYNuB%>_kT]* U6y:> >ӵ]-@Q]g{.˲첰<Q75BZVgb:I;>b|Dm*MRv t46DEgXtFI[ij}Į:I..j:]Ù;;s^mᬌ뼞X{TU[*5QZpiʌbR%>8[<wϙtCq-nkSTo|*%{Lf<13լr~OZhs vm(wtg8ypj9w !ٗB&(^IR+*/8[*.Q]M嶢\Bajz"hfЕ& iS3uOp}Iyn"wJs0& iKKnBwKfI(f8 &HH=]غ"+AY;)dKL)A|).XvUČ8zx?^2>%,#$t~^ݿg~{Pm~I5hQIU*p D)ɣc.xn,wMߦdBb>7 'iF`HRrs'Z (8;|wM_dW.HݳqgSLXWY:4anb\IW%m~-ˮhVQ4KEjG.VH4kCp'miԝ[j,%f1rF>Vr\~}u$xw[w %0t'bqںhߟ ;|ң'1)g\Bn;k֢h;G+hы#|"tͫ=dƇ|!DV\n#q7 ls䇃{тjqijy=kÑ;Y3NP`gGJE`ח>|S\.Җ9Z*lvSYۙ>kt񐓔@68+՘u˜:]Y{RNĜٖ:&[O 9>81f {  -.puAn{s=*$ANCb/9$@#1M He7 ̦hgjA % <@yC^8;.#B!9 G#ŗ#UP$y$5ebZCnq`N^z˅|u|c&h2SKk,?~fś5nǶ,5ulէ^_gxiUE˪>n~"zsWr3ؘJE/~/NV e]zVee?"t43igU L"dP ,q>Օ,HEa#ȚLrCfD2hU*xLcP:l13` "gV M1 9B8Zrx/mW^$JG([|C1Lxk72+L |S391Sf LNd9GāR?9$j.KRuHT Hmb&#6.eR5@\AT0@em=¥ E2MFt>9s"nq"(Sjk2&Rؾ_[AVYVYW8-Kн]t40C k_z[D mn *RmfnH[ƙm{ۃ{q=<< (xJsE0@&>p"8&VT%3ql'v1I cjS4UH$)ԈG;t`v닿q?2s[BgaM }Ct~ψu`XΧf 2be%!_ NHLpW 4C+tuQ>'S"+& M3]D²a;ܥǦ׹SW\0Qy g"c.r0fKd6ņ \17J+Mܔ ?QB*g&a!$yR&pq $2q0*y{Q;z\V_Xͽ>;ެw}Ƃuxzsou. X gXX;~ModdZCJH 9L]Er73PUSA ėc#ND ',9:+N'L1h¨ =] I{e߈9p ufF\={raxԜv8nrI.'"i#yΰ5norcTM(9f<%kphM`Bg*p Pu@3R|DWݱpeEEt}p075˶~f5iG>deT1OZ*ݕM>n8=3Łry%Cwk( m@y-a.#eM4 @tDL/I d)tJy#aZXs084ʳ?\NjXcqrQv#d␡HY)ؕ `}X^m> kkjx.}_֍tJ;^Fz<κnxVi&`3sׅ2Ȱ:\7 E9= Eva7 [y,g0=}e'&McZŬT*iX1nsF ܔqQq& q1 isº0! gfXQ`@ү x1]8|d,eK>IL[|d42CHŠK?L54xFN:FSna KIG9w6q>}8#Iľ1&Дb*2Hώo-A%Tm&$IB4i[qsHY5Mw>=OՔUvU۽tw6SQŷM7|̸|/_hvk*nj"G%Wz2c=m%mn6iџV\vI^>ydm纞52YG?"\|KO&Q;]..]bZ 1UN&D`ct*xgh^S1x~:oUSdϯȩ* &.<5 #F=D>.h.X aɫ\.\$ ᑐ9t &K-DKHPŚx/>QO'ur$ĄVȂ.\r % ''} wKaRUW'UZ2.8R%L(Oe#kL]@̬8W>Yljp39&LGUUkiA"eޓ Vk%\1˧7:?;rՊQ-\zʟN@7xV[m>N؏W*ы7P{rŻS&oyk><b?$+lrj}TY8Wk)Vz֬ML("',Ew]pH"6YgOŻ!z}bY )ve>ps] 8I/}5VzaBix`EЄa?K#r,C0+,Z~-ӛwrzj^Lf27q}n-o#jm솂P]AQg}}vo?noΰp_UAƈ(~oC0KF 5jf4ZSkCDmLmuVh5cRStL[FqTĶ{{7{>}x?Y.U& X6YAtX?l.Amи6'*&s9S1_3!B`]w 4ح DN*'"K\NTY'5Tr]4F1d` KD$w Yrc区X [ҭ)&ps - c^2_(@ڃku+}k[ﭡ-ddֹc'\#~wm S`ttkHp yo\/ 'PR-5եZ*6 {y4UK%6wX|MNP5,p>ʩX\uiÿ|Y]@!dYu6 }\g"$xBj©Z +lO櫑Q #Y0> Y#b[[.ٴ$dtgOe3⚮5E`;oZc':%0[:Ql"oRad̴&M-ScիOUI ;H䪘73,QUP2 h*TgׇW%2{҇twqobݟ(WN."X}*6l-c?O-^ڊ_ٞTaQ8o7cl&. {B 8LO))yDwK/E(ƺXFJ^'9zP 8< (lL0lS]νG-פaZ!KߛH/># f{SuSa÷ivC(Tj:η>$HIk͒P K]>_ȇ+Q6US>Fq*(M^ʤ,/yTyb\Ľ)p\$ | ތ#{~ ۰]xss ͆sg:bh(erJG1ًެ;Ԍ7J V>b1?o|#W9[ɻ◕p%n/_ ~.?L>/ڃz"dꞈ&z4mzRȴ"VYLjBg3.[X7n$UP鉗 tG&#OG#NaÁecX) f~c>q[ T`sSo&_wSLGPC~Ar(\i& Z y W)yڕQK%՘:dGQ~)~eAƣ^vbjq-wZ*ꬄ7 E%,rGc{0(@p!%wFѐ=9,81z SzI)5;ъP8\SBh_Y &˜v:ȉΚQү`ggCj=7Q"xqowX;? &=cIAYVe!ZQrj"ԅ$,ͿE=ۃaϒK cф 'l"^P$2Դ.ӥӯٵWM)V==PKF؉oZ;:. ;)EKr(7 2'.yȫɆ"C@ 8gP(GU2\dP2ʞ\<*̝̣Y X@EKu73$u Ku@ nH4@#}Jzw9r!r!r!4ԃ`CPW7HA4@W㚌+*E|,^Kk!/h\pۚv[}h.C(NuX:܀&]v$e*QE2H-$ t_YK\iw m,-.Q ťM; "Ks,-h,-~1KD׸q^`mUmiڸoF#"jj^_n;7knjovow endstream endobj 287 0 obj << /Filter /FlateDecode /Length 25212 /Length1 40476 >> stream H\T xMWyu#i!C1QJ"JrWcg}J1 0$F;mu:^ՉVUfOuu/csk^ X1zNX:my`P3sf雼oI'fL7}HL^Z)`Q*<]ʂvmyݵ|V#ED VY衃\,Ty[l? Oy3 ׁ-ռ[9sϖwFWC,q.ĪV3u-oغ >*> (!-, X+2i,@Vz`; ; p ( T~VK.1jiF.%苑Y.kz;qT~hEP`}}f}؈͸H[B:GYĚ[15r"3p`.砐:){1)e{aֻ Q>4L8+j@Q^7 3>pzjA, SF:)w3b y8K-*p-UK^>Fzag-A}e Ekx)zhzVtb؋e%a.Wݱ=dIk؊)+i&DЗ"CL+rڣ=\s(jmjG(~ITML t!bx^ܔr<2婙jL[iwJ!a1g۸hyBQk&4^dZ@鷴PG9KWkݢ;L6Q8D&CsE41ߊhE&>r̗r鐼TTm[۫ZlBrw^ÿ¿_g]F{>8F3q|ޛ# g(h$#3tb$_}?g>s̹#Lωb1]ED(2Le&'b9KΓ)ȿ+{BUgE%$5LMR6լ-Vn[f=>ƞch_k?l8z\,ֈ*V4FI(ق;Ub>ՉZmHТ{1PfT7[z;smN M[8D쩒i|./]mT(E K.84r 4ӝUǣ- c)~]W~%x^||WTJxoE7–hkOe':O]IjQx&MqѤBqAoejro|,tk1i.uJ YīK<ݪer!Oiv90Df$;g$85LPAe|kDmG֚x?7*A)<[q7Z즥|s.H-K4iYV"Olz|x7L4O*O݌rq$zGZ+KzދȱvY)4qkؓ҇ IA׷z٣{JrRbLjtq蝟xSǸؘk6MV!v $;,n&M` XxMEY꘺;?ΚSO3fMaPJ4t!iBՙFnA~]``S֝f֜RӝjB3ДdԆ1Ɯmxk):v8#i Lcr\̎G~JIdPMRP0-ôejRM>[U"S2'?m4_%;oZnGsƔ遥Ϸ\7zxYn_^ 9X2i)W_ HSu1(MuLsK?j]BSuspG#ߓ٩6 yMcIIl{m33\P=},220B3q\S|X|b+OlEi[0n|s_bͤ$311" >S1-<^7RÇ1'@tL慹(uo?WkpUW^}νP@PPB BB"ry@B^TJ [ie#\(L &$&8\ i#u 1T->ȌmK;3rs`k^{I'(14qM[(䚰[^Hn&~{c~ Ήj?N}^Aj^~$yNL6oI7ɩSh#IW%=ɐʥ,4,ԫZ|~Dhɹф))wةž½$̌.ͼ>[DzwC9S%%)ɳT_:Q eQ)[$U.Gg\$H$7597RYbW&'FNs siOJVWtSWҬv+;!GrܟR*5~g$n>{FWBw\藩ʽ|1.ޑY(6ob-s>s<&<ءQfףLيbm~# W<vX~@ 0J?JS/ҋ Y7Fyͱ5~eӭpl̋%O;lƒyF6@JWI1;`OU; f2@b}CZ*MFXdL?(J͈ pc]*ZǁE@zbc /a`-ۃ3ZQ}m2$sLrTdGjo`2P2 XP`@0 s5d"f86e| 66ɘupPsfX/;6)e|sk:9b':~AAĖ|`3z!U!+8f>/k'8ƭ5SAbe1^M0f9oCZ!lc433z6wb?޵Ep ׮1[&O%]3&]f,^hN3#&&=ݶ|&|Z&2'0pV/Z|`]֋ A$Z?y 7QM;EG-Lax.U0x|8sXrٍW/sW15 l]PxGc#O4gI lwվtˍOO{ⳏ7.,wŽ9?ryq#z#Rz|:b۶r#V` ,{SN-}:ڽK=ޣf6S7ҏ=Z$eu;rVAv2|Ua8K'r/{DcwQ5Uo}/S1l?/uSYgSIYV^:{c@hghhW+}#2.o9^?o% цǫ}4@쏷cZZ,YH8Cu0Y8sjѯmA!C|U"7U"琌}hz6!F>j0|X.>[89v {bEcS5t&$VLݝ?Lm;-o>/ %Za iS0.q<ÿ~F/4=xR~Jʁ%8 gkzqSb ="(OEJYhxbY$(\oK_XBb*W72hGzg~q2?ĸ 2p}9bK_.<6y|)r7{akN'i*}Hs+_&V2w$u~Ns%qOОƉ _o  c>&2Cw~Ń纝>fu0Cj`x1D1 O3= =#c,<ɞ]LC5xn&rt7N |Cx^LfK _!W@u ,` y>L~@yN=`|!b.yNqt#+N Tq<ՍSJ -ʊt*@."@`E$zCx4Q)@& P@8 c D& Pzδo7M{[Ӿ6vYH(Uymz[orY(\Hz%5ݒ:9'fڟ8>- dJ4!O$8~)%^F2 2̣@桓yl% 50q]$`;;%͜g+S|͜X}e=^{Dհ +{"iA Mғh0?9 j{&#+v6<4c5>^Y3cбZXj92xo$r˶H^B\W\ЌiRrXM=ESN*hh u!ed4?o˿_!ꎺ@XDwdfjE@eBۘ"pUht*ěFZX\eV/V1q Cu]Jf f,w3^_Bg"Uv`ݞ6 XQ̺&?d/=qƍ^+37K knzpk>Lryω6L0>[mns*VٗBbcs^pV/d3#bsC8}`0p} 6xJmu:G`0@v:ƐN<sĝ9$ѨX?B~na]gE=nU/ ^eT](uơ5Aj\An}.kFEM E?DQ&2ި7^Uyۿݻ۽8⡞" uMTz!11FbSgզX B<mR4MI1q2[^lNno{}}RvϨW|XѱTX=_hDZvܭ=Z{ЖMeL NPKm*Vlkdo1x@ k3g Ql4qg8;[M0viDITCĉQif=  X'*[1ظ9{NVEsl{5[:iZ&1Fz6x}fMtt|rgsI43d5({_[ hY񗇉wj&X]Vn]-` Sy7 VdX &nS9DSq,e]E9ʄy $gAǁG]Gφ֤<7B-"9R">EQЕ[`pa ˹R`6e j'dR{5nMKj7jwMiZ 7,];ǣ5 F<^WB5bB0O("nv9DQFuZǢc7QC Uf=p +fEp`úsqTBEz6 )ed"^f7AnejHbAs.}}zpi)4Iu=d wFQɠ 9)@f)YDM\Q"$>qM (&aEZ؀^>m[,NJI骏uBNqW|/EK^ڙ 4q,L\G.8p=fHvʘ9̓8K=J],t<bU:gLfcRgkbqeUrbiFbJY5cdCkJZR!6_:TttpEQpʒ#7rxج,fR0+cF:k{6~jȇz4JF0眬xFѯ0' Ix|҈HE`+!d=i 4'ACV2NR~&7쏊J5'd&eEqGB@$.ȇB܃x}QHR&_qA|V'ѕū.5t{ىc$Nܢ]d%<{6zҵNRV^7+"(bD+,Q:@*L6h qA 8iw?9DžȩYܖFtʼnD"}8n5, B7[7pL`"h Igf_눈UK^ʜ贴b݄/dUTKDز0"qzİJ<;x(;eǤ#z^Wvў]^3_iZ:7w+ǩ&NR\&rz;s>eɆ<$/IXu80Hx$PBQR@My&<@dҪV:Jrv7ȸso>2|}i^Xe>E)-a8 Ը=k˞ya&yLuG+6Sߴ67ݳdA::7$1kG"1x)>pBύʃ;<|͔Z8[Y-E$jF2&̖FwMj4 4/ƻlde}?q6;a_`/ =pQ186;ѢG uq+ﯧ?;􇝟{,N*{nD6l FFfl -KB`^dYވ6Fj25X6?_LprKV}lQ,Xn~ֲML[M3A!0c8xx(VJM,c0n !^Ԙo*h%*toKaXphooD%&>4ju%v` x7-BT^T4pDLTH=J9#{Q cA8F6'3m;ژա[t8} B3 )Lc n&1%x f_)':$R9., _$_TK"ț㋣<h9.:g{qI>8.zQ/]>IDVhlZ1LqElr\FJ@Bh)* h`!H9 Rs"AyI8i$jr &aVPJқkjwbJěfՌldB$Vǟĺ 7\8+Pm+ Rh-pϡkcθ hq4/lvFq4dd GoroѳVjxrQTdGwOC:Op/3PFy֍KxƪPͦk)ymm nČ,"jRbkz[]^j Y t%ܴx͙= i:H|I ep_OxEl,< 7Zܛx9E(KHR6U& JpA;zCb ŵ,%2odOF =Wa.F|k<> aX)! PȒJu@cSDT%$)QԆtYp`F_S+E 8L֙Y-t|CˬYZP C\ R0J% r:e[.;NP~EC0 QՓ| jƏ'*>u=I B1 oѽubz;ymO ҿFМh9xjӎ D!CaKGd6gE*ag?|qB6' lF)HT+'lo7: 4Mt t=G&I mciMYCF [ŭRhQ8HvMd}s>6I%IVTUdIyg# O9tiO1} 'd@Vvb;kAg],6L ̣dN@8>4;!UY*!BpF Y5b4C^0f>~n=0frg0wȉ u\2pujN+n.:'kr"<Ϸ\G/=sdEobUS]-M]gs׎\'%$qHʣP BY,J 51Qh[&6hBJeQ [Jģh712Zc;,>ڲ}+h{ޯ>>?T9҃HwɃ*g:ǁGd"W3%MUe/1fvCf⭡F/lԋQ+F~y4UH$Յх2+bea]-ӎ*okY:,;7 d,HPLCWd`(BT2&Ǚ/S{YY~a3t zoQܲ9yg|]U-JI%pL6issV9߭X|Az^٨>ﶷK;N/ћCK~퐾;O#ڑ@,PGKc%ch̡dR:rPZ6ā=&kc jTdQuR+gL(IMfbCnat Qoy7$Z_>|nɝGջuܤa琟vgvdQUtR5~~0 oɒ#wȘEApxY?Ƭ0{AKvKUxx`*crb8lA?Ų! Ik~SА=2=5d1]m=(Eb@)B@-8Hi3(_D2VmMyHMͻ L5`vN/+Eρ{wimۘ꼋 So itНSMj3e&#m6 s4V&i8g%J,(~4mkb)!Q6 UЄ0C?B|,`۔!0!5 Ya 2oXWl@fi[<M0(> Er(,bAYt"gG‹ssp^2{= mCS Tx66=Am4AJhvvn`I8M@!eݖ| _& LVx>~I{EMzhG2A_E>pA{SmNH26L-BҪ4-Ѵ3hMN~1Q)F%pChmE[%b}U45e61|DI~9ỤUrsf7Jnja}L?c\/1+EsP ޣiI-MUaZ +-[8[hY%4VGLC4Ɩ2PPȮ,Z}ij"-fQvxuȝvc&Wt?p.NzfDf"I^G}JX:i=máPFS!/$riDā]qtULvHI//ICuF?# ޾hwnT;#!V2jVn| 荻*$L6lQvy0UF~X2zntc|8'pт#J~Q8h#`~`")î jۮ O.F]EdnLJXDd#Z]2wAhu2w o3 wz hd ԩC4/ 23:yL p]Z++-۲`ˇ9lC Le+PH@!Ӂ&PQC@hd&5f $ML4(f:G!БvW=ԇ'MVNi("nƾTLŔiA@BuIn"J%M\gIm:E\S) \YEs~W4o93EN疞DCPQ`b5#~0)$ t:Q=~d9c:}\"i:>ws@;o܎keƌM4Jidil޲' 0:3 Q_&zCEb(+]UY#BC 1jCWEv4~f:΍ YEL4<eS\Kb*"aF. DS8* r J : ؂[y(u ܒ%K2,5RV ^H2,D/6p6@7k c3T>!u[E7e` Py2㘵;l[˲5I2\\6Θ;mp7xք#m .st^6>wyw 54C^t5>fdž>939ݓt&=>|kB5-Zjqx~.nw۷ۿh)y*FĬK#P6kKt 3tAw`G@͂DpWb]4Z՛؇ [ҭ ^U%2N!&EJ$ OfM";c[ysCG9׉}?oe--:P pb:O1bljlZϦfRjضPMꗍOΛmmvt0v^AnGPb=U+֩,qx!d%AvNA`-Z,(_q5J\I*Fy1#p8h79-;Xi'nsF$SkW~$ OEW/ږO&(9=s|b[뇃m~ehJzXGe|џ? hV՘ rf tMf *b*#TagC+3sizz}~!z;&?BTgޅD;9?:r gHXO8 ϢjLGy;~Zr2^qZl!H'zakֻ}T':l%p0sUD@ݜ\%"lgk 6\K\N Xxڂb9e'걫"<}ӭٷnWA3uܾM^m0 (?WQ{2U/wraYDrXn;cʣAD}6C¢QѢ%ݤU"08"dtaG5>¾lWf:n??;WE{$NRjHpv:8ޯuqw #OUt9pQwVQ}@2v-fb;5:.4%eBE)F%/Je_W(O5ܼpM{\wEwߛu7?́y@_p^!QxP`RD}"2RB*:# pT(-Rx\q;H0_lG>#Yn>^E9T׊XAiFRrrN5 9-dV@-`2EXP)KނJ>Ñ";@APHMUӽ:>*`pđ9!{ _58]S Yetd,E4~fv>qv{'qO5 o.0ѽ8 J'(DYn|lRngc҂aRRaʇi3@WQ8>h? 'h/iC]Z/@A,D2`.,`1iupAţ@e.Zx,ᶣ_2Bf ;l`AP`9~__.ka Vmr=燻҃+Zw7GӾ˞REJ^xbdvqmms~̇c:r;Ӯ #>0Nnnmj$"]~ ݩ8]a1۴9lrfcMb\< +b;չ  c-GV$ETxA+v+ޱM9fc:P>q*XuW웣)}VN[:)uyVi{f Ŧ;3'uS8Q1JPI ҷFIJhU*̬fzg"H~JKySSfmiM&}d:Oӧ*~9]R#Xd[DDM4Ȁ¾%4McJ 75Jz<Γҥ2  ^=EXtk`έGW[2>.sN; 6"\UQLDz"\8-( |9 cFAZ.2VH+FXJG.W. FF#zĴXTiS mVAVsx\3| ֔DP\k!q$kp=X`ĹX!fZ5hVe*L9f{Jyj Vܴ_lm{kd?;w_en*s?{.;״l#ꥡ|Nrq'U g i—NM ={.pSƩ@%WN禪 FCy]ǹ1q w?)2\qRk{Mk='u~L`O.7%ǜ" y;\|e7mkGwGqٙ鞙ctV{I+KXY . (I@dH&aQ` l|P18$tCHT1 %$*JB$Ea"m#;=3ӽ}>NMi2\SOzի}r'x([sJ]Xq| !Q.b4PЅ]$d_j\Vzq jF*!Jse;d3Gx䓭k>SsֺsfT똼n]eCl. mi/˩yjozYd%!z9f&6&IiRۤ66MŴX4 >nd maVHOcgSя[("  ^. (;_/h<~>/h-Lt<* `=Il:$%M9rHN .8[._?D֜E`vX. ۣrR]YNuqZiqc͹DDz4'@\%&:ҔnV&;cf7S c`4n.Bj0 ѸA|gA T28LHY(OϱFqOľ}Kie~ғy,+_/SULAqϛK2>kcugYRѭ<׺):#ZG\e?۱a_|e5;yE6V4,q?󁍴p_q yQ0b-Gp sȣӄƀ*9YC(U3(=M^'n;7DA9!v#(H'zZ2rvu6 LTå`(+J$"^;hQP {pKD~SD<NMS:yxiqe?;|Хa((Dty-5 Mw2aQ#Æ7 4yËGػ,#@N;Mh2mx.(f! ;UM%!,JF<FW"M(o&i3UᄆT.{o*#oMx*0pwa-^;X(dT1{7Fy 3~x'd$Cww- jyV$hJ!(Bla* -Jtڎu.xPzoIv = F7W/NJr08JV8,ӓtX enJ)0nI" "ŽR+!ɲ,I Y+CS6O6O6|6q|iյr4ioN|ㅚLJ_Nmx$ϗjxj3|l46u8(FZM0 /N}}Q۝WO *ApP4Rw𬇯da$z2ȯힹ1{G fzMMDZО[JϣYB旦'M3RwzY" ^d*T Pz jh#T 4]^\õS/g>^1oTųllIW}}|ls ",.BF* 2 ;d xwҘF!K8;t0^'?LhdRGd<*l&=4' DHPl Ϣ쭠XphDv0AP;.%.pKI܇qb.0J/p$ itKJ!@`5>93n]r6]U,.FmP0P#$"y&EH»=#󋤥$LMS7.}v,'n|9v}n8|q!}\^IlC+󽄯*'e'Vo|s7'vwf_<9Z߃"l̘&1g9'_EEb^8b` (5PpֶhmhyjefM1^{tp|VPz *.g5}>̲G8Pp3>bjMdnMފNe^≥aܢGJ -AѺdfeãe$LZ[Txb~bG=e[ޯ5@ ;SRށc0hc~($ {K$W] GZ%lڈ %/iI,t}O? ^R^m(4l"# vyu `Wh9G|΄[G.L/VH^}WZ<8A7Cg9:ÁC-Qſ|ƴe%wG(+,qry`q wr 7m K&bu%OtF2bYGڅ=MP!'N&S]]珫oWAJw;UJ w5,4kIhҨ(Tn'pwJ.JDtzT]?95.D[Iß]!m |S:Z~av , (c8vc0ّYkoϰֶA;]ǨdDhAIuG[~9p ]3gLj4%(F??RJV*}Y*oQ~J 5:[f-:9^W7 ),"γe "ucDrm)B+ =f-`TB7K(VɺSK (fG {މ%U~94jVߢf:mrވx@~,\-#,Q 2v8t`^@ܿ~0Miegv-? '=%}q~ և{l1}:OlX Z( )F|%~\#k$-@@s9A;A\/} Z { R= !P$' QKp1TP= } D| 0.0>L)&`P> !=sv0wO7}=U0!@gAdAdAdAdM(`H6oTP?}P\\SK[L)MCʹDb%!bb`#:.1AsVX1XkSRt̬l `,,2-%eU5u &;mN[  z}c◜|g??L5B0B<Јfx1Ib 6vXKg,A: #,hBk̭wt^8 vl~g s ީG)E+]9${ܺ_ { 7̤HEaX&\8_ԙZK-J QChOMDlI$V̩Iw* -4I)s#WUݲx룤BIYwOIs _#$ endstream endobj 288 0 obj << /Type /Font /Subtype /CIDFontType2 /BaseFont /DBPJEN+SymbolMT /FontDescriptor 292 0 R /CIDSystemInfo << /Registry (Adobe)/Ordering (Identity)/Supplement 0 >> /DW 1000 /W [ 3 [ 250 ] 120 [ 459 ] ] >> endobj 289 0 obj << /N 3 /Alternate /DeviceRGB /Length 2575 /Filter /FlateDecode >> stream HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 N')].uJr  wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 290 0 obj << /Type /ExtGState /SA false /SM 0.02 /TR2 /Default >> endobj 291 0 obj << /Filter /FlateDecode /Length 34999 /Length1 53080 >> stream H\U tMWϹ")Fnr%B %wb "U%^5kVˍLâۨǔzLj,=_2Yo0 %Z8~#C>-ӑS]?2cW<_~Q)e @=9pz/j{`wԗAI? ѠeRvCOlw&NUs ly㧿kht90OM;,KȪ6+07{7<kn/UxQ)8  2=qQ,-p(!TleIAI}o B a $NWqKMBcKlminʑN;C {< rQ&3"ЌCyɠK5w6KTl FO`@jSp!/.KIɦaֲ T\;#0ٸ[Qiyh_" L^>^IDBP-E)S!R!u&44!1h,2|㱴#H]l5gsp<1?≴"nL3l1ɥ 00oC8SU'#vYF { \e=qq |Y*d\ʡ"Du_1}`&jf0pfs[pG%D%+?.*جNkz^j=nxj*d%>%6+>Lܬ8Zw)2B Wr)#bYY5\[| ݲ8/;/XiVsjTg4ET@"_M |j*Sntzަs_5ʷX'%n{{}11ֱƱqpf:s3眦F {'~$8Nd5M]>{ aj.K;O*KB=F3uz'PDp;IalU7cu Xn5A(GUbͱ"[R9z rޠNeP pzZI'U,ciw7@ZsnT*qm5Wβ\LOV"Y7"[tQ/pt5v\G9*B$SU!n/B87EKP-iYi0?Rl]:ۤ0m:$D-Zf~*쑱H`e(}HER G0WT y7œUjd2PKQxuXiE ,W`*^-7FWMVY9c] <ȹyN]ercxGxcjp9#&*1lN5;l5ԎQcaGv:P$ mdޏEjgwȜG "#ƚ&0iH~~!G|FK,7uchttR7iߛktN^XɒΥ_(t0}+}$Ϫdn+ev<j^oހϸA8lHA "$E4蝲YVf2Aܵ8Qظ@֫*7f`mGxi*fOc̮hE WM5AG3nkzMjx"#gzS9c"kLeP ́ƶ/b$mhQsSdIpEP- wHW11QZ To{jfIJx.-Kw= lQ|Qӛ4`}ƣ=,'ld}ވ b0FEqYF4 cYyq1cm$ftGXT-뇛5\zH)SQG釨^!}k}iԛ!w!h_ |̀XĀ%s@#yb'BuRjȏr=ױl1JQ.=}O[X~E_+ƭ I9cl3d8{v,Bs(l\(1jZr.\1 ;}n6dŜ%R{k-476DپtImUb<-ɽUZߺr@KM>BZ'MrO Q`ϹL L([hG_8s\~r|'M.Q>a)?78ZO1G-Nb_>sWZ2tIKc :9Wr`$I&s^5cS`&!>I9ocvP}^xoM}e9 <7s9Hm+Wy̩4p˵e 2d s=T͜}HiCc‘=2F E7a;EJ tK\ңLZ.N(?9'c}X%=H8K3n{HYb .Z ).Wuէ>o)L(%#]߶/PϻI,m^^wes$ڏ=@LV \ mjdѾ sWj4r^G_C8G r>"yt̟kn%8Ľ7 y--/ %#<4N wbZ/Yea&T*tQƐ@rۯk'㬜{^E4GsH5(4 n@^\2T*K 1O ;0ܼԛ$gs-O7ZT Qպ"y8M3D==r6zG@| ^.sM7|媤=:%4KPx<^B-˒S.h}Uo1cda{o!Ή(AE y0u;ix%VhKůKRB' ߁=0E)yPLEx-:EZ;9@؊1@EůK }p6@>QcVoR^F9D@ЖÍNhrU.{ K4"ƪZ ؂r9 ]=@/> yTǧC}|D>`7kG^npP[dd׶P5֫1ezo6Fj)3ufioӚ{s/(h~5xBp͡y*ll +P;Tk]ٞ:?\!9;=yGMÃ^B2wE~ Swֵmx6XF]_JO1ԩ5_H[r|6dcHh;#ͯW)8y>/=K3 !vdbm|\s;&nb_WmlS>ؤ!'7&uí K5iadSR* Jj]6:X% ($65rs )lD֪U52iҦ AKۍ[ {αÇV<{{Ͻ_'>.A#<<%Qa=7lx.79n~ЃP#JNNA9n޼[Uj]ipu W+iokO h8= kıO3(fFqnXݰa%`W#'ǕLX3 *5Lb5i_i?!c}LKGYD{hw5peee/^ﳵꭾ>[^./T곕%’>׹>[WPŪKUxUkUg+OKR}BZ GnHI\q9PV CAXa$@AqO{O_p֚*v@{j I t|(L7zy߰8dKl ;m`cna[A|7s97G9Oأ^p |\!!FfvRe| 8"9p3a:5d{M+YL"1~#;O$O8O0O{%Oە='-XS%ӣ+%/1\\{k[VƵFOQT'KL?:㎸^=^_o bd'x3uGyU%)FŌY|5UIZ+ b*/C%ʗR&vuocoo?n_k_i_nsَ,Ǒp8T͡8#;95nXOIdۣB ;,6S3+FbͲX>8f:jR;L3IJ6b Ӂ3k]K9BTWGch35yq73dP֣sV@ i._|g~y4|# Tn]ܰe(mBjGsʮN#Jp&oK%H#4!êdi~42 zV leжT@!S@Y,°Rw'M[&s&2"4!!e> ʤ[SG|:މ)L`cb?OKg[-zA {okz^ɖ445?-żḒz;-jD]M\ww]οr>mNIĆ$&(n&) cuL:FEkD 5h;-t`&mj[N4ﳣN;}\ZqJjM^гD|wVPs>9\*ۖ֟yZg:h !N:d!&~uyHn}_g*H GҶ|$'>{kLt&>%0uGzX۫d(:NG/GtMܤ=uOݗ,vfMxipˑt&H -nT*@FπTo=L ~J:6&4[΀޹{t{gAG[B6Wcub(1ǀ P3!&xJ1br~DA<TRC@M[/} Кq 7PrUVeh "V} bpb4(A[x"X2^@aCE2;ӌ>Ոu.Y>8lee*+!@WÜNa GycW 0kzFeb%] 2bi;Gy P,Qx81S GBVc\#DXjؐ۵T5#<"˱O>R/~.1{;Vءl/Rs&Z2L5sA*XlR5j9Q5dXD,XV0fN,ӡaĢb̈8\oF7Hgm6jecB;%,+V8*s3L%%j5KP\iEEkHF3KmlĖ!zEҌ;q-9&z/ F0?'-&  U/WWOWۨ*#X ׉Kc(,.FtT%m=pΞxRd}gCلt;k -"-1?7h z'!N8f C[f58v}>Z:XUWZORe0W(u廸~gEhϚC5tz6Z*:=d ~Bq7;NFUUp7-<Bd }m:gȨ߳Zmr=kgQ Qug4.Iv}RDԇ}qfӕCـhC<)O˗[=gF7Bpjֵy+$TBc~!IB}O_:\.;{^/IjqCޕz74* gf0fM>~gyϜ;)VvXuK]#XbOw<\b3G٧Ou_[ t%>JO/E@)OI zR :WDԨx_1t0oDSX=Aÿjpw3oA'Lgy܂-r hu,r=gⱗ:]'rC'4Xܹsy `W64(++TE Ln#Dy Pֲ ˰c|Vj G"&p|53y>w?]3>Ao?H-]S.PR =+K2cĩTE&IiUŖR˘dp%|Vbv}/ّM1?gjexX GqsˑZa^9L.U'D=oR}Wz%LL)##jL?fw~|?,$vhLE2-Iרfd>DCDz>B'RX-JcWb>OyŸHŠ[֛&(---FeH3xA`Qt 0f ܗk&;Ə$y8T)j,ƭJm!)A*:J#J%n) ӺU:6$&mӊ &:J fK>ز|)m 2\#s9xúmv_v0VKiaGne]xI\0 R(M 'ݭܭ39ιEyjjְt&}>]H_wJ$ 3ؖ#cW啄{&$Odžy.}aT6#y5Uj@7L\!tF4| < y]:b)瞾vr,} Yo_]ľ.~wDjF||}.\AM9+Tk*gA3Lla9) x:4l6>4%B/&ѫ2w_YjXd##U׎WMY:kO=$Z,\@H5g&YN'!c>`)Ch6|ص C -`>šdIS„83B`S0t0 a:LmIiNЩ۷'Ҿ7!wT+&Ō6A xz;ۄLw~NBbl֮$y *j`V *| T%VnMăfu4?/hdEb&d2+V)+PM)ZЉe@ b]OrJEj.w?=~2/3haR7ڮɻ뎝hw-[MVҵ _ɱw=qޡgyhzfԚĝ9;;u7:0 5F26y0m5onmgzb6 +{eD"#{DA;9!"QKpF6sgP)_ZYʔু5151j#61*/S-o[W/ڒB3'ÏCtzP_FxlZ*KDo0ߝ/VIKBEXKP1C:޺{+]:?^zQJ{h2~|Gg{'z$g{/ӖC߼p:ǍO2,AhTd9 oj9$XUFERXop[?=4A酫:1Q } W8ktGVaDO#|m ãN2pZ{ҖW׿XNSʼn%)!QW}&;xh4 NTУ Ii- 'P])n bqۭo9/Kٸqnlʗj~>^+#ųYWY}2neCw+߫'V> Ul.2t[DE?svyOu1Pq2H=T&c.d6G}{a!&#s^ڛ3#k G~j 88B^}mvTq>~o,$VGyr8spC3̭hEN)jS0*Ró=(|"iK:q|B dMݣSKR2y?q, \f0cB]3bx5FtMQXQ3͙d%v:Mn۷An[i%+gao UJXP@ ŦW+LM(N]"5QP=kUIQ ᙳRr9zTa^[Ĥx`zuv4]#[ڦH!+)xtu5d(c<>E[36ݙTE;V{݀CD(cLzAS6@lS/}Y7FNW7<’KU'L寯3y/쩁-3g[7C}u'WYi#NVmk'onwأӫ~j)DI$ǾAA-B࿇B5~OӤJu8|') '1)H8~E 'QEmr ۊdRqp@- G3 f 9E/eJ윅ju}0I4S]n66sageqΔ .8P>}=hqfU]NVcIa5ujQ)k[.6'f>gGN9\"My F?1s G1݆}1],zvߚQ$PN: H$x MP;K͝U|y!#Ћ.8qUy$^ SߗOq:E B$`to;Nq Ϣw } ZPgmƑp|B3@K:oziSey<-ϽL 95b5ǩq炂(\\(nMe0 1@sIiPH ( Һ8¡ w%RE #&H uq>/lœMs$Hw&(WO4ΜDcZ7;w9gְNemiSԌN^#I1:iFDaX *#+ryu PhAvU7?jimI*{=)uqT[bǢprAZ2PL.&`&U,YD!IȚt&$7~*_ 񫺫曺fSN37Q8pCͦZl"(63y# GS vm'x."H񺘙 <#jy!bt Th4*DԤ:12x#2w 6  Nߧ9-a_k g]>5'vx}`K#IU]6YF7lp]t*<0M&}dŦrk\}\^YnX.ј߿nVc-Ǿ?i/ᬒ]rޜovwϒHY,7[duVT\JEVBj8J:+bu=&P bbA#mVDzE_67wG#GpdYf`缗'5K͗rM[i Ǎ4%Ǣ5(iŠvBi7':l_HĝQu%W<> H]ʪa5XVQmFp9\ lhAUPlZm]z){>]|0Vg+oþv|8v@ρo-OB$([o8o oKJ>:j'~s:wt:&J.1PaDss-y,HU< [B2^9.*:HWک=j(#xRq C }O=hy}ZpT- Nx\ D1_ziCCC׷^֮y#wcvZi/SOU^7>pʩ8q9Y}1)MPԫJ5M݆Wf"n8.LTsl_YwCV5«56ξ흝wfvޙYˏa6w@Bj$HBЖl< M ZӒHiBi+m*jch "QJʏRUH{;6{wwNwPAn4K ?ǿ4F?k/Rm{`6Ѳ`YvˊokvM,-DĄe[ 1m2-д,o;@CmY[pyt=iq C|>K&--TP8ÞvWuimz`rûċwxcOT?][N vSy"}$YP`J۩'^ zƊbSP~~ZMzBDnS;E.<Ÿ _:cmquz`V&(! ‚s $=CdP-xH'NH堌^ ^UR+[ODd D G1"4X{FY#WwϘ/{6cLJݦ"c#%?'*32Ͷ75>/k~uY|-6OꧾaF*M%;>hh|6ɶBouu ?VWpzy"Bj曵/TPKqrv:"jtIh : m~m濫C{mmpf.qSEZM5㝤C̎ȵrœNnۓ{YP_&ıVʑs=p.M"2pRQ %QePv1Th*\3nɍ};kF^XrP}x(^ HN Ɏx@ƺ*g҃j$T$gl6sZ~ƴXIN:=FNEw6/+=G:-3 j VjY:]×N44M|f'F99*^ _q &1=Kj=3BTxQh??\\N\JޮqhUEb@DtQ"rUIUB3Qgem2.E!&POH@` i*]nHyeCOS eq*j7 :[lpM d<&Ln b"Ƀ3܉:_\M!)RA`ZT|}lyr>K|/ql_7%MH"YPQRt &E 1 5a4`t -ЎnHT?hNlZy.ڛ&d}E⊯GwżnǫեwozC7_|<_֕=^7pW{ n+2#ߜ6Uŭ'TTiaq{-+ @&0Y]`%Z-`&ޢ~Ai0L#qxOOSc"1´G fyr&M`V`5Vg-S]wBŧh'HM͓ԌBt*(ժQV"K|e$ ¶ ՍE/DwrF\$ʷU]lW+2qKvL\+<%v| z.J8j\ffLV LT3I1 dUIg 7|CW1. e )0zzF_SR sO4?Sbbjtid@ӥtb ؍T mIMm1/k$S !h 5tXtd6e=ѮxAE EG_Dh[cqĬI#0aӍfnhAl[JA)bD_Aï0Gppʣl>SitVǺֽ3Y` L*J -yƁ9UA1Jb7w,iG,\|t]={*ӈ批VrG/tN6 pCxGfU#5|ˇb,Ĝ8N7(Hs#yfx!`A/ @zj6 !j|EdxLBmY>pB:QX;"J"JT]B`ܪ# QD̮`3 > |@Vǫk0>MDɻL-v9^/EU};Wz/S\z}jT\m]A[yaDg|0Ya21צY/Ǣz5;L`,xrzDy S>az3-+_7T Nʪ6qu=i'Uy=;>۱Ǿ s)EkMP~QHRV`FՊjk(֭4k+U2UVM{w듥{[ΣEkDҼ-o}uބ,~(斍WNl&w/١>pmٸG}ݥP"S*lX\M<|DnUTURCmb)mS zMU=8C3/7Q8CPeUˀiJRimeY>؍dv;pL0كD<x|FMhb&IXR`zJ:ьXlǢQw,arZY|ԶXY) D*đ"xaG݆ M7 ɚ5E nA]Űς| 5'@vPCg34};Nr`PLXO1 A?5`@~o=0gi"շ$/6~Ir"s$W䊤RaIkOw;;[a>U< +rm5R3Plw$K~+ZW9XVЫ#눕`0J=ώ(r!;LSD |,᠄C|LfD$^^#K󼅜 fC!,Wm;'K7RF,zyn3Wt=V%OD9}(՟薃fńX5 |vHjU`7cȠW`"K(êuI.+oȑ#X\n2ZEA0_et(U{)g+ƛL|q7:HhьgλAFÖ qbXuVrKtImČm6ov]sG XH͝?[,jQp&'[#CP?ngMv$kYcf.\˩Cb^b~ v%M u~l9er[7ZUF\fZ,LADu:k x7gU*f|hm嬸J&Y[˅RIr*eEa\E=6qx+WNCB\HxdPلV>V#eKX!tp pZF i1}sxՊGnHۣƞs% Ӆq.[bpPKkۻyl >}DC)HxS$$nN3nv) Sn+w_sD0=yt>k/+Ww%cezX4(NO)r)gw7?xD#1rY"YuZU_QEzW]VjX}˥9iH NP{G$D R ,.0x{ԋFzo@!%-"[qPM 軠 }>7\s\f)W^{\Yl$,=Ao.<#6d?B΃/\ZMGO]_&?/^x6s.Įc\*V!s59v m[-/?lz%-Ɩu(@Q*mN*#^er@977M|d6Iu^޵Ix:FᰟW(̺ +.u!',׫ 8o't4TXK xƍX$|˄tTL ik4612aN#1󹌮klE%/XQG7Lآ8[}hSҫ[-Rj#rM~5k{6~\\m{N16:FtFdj xbjlR'0Z҃q_TC)ek̻{]/{x9HYl;eO'P]m[WH)MJ6-[%YדXI؎K 0X6ie^JȐn@,Hu@-&mݺ؆am1$n@gC`foRJב<8 yι>V 4Gq45Ma]0K´Iuj~' ($L{>e3z1Q|_.W 05a9c)Q2BolXaGjd,GKv@>+2T,] 7pfv8N;*fAxwiUBߣIka 05]X-r%6Y/1`yOKl"fR9ʜ9Ӊzph @h<9f@~bNj4Z ˚2F5iFBJTkxLcAT֐sBF3phJ:Qv6ƂFCUM~?\k^7.l2a)1}b0 sF%茞W*D/.s+QGr~$rDE]Zm,ynypY@)B*-f&Y?\89`2 %f{z6[)Y!Uamv%Ym$:O35:Yh>?lvk.ۂıSQ4lA# #$bA$  6Y}(0"(WOFNAާufOʥ?|$wvwe;њ-s?z^iv]\Җ? s) y3&`ZmyR`3U,M%uL5pYe'y?,_R•~R- g **mV :I.AFUP`:P\H1e-}`_{i" ^3Sy.fbR%3!Bt%*!0*nC _)VRK0VLMD8>09ʎ;jMպx(x8HhI {>I>p3i}XP6gSC9nc{|b;~|jrl^ȗG5l85}@95<޳[J-NxW<']@ D zDl89A·Dʘ "x<8.-=n݋|`LxO}"v χvEו.=tFSLSLg5T@ ER P0ٜ&J/Nlp&=Zg`ء_/0)Lc^L,fS/sM$u$dp| Qstw?:c=o֕ wSbPy-}g]ߑypϟH3:/Q03MX+*T%0-,mJzTp]$;v$4hndͪ+3f-6=J2aFja` hY'QM$uYhxUhΕITyP4_2ն՗ FAāx53aXm(_ZsQÑH$m!k;p(K F"- +8v``sl{T0/)F;٬bWRUW[y̺BP𿌗[lY̌=sƎ=s8u'M4qzV^(l ~YbZaZi+mC VHQ)ծ $]6>PEq83N53)`Tl_@VYʳ nܒ hPՈDL(vaIʀAI,? zGn2yn+ß!eFl&śb- FZ8!)+ 8Da}1;<X&C!||l*ބyoe޿#if"3?/S(0Eڏ .%ptk{ګ%i)ä|)Ax?(Kp}q]+o;n5|[pY>Jr65-2G9rDnL H d΀37hwI7m70RPv;8\M~6ȉ:IL"sY}\0(J8 9N $K e2Md aAMLcaXeYI\0a<'v;cEqCf2l ,y/@ i!0QK$VBT( ,d^\ nl!|U6C%il!/sޑgC?nv(\p !ߪ{YZi~c/v}/ׇ}|=1r9wwZ.>)aɲA\DnNK/yBX !_B̡*wc~^V!`Xt7' ЅPBU~HHUFCѦA<0` nX-WeA%VC^lfK*B2 E}QE贯o&oo遠~i<^v!zKZCT/dk"Y؍6uBG[yUTkhT_MEĩaZ)ƨ ["qSVBtwyaKe&2t_M]_3O~f0<1! yq,xfO_4[\vyû 2Z&Tt#!5nC1Ýe ;=|fH-*-:"[E -Fj26E B/w/(;}w؂ib/3"0d :qzDnQ{6_wMyr9?:妶=F 儮9PPCEgz0_wO@z |xc aON/IZ3K+h%U56澂4kM[J`E#GZoU1 bю.{ R8NC!s0'LUiPG%MZ`CR jI-dE5Tn6T&j/^a%#>Bh|t!D '[^ϡV{9u2j㝙>x_';@%?53KwfSy4~ {PfxB~>BWe//eSN~6iRqxh6̂o⇾c*A=`$bA*ep`o5V8m<: K ǤMT;B3Ҍ)>QT -*@ZQm"|-v` jqJ [ɶ0|B+">o@p5}w6GZ%M֞^ 4EM \C B^B [VB-.C[!C aR 8C \|ٶ|Kx  Xdʄ" YfVyWq+ia?1'Tu:HרD!o[% _*ްbO[B1\ U&&HF_~(D Pz6 ) ' rDp1"1P"}Q/60LB .Rw[D2-2+зUV($k \ +X"FH0L$FV@%1a}D/똢7:@ouc~pb &03Zס42GUބCIP @@[瘹PwVWwsI))][^ nKYB<)fp}=}?,A(Lit&^ LpDdBl8NP!"PĆs>'q饝][v,%r1a*wpU_&+"vL1!z]|N|{;bdi}S%O}k" {=-?insbt-vwעע"M;Ѳlc qWv|67#'zGg(ˇd(2TYO]mq\ex^<3g/sfo3y]:ދ=ưS5W7nEr6Q-֎-T8O I`ҒѾn"SA?1M?gvgg_VAxI`Ʊ=~F@%WPډLbDU{m>EC9>ڼr*RL~HxiNry޹Jl_i. ZO!Lkҕܒ(6^riyk6\Quc_Iddu۩.hKe `ec6" u[JxFC |< * ]]5$.WCZY:fV`Iy!i>1_Fueiw1R}o0+OO/?>5k p)ZwHekrzVl@>n뇥yau8Dn7ǭyv̊?yk *ݬk5 ye>'x5xǪK il`gym^-Ks+Ҝ ϠS4X c2!d2YgȚXgUˌY OY39>q<WX+꠬dYP343R "]& 09C) ECo8ͪ=a0f/_gY9q'UP9FrjO &4D4Q5mFg+:rYݣ4]F5!tYsp<Ǻ넅f-XYuݺiֳѫ{֔Z41JBNI1:((Tɠ0ݝotp˭0p!het#K1n̥4%"eNb&5;K}i[z$hؼ$ qwu1%$}Q>thPuɸCχkTvB*kFb ]ޟq7ߍ6o}(=2,ez_΍Iay%Rŷ>CjN<ֲsG`كBh! s[[GGU^(X i|*pg- Gr$MqDovmp@9$OԜ#mb;!3LbFz]$yyUτD%Y1[%UF1=mLttZFVǁ`*ceVUZd!QXB`LdPP)x TPrٮ|fYFu~`~?),ֵX1X ƪ zLaw.0p]fF=K k [Vqj[X`6d.BN.Yo:=hUZ!.XdFB nL1SkL@eޖh?-ACt^rR),,htRw"&+áAgTQ!Z@?m`k9. 6K#CDC!BNׁq7]Kl_!A?B`0JN܎vi՞8m`6hnpq Kؼa rDq{f~; zEs2m JvfR}Gh"!!5 Z 5TS肕/=OG]o;Jy姻:)_}B)!wsx 7}rfWUcaIj}K cNS vzA5B'xUʉ?yS$_%u^I8zj_8P|.uwQN'ztU^g^YqYZ,1r͛B_iӹLl(+%IއDd+Y #m Zfp^]&=Yʖ AX 'q4D8[-o~ƴu߹bl_!1 7BbRr28I)&*u֭UEٚm_֥/kS:)U|(_-a˔jG߹s|ιsҗTfSZ;#'#R|P$ސK(O:`xͼ"J\E"̬j I3Dm(^H^HEm픍*#jS媵 n| Y8X5k [ ]G2\E.%O#^`<>.ԇLsyOZ((Ud6"\]ﳮ.1YGxʮg.©ӿeD}U^4(#e5k;Now@M.hg^ҼڑjvOY5ptOa'˥xx}U}f܄kNN֝ZMYҠ4GNJCLV,m$r)R$]rgښυ+z{2PiJ{.b(yzo7OGz۽y۪OD[wZe喟D"D)9/U.bLƐ",5* 39 φy8WX[bLvzNֻQWj1SfE?]ﴮZ !g3>fv\xixl\je C] :1ʌO.NwJ6):xrNP>p@¿VQ"`&\L{u2ͣ{(o]@=p OXG^|j h[ @D$W`Fm~'m0Zlo&<+<\HECL;OSd$K9(!;Zhyk4fvx|'Zxb}S%p"("("("( zqUJpaQ& JtJOU5u`c9ѹ+ٛ1epж;#;HgW/؃*"P]1Z4"^!w<.}>WBc}4&֎Yp:ߣ녛/qaHb`+0)G31$'pX8c|YzY̠$ d!0 %sjqسQ$>`'^M}TQ \4˼kL[w/ٿˇF17V3KTbFr+{Z8H endstream endobj 292 0 obj << /Type /FontDescriptor /Ascent 1005 /CapHeight 0 /Descent -219 /Flags 4 /FontBBox [ 0 -220 1113 1005 ] /FontName /DBPJEN+SymbolMT /ItalicAngle 0 /StemV 0 /FontFile2 293 0 R >> endobj 293 0 obj << /Filter /FlateDecode /Length 6571 /Length1 10232 >> stream HW TSg~YlMPۡEe90 uLƒ ؁ ,atçP>#t|#O%.3 ׽w t./`3\ vMGEf7#q=wb@HĆK(xgYͼqkWC]F8BBAyE7j%;nIY&M' 6T:}`2q!r@#)©ct0G>`VD*Bz5 4b'9@; #6%FUi '`)v2+t2QFCG}=G2i:(FK#"?pnx r.^t:p4[$8wqwzNik?CGt͔?4lWU[st>Y_u= Mdw :SAgO9\!uͻAk76NH^5aK.uLLWRy_{fIgmu]G\H?-d|f3lUS+l9osn=mH_"(buPY>8o3QL?+ߵw;t˛*B %qt8k8);(z nwJ+NΪ_btJ4/H;+6Tp->I:Ŭy^UYSn?2*jWۆzNΑhZKO{8K1^7 1[u[?Lta2XЖk~"{!8 Lwb>~Ժ\{Ï/_Sycqoc=v^X|oYGH&;gɟtfLCb!-i\mfعOVI޹fܞ$E][f1LOavuֽ7U x@L_9G(As T҂7IO`Q6= 75eY?=W@6GپukWD;` m-C-E4[XndfKD3ʺ7*o4Bp}O|# ]w̐5 \Q=-o65m[\w 8ͫb!;_qݥJ<Żz2b;Vvfm|(4c=Т~1oODZ-9Erġ[r޽Q-?gㅅuټv;J?81Nں+&iXRwŹ8{DOz,&u,AU۴1'?]Brq  n%6\C]bա%Me'cFt&FAلQO):p;p5  H#I9#e.yM(]^z#E7Dؚ(܂[)02 A$ JP$_O3ekz*Ҭb 4qz;t&T=NmDإǝ)<:)iOړkD.CBY\` 9GZ.s? 3ĊӅ,Hp*IC:c?o+a<1EJhgcM-:fL=/if4n ]?8?M38@wa{1ه =FSYcG:W SMn_YsƁǤ^L ;"u( "!!$ dlm4 wMMO4ą&MK#ѠqeL孓#VLl w i>x2ܚFG.:^EމsE]BÏxnVU_=fOC3=4O43J܌buZx(W>ؒ֜Y\?z^{S hˡK36ݷ,1ꩀoV\-0WZKiVd15.tU|; Ie#lȻ}/=m 'M6Ѣ$\Ʈ"Y9C'A)V_;-NSFGCtćq0}i?=dR֐ʦM_´+/w}uۅ;TyҺHiI?޻P-ϯ)^U=Kn *D>t |&:UPnYQ(r}ΧRUS6>yK$]3o_Lw٦evc3uc&2wqFY~'$d乊T&mR}]Ni2Ji!=AOD̙E̟-gl}bVNr8/.r\#̵RކBS|hD4Utu;u554BTCd $c.i I.sl|6O:,bAd FB?flM|Q0/ .~%E,&ݴV@6]. PRZ3:;AQKZsԀdtA3z 67-ÚAt<:5;sRjĔw5V(#WQ8C:`ɜ'{ zc8ޝ.v8&8NL3&䟗c_H1x Nj; Kж 01FVtD*jڤjC:Tt˷}'LZvZMℤC>Nٿ>w{7xU1}`kޞ;wvlkjZ[o﹦MO{cWa%& u(` Aux8d5E*Z߇*i=|S_IJ¡6PqLUh 1T=gBPC1 о\G!8!Op."];|i -مQQ$ېP>-]/P~;a~\ӓDrX2 GywEs~BXoJwɏV  t׍BkJړ(8N݊@6B9-yc]#+hEwAr `qs,J.q:;4e@w]KThQ%aSd#f 7>/[׭vjY˵Ϸ|En7 ٢;n5:]p3on^nB|{3{ю82|7<7f ]v,jeHnź|^I+R[R~BCI]R~פ|@od씫J.˲( 2'\W,5׉NFD;9ֲ98On-^JiȡT9{H`,U$%]{)͞i8]xVjEeS u!>6πg(&]nG[{GN^9>ȫW$;&Qy[ZOO&v2d+k)vH$"Mϝ3YE)`:;>hf^S'cTWcJ!rSQc8e |Dj&f.@d sx%쿏X$Y6d+{F9fNs,VDc:9H# &n>98Uit;[Pr W⹮ LH(L`&k}~RDu:q++1cLJܙyS3>ñ-1Iӟqلqm I' t{~xp08 amZm\H6'8.w}/O4V UٰZSwc;vmts,{.qed펝g?g`*6k>8pB;ݳXf}&@n-/1:J'gEI_G endstream endobj 294 0 obj << /Filter /FlateDecode /Length 29851 /Length1 47748 >> stream H\U T{A  T 38"6&"TQ{]PZI5Di]Ц!U.Qm[֣E%D101I=,DژWcL8īYE>i@ s-Y wV4?ϊyk9}ւiyY9g~ gvhϯ4byw4,cY|z:RU|u@8vs75q-Es=; WC75wзgLX3tgka/pDPc/^F: BG@(F$4 __S2S8`+BHĻF},%G%lQԷ0i |эwɪo9xpؤ4&"eD#P%؄8zzJgb6ɟ"ej?V(#Y yQe)TQ?_]U21:};DJ fu4uU= HKQOqZ2 V-#w?t[{lOhS~D611 Ph"*%b7H㑎'aB,c._dMWW1X+ 6Ԟ,ԗg8]U˩J :pLF.X8;xF'cd:ZOy9^N(7*FjN/8-vza4dLBu I\#I)- 4b*h'j:M>=DX+6⼸ ʞ.(k9nt_]ѓ.\ H\exr,.sH=s~$WSGFJVNxJyTJ>TOMe)1IIzKLKXT걩U۴|w{ǵh瞋0rd1ZʕS0ΠcיZ}whS 83I\-˹}!V&-H_%Ew #p(MLYl\XΉ+⺸#K?NvݥCNR?e#H4Lq=wjVkvSPzML;L59<μga 4hĊ2Ң>*b#UB`Gć( g{(X-ZuP 8-:Í2KEUF-ƹSfQ8Suķx?J9&UD WUT،mȥ.Aij\Mj*S}:kܵ[8wX|ևqDs$2 'ayI É89lkvrɏ&(rrg^lg$^nfWrKkr1shhLv^6oe?hu5WB*_!_aܣ|.k]1P%hwuЗjCMFіhFA?p 7v.͖! /o޽b_#{Dx54-kΝ:vhR;>mZ{2!!aMδ"2]Fi[x! . /%ZeY~icii{i&Y`HLaڭuv3y>< GPLÕ<7ԑigs{Z{'Yrcǻ5[h-Cg x0(W'h ppGV+mlW{pTg?\&4U|zo Xc%5-j<[.̶ٲ5-eD`Nt IhjjIFVb tI*φ 2;+0 ?1 .f+6n[Z`ܸ> 7'IP*-exjqKB_P5 ?cl >ZضǪf# _b6Gz:QP̒lt-9kZ"'904~S%M {SԼ.7NlFi {E洬Q%OvZr" `V>K-.H5񂕢G D,|Yol\0xy9nZUz(}{>SjԶ4ǍEL3b"fl{;0_J>#yV/Cl8؞H==i׋~g+|n.錺,X%O-r%,i*>F\uos 0;rc^GH^{Z A6yh,Z2M, =GA_qYB/T{h h5ql @6 YPɾv!^!(\ KпzsJ~ !6˹_?z=7 r6{RDI3_VK'΍1ko ri _*'xx6_ TQ5d/`E$ثP5K8`EЯfEZ:0A OOi%l!I}F%*al#\6܇ 9^}ra 8qsl|G:4-.c9}vܴk8W5p?_cbcj%I 0RP>{r8(^ہ<b<kC`n`-F;YwXql:ޖ9+ Ŝq%[2hbY^! uHIvھ%Se:%#//ii#hCx;Ct g%χ JzRoH:5x5T"'P T5n*ڟ5:o"+K"G87t: 9\#vM08Yĵ3$_]^Qg:O~ty;/oyNmܝ{y7#8Α8ǹer~?=3)EM@þ#~읞S-w!CM.y5YSUeA:z2:uSӸתQuTO}!$[$~ lyZDrڨ\ EY~ 9m;hruOוihJLa jD-c,z}쿾s.жڻa{N>JEbEa泈4 0~MW+E 1oxpF"QŚ"MkcZ>=o#g^[)!s^:5AyA>"S)2`1wK}Qo"W#ajl.{^ fꋐwR> 1j#Czybo7Se!8]%" b8Ԗʔ% ~~KUQ_vS /ݢޗSXG/SiIIj;wB9p 1c# ̄xsEU^ 1 ԧc_f}u1?%ߘ3ca"O@AB4G4pJ~ si${N8%-,Ќ;'VNBA~ 'Mubq^|Aǿ EYkkK5V*Կ(]K10 t4_r|K.Z'6FZKӵbzJFɟ;A=1Lnxo4߇EO,2U]Z Gx9=O?p~j~z'I(C@G:s,, WOF$ !;vljy]Ֆ'@ 10v?#%Q+ޏa`vئ;|ƉIx&qm !Mx4Zk-jڴ'>7, _E#BQ7଺Ż4!rb2tڅoD@-u[m}<"'Εh_ \!T5)O) ('(ڏ/B:ˏ~ X.WCzǿ1wH7|^L%nz&Mfw/(#;+VXv:oVkY5sqNn%TN-D.]:a,ZR0N\EIˉ.FL8Qje?1Wi ?/H7!yV)I {emj ))! plĩ¿/}߰ ?jJLǐ[=":t:`p7hD,nneYa2jZo.GH=aDLm~xU~g?l2Nk P39&JyaYrSߓ~3d*d EEN^~^}`eq( +~+~Nw'" zIX~\\b@ N?c| Z~$ tv!FH>>r ׌p;Y`-n4MRf|+w ^\-6oc^~o˲.o7NvQ&w͝qg]n(-j,ݮ2vɀtCrHg9Cz!ąߠnTnӖ nVSž ,"6;aZp#ЪK>{lV&:d`AfdgF՜@:+y.+vyGAsiMwjܨw >6p׵~R~:kcP22=RQ^\c*D"~S$Q8A?NͿM>2.߇]!#Q&aTjPvY l ;a$A4bWAOAcx1hl ƚqOP1,tq9`9`0 o9|< Fo׼p!Yƫ kC>eK ^"͸qvL΁̔Z&G!2M$&h~D&U6BC2.?~Pn"2eyb"!Qݼ,ԑI$w rLRڣҌ-.}EA(֘.XEU5F*PH{$dj.dLTHrb IKSyS/I%$B;1ĽoHqS)G!}d[q}b0m8fGSA Ze.lhpEN't#&hQJ#1표p~J@?67-_M%Wx\_رW$$c b "uJ@[.*jjŖ>(6ò%]ETU-M%U T%N\?93w7sϙ{kT+][zf:\;lj}F^p~5ܦ2>P{5Ԧ!:h[@U|ylՏ Pd dЅ6ֈC}W:PI/+ !%BݩQ(-$EX$VԈT,-ɭ.*2D+BaR%$ P)N2V.JCL N9v~5V$rgrPk_ cz6dw<鲌/>GN_(ce AK?1&#ay=9d oKѡxL3V$MC9z #H` v'֥HAat? 0Ylghdf W= C Ϋ7 kTX0(U GU~?4glY޵)ט:ojOk#h=3Q_d<9?3ȜLc3LpqVff|1_؝?Gg_8Kl\ Qz"pbvTl{js;d&dfkٚdVt0ѝHBr[[D샅zc3dLi,:[u:%e6ɬ BOQ}JVZO/x$t=DJx= {@G;KtP{CwtHݱnĺES1Wsk{-_ت /BC1FAP}df(DK1~R2q\^YBK`޲QZyokmoihǦ 5T]{mku?2D?:tzA3L4dbSÌ H1+x< c~ DO76ˁUѻu:s&αO1F{ "hUH$A8ҮT)WB2P=wN4w"l l`A~E `${}l_}ljȰ]qʴ.mn]pKt_+0a᫱[9H"J(rv]= Ib+Uo1y]^eHPXK[u>a,[JWnAl/d MT+%B^WK[kؾkmWRP3ΨCfƞ} .KRJ %)i&dA@x" 4TUDU`1 UmfUVVlhUC;^{7#͹||’`s.ώ*\Bdw0*УBJ÷)9)_M 5m"R&Xb 0t^zp¾y'vxb睓8z&]/زsVfR_0;G 5>).dD~]ǧ ip $YDðfRM"^G *Q~;QE)Z=_]P@hcIqWKո0#`z#\sR-QPP˖%JXVr)q(dH*=U8}^;YyϬXpM*ʼ/_& -"?w˿ܱ7n;V<9) a xÉr𳶌pE==\@D0,"Ã}xhf ,(D(taO]}<0Κ<5`gdQ 科ڐ(g%F%*?B"pS7sܥysY.\t͸)3J YA!Jpen t#M=0VڸK dE4/ F#JD+3p Ƹ}G^)\&6ifM:=/_G|igkc0Ww;=0,1AP޷Ozi %[Z}Q? (!Ȅy?s, xKc[.uG}E:̂7)y^d |&CHxf%sIZW[>@x"S# P{>X،<ـ(&2 : t^g241D $1[YoM%C6+8Ub]m0d3!aJrZm^lo<)dMeZdr3_EpzMVopC*9_PWl26?eI`jv^Q*so_ڰܹ>pJ63q׈ږ'+띯/n<\ٛfNqй[;n8-sx=lFef/ 7<\_tuuj$PRN$էV!-L!Ɇ2Lc1"WijP6*fڠэd76y5XU`u)T3D#.` Go黱U߸ἌDs5]7K=z6_mmsey7ĬuB~=f8j=/I%@@◘~ܥ(&t͏ OcO#XW׌Le]gbPػuٯ^Wʫ^yqB<'Og tЄJkM*>VSǖ\k Mֈfm|HBA H0(Aք%6fh 7$=S] aٖ%B ñ x2pm 5Mfi. #Gꓣ"d B^~rCo+BnuAO=A/W-ksX\Y/S5`Dbc8w;gٗr'& ' PJB$K`]g($Le4 S H +%b@U~Ƥ6MlHbggct<ؠd41 + ths5pgqJn J7;ɨqVM=s˼{c/ǣ \aRP zjax,ׅȕSd+\<z o ir27n[ ĪkaU9%e$ze%EKKE0Vo8iWM ib'e/-ZEcx,rUgǁ|ya,!"64nz32-n =сֶ^fi^g\lٖin}u M P DP`|tEV*)kehBC`5 b 85 (@P^ NW+:;DE7B·$\5g,*ӟV-qbg}ۿ9¹K2& F._,|vg;_ǯ_7Y!ȿ^;+pll lЎ+ &،« '-,[%)+ˉ5fu.1*b5CpIBQ{r-r9Lt\o7%J%_[2^`vS%JY5b*t{xNUD2R55@E Q1d:%Ч&fkȂG g^J!xjyyh(5Sگ^xՃ tpP]{hM|Ĵ6hܿJum?d2$aC<`)cp)de{?O3qK({7F]gg %XϢ@try:󚗴n'"-Ib@IѠ E\(N;0} @UE@X)EXsM/4墷8]=g\5Jcj5OIj< =!,b.Gԁ;˥a%ruB^ 'RᦦrCdWq4 ¸2Y4 ~^#@MfK}ڜ Ԉ{pC?麽e9Zk%5_ :m$қRms[ 3xy;f {7~Uh|N9SKWvd_ MC|_&Ay0lmKm$ER,$[tVVb1j[swP}v2%ﲬӴ7Q}%xE~sL]V`E 3k J#ʊu[~UZ4HcrFQtaQ X*^*`PNhX>k*6hMt^nl(D5WܤO%qia23"hlc{]QF<_Vؗud( 5ᨑxo` >cԸ+Լ V@[9ŠD144n0Sfqf ]$EHb*aZ+9˹& E]shKr4aָwPnk"]z,u !9.zZsaG+;|ՆZaQyt>/uwhyڒ/4Dg Ż?yApyidT,-LΛl?|Wol[W{{yvءI8vb4vKvSTjhS AB">4MC"!6h*&>;k)}\VX L>uCPHC:N#[0?{2}z?ns<#'O۶J?9 opy TnHTڬڒml)di>8zkdlz{y[ug;Tp&BOL{[Otߕ A=Ts{! D;s ߮Lj"cMͶ,naXCObƄ1mc .VDUk(gK^%z h3Sgl,u0%Er8–w7+ZH&=k-@=|<O^M;LvU#}?&ÐpO*_'3rJwߊoJMJɦdPl MBÝwI$~%7gȩJ!*f~rt($ٖPiCGHo_\Al/,- '/9ݗHl/Wt9Nf}KL[@1npsJW*ЈT.mL?pr_p|iajA 6.t@$dCG !M}~_Aeva.r#|Cnod>꺎ݩxFxV"YYv6IBs4u#c8r>q-l8IqpJ)#mIچ *a+A(U*hb Z &!?֩h+S(MΞ;'-ݦ)Nx}>_f#CL RLn;N5&Yo^WoP3XqM줕L8h?̂F[猣vJkv0ӉYrwE"=Eҋ+o=W5>ty0;]ʏG/h9Z>G}565*54$\>]6.o|RyYug3"#"֯d|o?%LS'cJN]f8Xy԰ HZk#:QQnj5iW4 k|QcF:Rx`9ߪX˱Ǣ`$"Vi6`)h0}OpXEȂJ"[E-Xa\[yp]M!Gي 2A 174$2E[2`Sea t%jړ5Ж&1FU-7ma6MAVF~{<-W:ևeA7˧rGNVi[y!ܭRIoVT0ɚ$t&:̃x{)q>E$q /8;Ix+PO%%MHb%"sf2`c1ǂ? 0M[t.<a^blJjyV}^'«wִU_xI]țE=I?8yGjn]lɆnA[0 L~>4TԮ\8kW4 ut(i ^{MQH!lЇ-\Ҁpd"+`E2V>yDy@9uV{"o3\J~2L@ Ĩ*@:`*F:)əuy}.a[  CWvIWtsVsy|wyDGE"5Wa|(Vi5Q@a$yyN"磼n#R|069 Ye:K8z;>@ Ќ\-S!L#6ߋ0-MEQ$_E`mC$ECJ)dQVZh6(P=+o{-&L|,/eL3Π @JD8^ Rg~7DzC𮅱3&< <hUd@R(:g?I sgv96ΊmtNqEʒ8-nm!ci|(~N+xXEp 3]UyaKծnʺFeaOEV,ʹbչMt5̡׼k%SIp/S*_$6mwΎ}g_ž$`_ʏ&lB-cB [I6mXh)Jm1F&"Q Z*uhecjʴ${w?f=P4D%%-22 3@+Њ;nfQ$I ||;3i #E$~3>I|K-~xMT4NK|KJHgCbjL엏i~*5-OjM9JUIC&|)Lr4\嬜:A)l>1fYXJŐ+p$aDH, s 'gc䑀xd%)C,%(*rXw3tʖӠArw*ȯqp[2X'DpӐ=h,Sպz>k嚹oPL"mn[E#zd5 <E" =T vI"pi˺^|>nތx!\X,L?1sȕX$ٟzm G|!R2+@.JuOb2pдv7p+. y: 5Jy No]&@. :.vQ]!zMa0KOҀvbMp<-P 8>NORC}af0Wz}3 2NFWw nK+f@݊Eݖ$0='>?D"]-ۄ+OK90r6ƥ.$q{/W1_G/<փaRYڏt*{dMT |~<7s.vZܵ\VvStn{*徟'Jhp2 Ǻ/'U*h< e)oqAAٌԬң/6B_wG?A]C:, ..e(}5[#:6Gx,DeV ἡ^;"o7SgxM4I/r1#tqAaT7tU^^S*xb(5eH26h|^$ e *D )y0 %w[X5%弒#jmU[=[oݡBfUe7J7)H8)2ќщa ` ZHW6!P|,u>A >>F91 y(E!31\fz['@Z-JlgF_w4۝^e?ߙ~]W_:m6\X=qZoض Jb4 Rq\_N^pY}H&:F#!g)a&yYxWb t6qq;ΎϾ;Ǝw><$>@-V(mF)6~W'F&*щQ` jiRI5XiTք=ٰE}|!Fj:"mȋH$ko Hg0das##ClͰC08f)xd&."EH;Ӹ3iͨ1OM6} j WG*LoLr0??p3i:i.aC.|uR-TNʔ.N||}WzoARQ1{kEۺlC-O7<4 u}vV5a2DA/bv:oxD)Kы~No}d^gYSCO69e`+ޝe %kNCy !Ԣ)f yk:9.21/?zoCI)0}]ŃYc[7nhJ%x=t*"i7(dȺ`H2 f,J%kRT& :=y)=>y5;?Hsr>^t xP-xvxA؊D]E}N݋[X6;؇cc}8gx"Ѝ| nD^1<];|c)jF xwT1T2ўުw;{&$O K߿ _>c-PRەר/]9{~dw841;e6ZC4͡,tXJ !?K`{|NNJDTMe8$|b(l_\2Adek. C^v<9lgڍ[cIPldgX`yִb6Ӌ:Y(CM>qZ7uc 5v=qYqA #eT&e$K$3jLѾÇyckR)yE>K%)r?5`RU.$m^x;?);"k报[7eU H,!V_&x'W%8e>u|9S}Q{h82R4,FDĠVdW&T/zZ lzzf2eͨ/h{4syQokamI j,DpaJrj f3ml {լEiDt\(Lx n@ e/"8^I~j2I[ SNj9}ta&r?Qs:RR~o~wCq: ȼiu~cౣnEU/ō$ 7&'/Kʿ|;QmS.]u>e/<ʅ_y-ٷКZ )=Q,<2vfiy'^<:ۂTX|0$DX)-#[7vS\Q2B@2MMN6E(DNr ='d*ȫTI+UrU/ӪQ%m"n.9#MyiE46DsII4 6Ri< Dh8D[*υ ^'Q.2 [Yv,lhYɲeM7}˳l:e-J!=u!UKQxiװ,0aYx)VGlBѺ97"HUР|yNPE 5U)PR2&Y7(w$Q`7r84Ĭ!+tE8tYHqo Fv a&d1xpze}!!UG7Y<4^bT[AǾ'w -:718u9ÞozpN F_rmۺ8/%RJMY$R-zrJM%cKƁti '%Ag`A%^-*ҭ0 Ɔ0f c`C(;J0C๼9s~'ZX#eP(K`Af a-̆ a<3@BVQ-;|m]uBe5zAWeuDAK$p)T:në+2B=j׊=3٘<ຓ,mmU(p{C^`ClP6Pk{Ɔea?s@R0|^L?~)|"rt6KM|ˬdR8`JA ɤU5 W-`o:"fA1,k1t]/ZI={,5Fdx3ij&)19.]P܄a轱gTؙ ?Ķ_hz?q+aD 1e˪FX$=AoVR~oRy3  C;|oyuu8!H߄xu5qoPs%; ~{h%H@c: ^_xӭ .q}#.m;߭=t7I yzoO!NHCbAmD f@"A KHyyP*0p{ux DOBGO,8šМ pZ.H,HsRK%qY_ՈjQ*+Ԛ`=!uHWdw1 MQvX$ITbSgFH<8~r9Z \90 G] ZdohІa-6뾼1C^z$Q>g= B95_cVۄ&b1@SHe{S\W<2ʝ\XrC9WuֹÈ ;XҠ86n5 F)ei(cE 9R!:D~oN;b D\@2+xhPqW$j*#9b*WRIe)BjuW?fޓe_8-lCAm:sfqy!#T ZMnJ?v|?-M =k#P4 w{Q܋XrKaY/ж4(蔏=~u|Tn/p,PJ7ir/Wg*޻uɉ;>sՕR/7^ٖi' Dz ׈i;C#HO)e t`A[s٬(F Ko$|r`,&!Ee2+(e;.q2цLP&T(* bE͙wز3 tH-ltyB˅LDA'ݭ:e>B͖dz.] 4; hLe:R ̬N#} @ҁ'h0ۡ k4( iн dn4@~2f5:mCHZq,}; ϓcwQ p;Hay0^7AY!$}1ÈM_/d\/=oH^״ky:Ԍu5 CkĺF]kK:~P'CY]4%R [{% Jl l:F2gyWW^a:t p-hjVey'J'JËaɂ̨7 L—P4ꉨ'W4n],BƄdXCCh;ڎOi~&4Y~.4gktxZIfó\? A$;ɤ ejR3~̖-OT{\|m1 !opgYhܨ1VxrY# 傐ϋmEd bUYpɷE܄(?*G+k580^-u<>'):$jLŚ57i -K]˄DG6jWjJߪ?mׇ5Hrhav}OĬ[{8I=tluX-,ƧvRf}?B/AKĂ+ARh`:~kB_ŖyY&:E5j >%pb5d!) 8Ő1g@1-D*c:eG[}.]PjJ x D^ ߌ(#c\l2mfSYf!G bpʃbT( $,)T*| h v r4 78>!Q  p (^pŧ JLKz"G8,dŚ@nIF '<YAcONR`oMćOuF_zt(]$p,BDғ>tz@&,Ѓ3)-aHPp$SD0 B5ّeܐ k(y$*ٜjPUeQb}{g>>"?)mWvwH|Gۖ}_w9wpenIvٖ~?rK@ö,<I Ņ__PFKLmh2%4=ʹGrpy.6*PLeIe tnY"( _!{4oF4=u]Yaw. ܂#G LsK:VX}- 5"*B(nΟ-|瑆7QNjB |a,H¹t})F4<^a)f 8 pt@i:|YտyPrU\sA,x<44P R.M6PFѝ:)I)m.w"o mT(tOǑc#_#|5pW:M#`\.ͦl@zI^&z9D9emқeTO}mKrJ.LY W!8b{&-p*eUH:LG:NwBgFYYDy\ 1H*橎Tqځ-u C'xl7Zdq ˌ=Yģe|KA`ƻ2Hh9o`A}0GZ8x3ޅ-NE~~n2#2>325%YDC,8l+yv9FT ;W*oUH~үEXb0)pm;Nc8qx!@y*D5ScXHA@^7J2ojkVVKPI` G<=P!PNT>xQ0+; ڮ:ֳ:pG7 o)|EZ۩Y@Nk3;"^k#ǵ6.Kk`C'GC<n&x"q+g3?(R=7{gBa)AǦ\ nOɜUPS~ qF?̰aC7bK bk cCs1_UDnV;V;W{GБ{2y @$۱C2sub ۚ}hXb GXEňF|̋Re(,ovUb) &hb+K[td38ȤόHs%0MF7^i:{e)yuy#צkVU +2|)+d`@OJ%2$xUahqaӬ6F1eF~FvsyYŔ⥑ bK??UYzVΰo))mLk3\4E*m?4흍H">OG&qo`bl4B)%3EHi)ԄcK89':_)>)RY̆Yy,2nnV{~1ɻtۮmLl;<;+,Z~>hz G'CWv. endstream endobj 295 0 obj << /Type /ExtGState /SA true /SM 0.02 /TR2 /Default >> endobj 1 0 obj << /Type /Page /Parent 255 0 R /Resources 2 0 R /Contents 3 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 2 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 3 0 obj << /Length 783 /Filter /FlateDecode >> stream H[o0)Kte ^IQQLM6ķNnˆZ)DZ}sL\%ʁ/ZR-rVò (]R0&9.@&COr`rFdZ mzf?/ɬL$J3j`Z*Xb5PԋR/BVNrG PIgcCq7\ >ȹQ9ZR2~|>uzK"yq*X-1MĝLL`z[\t2=?f=}S=,v2 ?dym+櫿CpCjKߺO`CG> endobj 5 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 6 0 obj << /Length 2299 /Filter /FlateDecode >> stream HWْ۸}WLElQ؎k)J&viF:=g.ҋ]"^n\+G7eiHEax࿱5it3Z@$ztPu4(rP_}.U0Vn{kS˿rD:L0BQ*.n|a PiF,t&4*nZv3NC$ugyL|Zjy,&i.rR|Y^nXc[dPU-c.uCZ@`S%a,i) IqQq7pa4G8ted;Zt֠a ❔zQ*rL^9@@f53tk&aF?om1OҔ3QirC.VP 8^)x6F؝!ZRlkPl:NLq`VIIgAUdtFP"#E=f!eSi{ 4 ǯZ BUWH{*(UGV̪e͏@iyU;y|. *>Aray$l/&>]0F>bQhy&_M NL;d0-m`i ?_3yjW`qt3β$Qo6O.I@ 8jF;ey葫3g9L珰8B Q<TA8P)Nj,ٱm~fmڳ5 381u q4'm\<;ӓ{{! 0!X C- PD[2W/$ OFb጖%-=ڷ]Bul,Tk=# & _x* 歜T)9HB?"LC 3w2 gfVOfg}?Vb$iqSm㟡.TwOvmܖö]i!=@IG, 2iuF5cvNtRVdPfbIRUuZIgWăQ.Gė 4X"{{Tx# A Vzu9؏|1Pq44kcɂK5ufWPyJ?8w;cnxF z)y|jТNd >ԯ2G-1v_ůI,Aje0[ 5@S27 {_07mek)мx$r2 mt4z|zuKy$^3F ıcrM<XCs}(G @'ofP]ua)L> endobj 8 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 9 0 obj << /Length 2775 /Filter /FlateDecode >> stream HWrH+6&ڰh'L! (s83'¢:N!Z^Ň % NC-BO14a$&yCBŇ%n LXls~T?X,Z;Ql85Xl/ ;uV43@*JqTxtGpp\p͈i?r#ׄ\^.+1t}]bmA6JFlsT鋐fdXF7qT@ǦY&&&uS-1U<6'Kȃawb/*x&NOcѸ  vU.NJQ*nd{C&耧.z`bQd=A 6T|,NEf*Ū_c$WחbVAyL,æ-U R$=F4O hOl©sX8@*)K#.3K}Gx+k~>!E4L (jOwFT#F*#="0)Ky<!1 # <+v!4#)Af>"xHCNуjwEɿܜJ? {qSC} D'>==M0yJ촃w>$0Vgt;D'4G[i =:Kܾ { Lx+=}*^>ret“]n k;7 D֍M_;ͩPγybSY"MꬤlK\'IxCeyLf`=&ּLv`7F٘W PºG<o)H{ a1mZ9; Q׻_+8 -C[Б8m[h b("R̟] WQKJ:i]1pμAAKUa`[ z(v:C3蘸 R aw;F%@B\tO/raz=| P2'u^Sf),MC73]W6' ~wG(?5j=^#6Y˨;䗄QFqT šҚ7"pW @a3ҚW J{#F7Qp+H*\8s OWO0kyFj8K($((U R"SbZdG015/6o<=Q<7Pkyj \SRݜAݹ ;%$=ob/0>y7T)$Dw2q/y`3  R~69Ә Y;U_@Y6%o3on@?"_282rMTi=#)B)Y9@S53=g7H#[LyN؆\c.yfDlmy͌caZN̽ٳ]M|,ub4OMm:9lkI 1\,b3*)_Zbӻ?ml1Ő)|c!Ͷ&^rnM|RSg9^΍ z1#\,V _lgBBk¡\(:3\|W1X=4So6[PN:uUϞV Sv5^T2fJ`n39h:;Q"VOQbL񃞏6w^+3a?]&$5.wp&I'FVTwlw+Eo,objE-ka+9Fzyb6a)y;C;{{fM v͜8mitXb'Õݡ?-O`*$9&GlŎD#Vb >"Zff5(kD*kY$֘zJdƴYӐ{hrB( aF~?zX [Z] F>]D?5 v蟋 r#58LrLC\ݝwt]JS&B2 l`WFsNZҶNvghUWD1pIC-;s|_pGVR:P%Qq׬➇qSdǞO`o\RrE pxfalەu]IWMDЗzZ`FI@v ?_ qz~Xt= .W &iikA̕>Qje^@/ũeK>LGJ2j1ku΄Tdm!>V?"A}&q1kgL)s8A8fT$\ Rh@JӮ]#vJ,J;Dd:`ba 9/m>Y=8ɥ=\j|t֠IVD_)3|YӨ+<$b]JD z /Qmy"掴L?U̚k΢D˟p~ߊb4UC$AZjUZBl8Z1*H67~u I< endstream endobj 10 0 obj << /Type /Page /Parent 255 0 R /Resources 11 0 R /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 11 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 12 0 obj << /Length 2902 /Filter /FlateDecode >> stream HWmo8_A'+")Gv ĈBg+;~ 'N8D&yfrpQP,ʹyb|pbu8K%CXe^+~UT'd\7V,`Z<>-PiV}=+R.> 8)߃aHsV*7c?E3u ikB.'\._]GXr:^Wm ]:jg֦ʇ!2Ȇ`Ϝ#Zv0[`S-5(;-.FϢi(>V@m 0`[NJ(T~y^y`@4a',gS] ־yt ĤV..l! ,4S)<7-JRhh pbl.e9F#>-^m]IH.zB)&KJVb@L#w4'pG^ P>#N%{`dLvj5(%!/ivM.abbMoq<{ }ؾ cT *k,ZpWw[ț$#8^~ÞqrGͤ M1ȹN [2-Y4@emej<mME9}}*/"T͎qZGk~_WNB|Tˎ7TV2.s1\x->yjl/usX`Mi-JH@#.¶Sd~DLgrvulΉAA5mr 3EӤ\&NSJT3|RTĵq Eˁ#!X٬3|M͖?An:&2* +k\4xX T ! 0@cēq WU 0#5CQhhB{>^m#E^8jqgk<4W ?0.& 2~R#?vXRZ(SHm%JbcZ#dH VYnwYMpCgxp"2D+@!&!nYFQ:S4hJ,\@-O4]A7w5xp1)t/Cţ+ a/+CĪSBȰ:sNdv=-U9c;b؁sfBVOLLO3Lq|8TLm4m8OCac>{,?\AvL'geVު,qnnZƦNT%4{pيE} \urU.T)d.`8 SY*vxw 毣a.&I^KwqI/cICyτP^;ؼT^()B|GP̩ď\x@ 5S3?sB"])"`}9\Lȋqn[A`slϏVj+tHw\b~L"3ϕ e 9Ebx(oj~M3# D B6tY@F'MT*)S,TP]"["[Hbi ,J?JKfx!Dʹ&C)@EC%b-?ͪB@-j z[?Ć.t0?t8utHo}CeXn2hℊٱoCz)^Q)V$iXX:m.df='cĦYăi@,Y+9!2tfbψX$Ċir2]o 7W̃ӨD&9Q9фh'wd$z. 7e flvCUIzoU- cX> endobj 14 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 15 0 obj << /Length 1991 /Filter /FlateDecode >> stream HWko8_P|ƚ$$w7;]A vѿx/zXNt ԔH]sW}DQ0s#Bb)D~~qdt;_va9! 5wnHaٯuPm8X/vzjO= QLbtlgڬ"fSb Pk6XI!ؑ 8~hJGXIșE,EEY|(<))Ih-sVQGS ȘC6bHiszt9i#0 PS 1$  ܛ ) `f^Ki @_ggl he7( n°K0-$B]fi0gGz^ Œ y=HZN"-ow9#޻-J{愖s<;pƤH w9 bo:iq`L` ! l[$Mw FA},;6׳1aو 6,<% 45̋a^Jk(UN| n$&1< ín7p+Mo괪'Ӹ@QoJ#(AOI54)p>55Uk5ʏK"+'{F3kC:A63 1Pc ك_4IfR[^R7Kް_'!ey?'f^v֚^5F'N15s2!D ;Xi)uypS xUV#hw>fIM$H}7&:#:W,7 ]nsAM(mn~Dwl ͫJb[7eVl#*Tl0^\z\bh,'5(1EY%xPɚ Q4=i-RA: 6eQgW:knPS1I[52%jIc>P^N-fX?¥GDE٠i|fc@LslhöXumFvvp! I쮒]ivݤKK- @ry#Cq9@Վ$[o_暯 UI}֖ފop19?ßHyg"w\3͐wܤU^,bgAL;̷t͠އQfclp s\ߡ<]@be ?9 &m.+W?jc{h*4 Yٝ)s endstream endobj 16 0 obj << /Type /Page /Parent 255 0 R /Resources 17 0 R /Contents 18 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 17 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 18 0 obj << /Length 1051 /Filter /FlateDecode >> stream HVMs8Wh@1Cةl0k4'hgdy7瞬PcR+1*e`"R=H(5JҰT!ƤRO:Zd5~X@a͵ =yd\7WyB뉢Dc-hO5$X@ 6*&]⫴cʌ+˱ xp?Oq8]#8˞_?rOF1,j>a g [!pAŻ0Q1461QK j@txI<5;]X4U~ '62|Jg?3-HU]^\*6y)2\> j~8* Z84lY;5=UwL^#3#HZ y{,10BiǢzL황lœQVv?M|Ncgy/Ѹ 4rYZ4Ko7L}'q> `iJƢJmaFM3n0Pqu4PH 5>s,VmB` *^!xlzWn-@/Kh΃z}na_8syj!u{:u| ŝwnAKEiu(T@1vrb&jt90]6t^~/UA/Uɷuuڍ'\X1. RPbÓUԻ;ld|#vGQYg endstream endobj 19 0 obj << /Type /Page /Parent 255 0 R /Resources 20 0 R /Contents 21 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 20 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 21 0 obj << /Length 4289 /Filter /FlateDecode >> stream HWYsZ~W%z_&e!G$< lU}w\&5]=||n۞B? [w6{)MLK| >zWcqŲ?P-nV뵘{=:ÖΊ*eer~Hsڠ$*B ;'*k(5=_S2HX-q&h2WXx:ǽ2nciJJR'd@.ބPT @Ǯ$F\ LNhqv.|(B"e0!vDj4 6.NJugTv#߫,_MDQSyJI|i zxhY\Z zX5\t-7b]|vv__vR (:Q*RxKkgRlj1ZY,d>bg)NeZLoDkvIr|>\]kuT-RCw@Z1M k2:.׾ ӇX@tV;GAk8}p#ŹyɐC!foqY W[ ̈́~ PogboKS@s*Klx=ax6;Ec1y4[08}})ӝHh+PyR)_d7L2mNm& e͝/*.tY]`HBJ&ܓ+6O4fk;8ʛ.KYC"F+龑z422rIassy%,p ^y</RX @]:w=cxźOYВw<@{otUYZnG aôqdZKFقنrrqU:ٔ$&a϶!3 T͎d'm8ک5?0ŦMKaT?H|· uG-hxR qI zYaE+XA^W Zs t:Z*-BtgI(\UoqAncmnϔC:VDVk76s@!^- ܓĂ3S~C0oI3'&O.MV:sJa4 TS1xbک`QK@Q{0O} 4,+w(dx-,1#0m] U1:߄ ?X+֊ J>ATA=~P+G _yM)FrQOi< †P5J<% g*`B+C*JHYٰq뵦^," 9o:d*O i`pX.a9YH5S1 6#Ml2HKuMEC *Sd%kL6l9c>Glka{o#YF6oBtM 7pexm Anϓ _8Spܧq`Mp\ j0/QRl '~A7 h8 1nH۞jXs52뵇Cf'mm}>&lZVW-mOE"%'`<WK!OAVNR}BGiɤD"hK\rO2C8vdm.ІYUhmFw~Ԉ,!jmgsD`'ۂQkcҴ)[Im9+HwކQ''q/Rp@ρ#%pFc+K@ZG&H[TSQ)*ɑ䆁yE))a_AR)U.Fi&JuK08z7bzM`cWq חMj""񝟚\aӴϹuCYU%`f|\ͫiCG첢]iC^:'+%-bUN4)%5yE-NGq v_8戕ݩׁEt.[=m%`02Q.2q xe6S0J\#È6N㕠mgAVt'(QwW!R>3is`:BX &q#*[}S`|V]TW7aOa_ EmD*ɰÄk"0՟;wZX@Ӧ`{6FpRdep"28a BG6ލP\dWG#dث2<]N/; Ķrri9IdZz.dm%a; Wqit5JIɱMBǕ֥5윦PFRn?bEعU)bs!u4q?;x%%zlOƈؼQ"[8{dPvDQVWGE{}\F;hmg9Vt'(رXBP⺘ /hgҴϹ\gѯmW٬c bg Z84őO¬i>%@Du%1)pzDԞ>;x4/^Ox>͜)w(2Q¨v F ^DaD>Mż$( %c+l k.? ;4smACdWI~Ʊi}W"P~^zJ@%1 rZE, =Eec16yei_йo9}e팀LL xW3(oxGd0{Ľ>x%&zYL$g&<6LII}%%%p Ϥ$G.;#I+FT.I_(q UH+RO/ٴpȍxp3tu4cn0Q.M7<07]!v]1̍v`sTka VOΈQYX3q2Elq}4B^ A戽:.cJLhx*u XJL j31AvIglKBqjNb5Hb+'nȁ!ּ&M5iWÀ84vyrsKg#6vqVOΈQ"[8dPwDQVGE{}\F;OѾNbxKP+~"fey%(G JjmN>f]R, XBr"{9[ sS#S FL='*se9e戯iSl:rik9[)m-甶sJ[9:#rrZȉĿ?IDRwr =QORO?9O+r> endobj 23 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 24 0 obj << /Length 2049 /Filter /FlateDecode >> stream HW[o}ׯG]^c;6"6/q(j%oK*/R՟wBi%R/0l3gΜ91\DZ`"Ow}컎QZTe58 |~ ?ߣɕ?Wc\I@p(vBTb1"~$A92# U8{2;2iv 9# in?=}<ޣi|OcfLu2juY ȨF6z{~bCyLjZ!Jy"pzg ?"QPGqHah= ̋mx)HwqLT<'t@PZ-/ {a?pVB#~HElҺ)9Z ϑ%Bɧ4D C5~ ^mjV@YIC2LEQ"FȠ}C5dON(<]?mmCUM!+";v($:FTPBUblM"2:X<m2>BVbTGy21Q*`E>v$~H8' `2(oߒw;qz/_8ZY.7%N%^b3̀kԮ GL$z1}3:XOZ܊Z~F._E CJ;TO_I>G1O_suQ5@{[L:Oܕ p>B5/$u~XVԯ,S֡bjԑ "I9FyAQ}Ű:;Pm*1I߷^gP*x9Y4 ELoXWS\KW$9R̀j+Zp;n=Ɨ1eT/CP[(fsJ8\;F_XNoBB:ٳAsqA EH@-y2^ 04b'xW"/bS乼fkjMvY1a[ ץ(-@>&4FZ2t_itsGQgJ/&d#IIE1;Yw\[8~jbMLشXܨcUxihA.ҊQ||rao#X^7تos8pt`27`3jgm礬QVhˤxrȭ,OЕY ӬQ]jM&1CBϿtQ,Djh;7#xRɉY𲕵m9:`%K-~LH sse 8 䎭T#Xk׬s"˸\0$"3͊s%< DYMnNS9'̊>/ph)K2c7pj+Z7LTZAL{ 킞5#iWl2(5Np0@/AKTfݷ!.O/~ٛK!ڿo+%_*Ɇ=hkvBn]ۄS#qh3gf;-E]syUWQC|H<./Gr?On>)0^˵/x9zĈWuy}|T*ykڈ`HG9m'kVTU!D)YE҂ ~>ow endstream endobj 25 0 obj << /Type /Page /Parent 255 0 R /Resources 26 0 R /Contents 27 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 26 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 27 0 obj << /Length 400 /Filter /FlateDecode >> stream H|Ko0{wZQ]D/M:E}lhZH8vˬV9; PgV, :8 j S5 LÃZ] >O;"CfI"u64cۑvw~ߪ>:i HcQݫT<ϡ 26S]H r kyM5|尒՛nfs]\nk ޡx&N/" :Y1JoOts7{ F6Mh=ג+Ƴ_'+~7-^hI?ǧ4'Gi{bmRPVɏXZܹ|vJk}y|_mo[:kpI # endstream endobj 28 0 obj << /Type /Page /Parent 257 0 R /Resources 29 0 R /Contents 30 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 29 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 30 0 obj << /Length 2003 /Filter /FlateDecode >> stream HW]sF}ׯG#avaxLm'8վ$}h%"а`տ. $%8{zv^v}̇*aEavw#kg:fw^8 nss46Ȟ\-V "J[uЋ?׿3zGɐD{~5~]_QIR_7g*( xAwuWnضht nB_&>(g,fD- =$U \7baEU]cS:y7BQLնn$]'9 灩I КzTNd+ D>T7ϟ : ;ߺ0c"LCzV±`E i 8Oğvkf&YaQ綁"pM6>*ǏB?pd%6h'?Hlz^MaNd^@T z=O\ MFu#qt9D1=2EZs@\$6K[O$<` |j,[A?AŮ;Qy ȲO/5Gn;4Gǒ `_I/M`%ۼ,AzB e==4/Z2:Ek%){Dn,JJyG`Gxj484x sɐ ZslQk18d;HY1wϪ(|l]MPF?מV2)nAV=MAeZ*Wg97Q3IdLYԦ\a&2ɶs8Iݦ_CE&2`v٨"WF<;ddvkA|D+ΰO& 0f; `ۖ,fE5QFf,*},|"$KӋB_s3Fͻ&d~r}\Qa endstream endobj 31 0 obj << /Type /Page /Parent 257 0 R /Resources 32 0 R /Contents 33 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 32 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 33 0 obj << /Length 3240 /Filter /FlateDecode >> stream HWn}W#X&$ ׻¾&[2w(¦l8/I]OU:u͇&%l7Ɣ)O*bRA6wZZ/P/_4'=2<|~'+՚iԴuՑ7=ʽ^c͍=#y\bZAlo~\b4YPjjͺs!2YO)\s"O&"~x}/?|'66&ǝ$l*GbW!wH,BXIO 0sr+I [$e>LQ Yd-_DYEea$8Ob7h ɋ b4h,yiN0I@.$""H5cY+,N;!᜻ ?aN/r_ u8t.ty2%"q2zFS0d,QKd|xSV %y+̇G}ka2!lQjvU=ymgsqt~]]a!ZsAcƓEFߺ3ʾeTd[m̤VǐχC}k<W0ldaSFSeB/жh=gA$]VM}%VR+2jm=qDFm[DF Qj$&ar ]Grז3XRO+cJH#sipz$!gɛ >i7QC2VdqIS8NKhSàj<̴ERfSasf@dGUǁZKf;C"lS뉞š.pa>;Ap`$UsF7ȗv3ƬRXݾQ $v0-bz^BxeĄ% @jSI)ӫ,.10`?-hd7ptyPk֌oh^` QauPͱTiA<ұ42]E&l9>˺ف~2bݫ~ ie knĖA\6;}ͨ`pU/i::ny;pHN\F IulPyUjÜOփ_פvbڗSz]^۱ d9R3v9v؝pdN0A( ,'YL:v 1×F` M?g]3KUPa>3ﺷEiyѶ Z5/rذ'!&4IukSaxLD,}>DYVZcZums]!|!xt6̃_9teտ`A~j7Or|޸3ޙ^6 Œ"dʏIqXɖ)Z5ա̊cCo27Uw{uRK"jw?Ex{uCrL@wێio `AV7 ł8: 9(7tsCM]T?$')!n#4#|ҿrA'9IÅlU.ssжw@L0Ix@2 yUwP(ln^fp ?G/+Qz^ ~y5B:X?f/+93jEΘ'ЃPػt/{u<.Xz9=xoMwz5L^8wO-fU# 0^moq5x7:DZt*a=yVSy^{T8PeqI-^eZQAif/_g`$*[4Wڵ(Ǿz3*1eA6tשWi#}Jx.vyؠtVSlLǔ 7R}um]#eq_/]3жpEw72[͋\FKc5i-<Tm{R[cOXgce4O3 EՋٜ.j0"?t'7&#iZ-q  ԅEdp!"ae*G!RVKwo_|lmѲ'b/a_!;L\f_ :5zآ0r7>`IJ{*mglAzA\OCo>fɅ`?W{=)Õ̴~KNCLciWk:OQȢu *`wr,lƎBb0\:B}0w7+0\^3]ʬ9g],|R:R%ITGl\M9Deϻ!E#)3w-SR=WȜKYJ3ȍy@}cj3}r;13keOUU^EMF!)a)+ oa5Es Gv 㽙<ڼKZ }9჈O'+p6S%6 &. R_V[￾`F endstream endobj 34 0 obj << /Type /Page /Parent 257 0 R /Resources 35 0 R /Contents 36 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 35 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 36 0 obj << /Length 970 /Filter /FlateDecode >> stream HIo8F<N꘶= Lfl!Oqbyk$@[X4KS(Jw%0Dǯ4 n(5)Dsx/),?+8Sq>S,mmhٴ!((D11Σ]%=*όݔb4K JI;Rcހ2/ |79ə#Gx~iB>]=R9BRxVb@< D)Gϵ(}k_VGXY Ul1iԮB:Z/RhPdXs8vK#{ *Cym|獢DcDzO5f(r!1 (> 8k3Tax %zwyYLWZ.apӎCZqrs i/QiݢځT&x+7T4hu&±t}9j+)o;U}];ɀ|u;m0(mj(}8f ιPCWUiym a-rWT@-- Qg]fp0ct4g:v-XuF;5ESȸS?-aܦm( Ԝ8W* 06t4 LtJxsw;bm0璟 >#A5MA4-‰4gR9AmPsSԩy83v, ATw_5Ȯ,< qB0N :ן3pćj^nk`ceؒG7@*ӹ&(H| Eds̢b;R!j$=;M b.8(GGzF[v2M]7"nL7L^QXFX ^a:Q^Wx8'&|C_* endstream endobj 37 0 obj << /Type /Page /Parent 257 0 R /Resources 38 0 R /Contents 39 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 38 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 39 0 obj << /Length 1334 /Filter /FlateDecode >> stream HW]s6}W %ْ%2m>8F;f,$ Lޙhbݳgo.I'> "$D ȴ}K =/!ܑm} Cሒ[YZC7j'H@,I$?Z փo!*bNo&(&R0" 8o`¥""}7-s~b,s~6Y,$ ܏M\q\)#QmZ3l<-w ¥"bD|ȇ0 ')NB$\ä*UKLI/TٴX:C4dtnŃM[X3vZ{?|,viKٲBQv}G~?›W3@v;f]EN G\}n\@ S;u"wbsqYwV!(N7~GEy=cRpChpf)Mrm1Aa`ӕ֯.AL~42z\Fhص<>p 6cg~֏冗kԮ9jyVzjt.U~Wi0f)+Uq=)[ݽnxqacv?v *HjRkXt5{w^M?6#j*63?@/ D ÞgFf endstream endobj 40 0 obj << /Type /Page /Parent 257 0 R /Resources 41 0 R /Contents 42 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 41 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 42 0 obj << /Length 11486 /Filter /FlateDecode >> stream HWOWϧӐǴRK:b&كyFFD>SjMW\5*JU_j -V3)b+mXճwνD}[7[Jb5<>>?.őG -Z[qee\\X&SYD[ngW/i;  ":6+v8fTBCn#|~v~zu!N.?\]_\.dX^Зm m6$Vn"u Ȣ*kz{:L^/!f&Od4w"$Kr_%i*T晃I$S;Ih84kXhaFiJţ7{(x41"W027m"!ڇqY"M!}HBV'gd /vrUgE_IUŷ9YrԘMcY_܀Jf >fu}:` ,O[!o,_?bp8;Y~&*듋.T/@RW829_VCnq IRMtuW(-Kq]oM݈u_=V}Uv+zXKkC9K޼nźxg)6}S|h.w}?atKj˜;\~9}# -z8X8)Cu]@ঀQ7pz$0-)qŃ7N  ;i3Nk 4}6Q|ş5 $ݏ#7f=;Й}3+gnv]289inE}}ӊܖjM4R\FXBoMS#y?a| ^Zd$Wu񭄋w,6Z c0rkfRU;̍Xc]YV|)í{VSv/?D<Ք;߶M쟚a4O;Q 9Пe U+7bSv?|ٗ|V/ :\k9GE7sJ<[Λ#EЗ[7lǜEaߠkqmχCQ$M 7ع#B ,>X5@?)vkڏ]Fi;{p\գOʇ98'QF&~x\V-rU.@j}B9Hɶyvz0)χpw״X|c`[syt &;}vNnO0v)=\zxsO |OmXmpG)oL ֻٻΊ?7xCh yZ w%6`UY@Kp%0-H¶ wS.(E4s"::^8jc5S/jmFwWcog1ED@h86v4g2!f^ш#jcGsOum(UFɜ]E;3u38`:Ħh\8 ! Fufeň$'sOQZPLm @L(zqI8EI<}SmX,Z(OG<$̉z kӖK pDKIL!6CcBsp8%2(2uP,1 p-y0H̔M\ie0 KM_("F`3)uM)  g".b&i)cX/550̝UBfG,3[nķrbhvNȗ^-3].yJv> UHe&\y:G>t;'p[ji.vD9pӟ>T^x S O;?9N&bEv&d햙b71ʄtW.sB*vvBnbF^P{'b71D)+㙰5n*vnķrYU{y;'K\U>9̔J>Ig+ENf[n>PVwM.vM&6LbGz.1IBdc,x SeTS$NL1^h8]Hef%f@PbzNH7napK̈P9t%Hbvb e"]LbGzpK|k ݣ9^p[ri*v oS$5΄*v2S]y:GvNH7na5I?\,.v`ni]>Q5NI73[n.|zu*v9!_c{˥P9c䩻PŎW6N.S{2\v]yʙc,9g3ؽ[nġqJ`c,1讣PŎ-7.qx`:G >t7' .vK;b09O,O/*vZngQuMp2!SZ4ogB;nT&8RczNH7ݧSnNL1\!uPƸI6Lb'zCn[P{y;'#&WLSW@EW{Ԣy;z]na&TϺG??휐nBSNS_ϫq5DC}b*vqJ`ԟ\L8􈫊'b7\,*vrۡP9cyn&dY&nI:ƒt5'KSΥb7q 1X4OgB;{\.vNN6R휐*{*v)tW-?W{03\3L9nRb%r&K.Z-7.1DStG,3[n.\.vV$׻i*vvUL1\!ubᦨM|c\Q$cEx;{7\ķbiъ휐^-3݄1is'3!o̜*vyg+vNH7ݧb7(Ǭ)o.1g";{*vquxY?< YDrUU=\b AC/]%a^VEfS&Tgzqyt+]}]=IHn>,p&Ptɱ{$ `Gxަ.6;m2IP2nXp&30&{6f^vXE`G8wTҐ;C9gaqf#h &}n$Xu8g?ejqbmĚ7l;#k`f3֟i28nn7(-[63֭ 28nnXNKM4 vUgBȤEփ&M@g&nhuohIvck#׼I$q]7WD3ZXN@3iX`&/5w;ukd;`ݜ.SCFtYD`zB&1؈5uNM4v;4[wvck#7`:\`@1hqbmĚ;`cml;`ݠԇ!ws#IPG3[puhL-y2fSemMPfcc# h;-^6$;WP/`ǶFNb-^6D3VtZ‰j4M0vPp֟Y vlp'AM]~^Q[ym헴ξB]]:EN+ca=/r!Dgխ(fwt 5͍kBr KDeǡ!4Wi汓i&;,v$e<,c5T֛zpqłIdѶrijy՛NjTPߜz*7t2Fj{+(uc?V|s}iӖ6m$oB_'6׷_xsuWo=N•a6w. VvyRL_';q=8Jȁ2N#Ne.!:TIrձCQñtT Q1;^J4{qvpP'/?ţ)s}t==BQ8?)MKgݻֽ˽{aTޱ\O6Zh>C[GXK/F\Xɝ6wBCC,mtR܆ v`R߷_<|r>JO{߈?̙3Q{`\YH9 7 Vٌ„R*;g.n_#EgxuHأNO\xpɆΞ.}D(KU"x}|܁ET*(A:e۩_YR~z3Bӄ[QO p[ XSQO[S!6J[O12ɌJ' , 0o4-’ȉW.l*af=Y/PYMS SUTaao>$R r< aِj͆hI:qka]76.^ax ,lإM5ddrE@%bR &&gSCTOQ4ԫHGi󣖪}UֵKb{ ۢ[΀JpgwϜS٪`A8fJ j%0f?S8-9#(%~"]lqȻJެ0kk~k*ӒCϑѪ0Du+!2x?OO«}%ef1Ⱥ/Z9+sĴu@ohk;ʁjU&o3_&wft[o;N6G?N7x·.aHqS SFrqAzu7-XlJbj"ֹ.kM`3Ƅg3!Xh-M]mc56Dݼa¬@u9'3ݦC'X#+Yִ WLة;8ɜF.mf2Ft@C,N1UL;bch_өe8f}YJLbj"~*Q]|DuY]ˇ|@[EJ/kC4Ƨo┚_[*qYHݑynU3Y{QvIqZqn| HUY,K$K]Z,mVJ%hRJh'fu$&'W(ŴY fڬ!Ғh %6kw9,t%ͅ:xs#ܼ<iqS.dnwQ+5ӟqknxzbf{=yXM1RGYn-0 [Gl΃cA[7R/T;\u\:L ۗ$&b1Z\wb+KŦQ{C^iO7"n@5TJѣ 90k}ɫ7/$z9Ǚ&mͧ}iZA_e }=yvu vM!?̐x(UC'Y&8[ҽ(Zgk2zԴӍw䓱o'Bu ['i\l,p7ٖ(:E ٌ,kbs, [C| ' endstream endobj 43 0 obj << /Type /Page /Parent 257 0 R /Resources 44 0 R /Contents 45 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 44 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 45 0 obj << /Length 2766 /Filter /FlateDecode >> stream HWr }W#29lޙJʫ:cHC64ղHM|F -Vjk֔-nn gÂ1O1 "' ܈펋w+\/Lݏ[†nv~}\FX }g%۾V,ֻ8aI"7aX?&xwLP.:uѝŖ,ԉ(d>xO0#8 oG{ fun?m>^߮lޭ?owhp> mawC,d@id6QqPv|M4tkL ǺF}*(WlLp]Er|=-;rc7hGyhy8TO)0,7xG=qr2uSo(Cj35c$QTg71{T5,Ǣm( U㳹lZP!tԈc vjbySEE#[p+9aS<&yC+Pϳ-;`"# &A4 *597!E%۬ʑiB3Uvh4zJRzo8dJ'"y+`gUhJ6 WWLfZ&eY[/ !T u]h$ƝdPNmDVrϮs ZmŽ4ձ(c S0-柠M/u.u ݬ+Ha% JVY}M* f>P ~OR>S[>ӹ}+@9f0E:5 ?2|l*InR;V`ĘkgZqw%֨jfT>uUOzؔ]f=ICPx$li8ר?3,*h|:t;%⊁<4/ hA,>sO4,{KQgs"A)(Y 6[ 3?#+'[Ɔb/C&]П :TU7\G̋sGAZ=n6MT߲T5khzQf dl 45%4CpyJL (s`W1Rqs÷ӍJp#r#-t׳b=)p{S<}$yYK kLdx~8 _OxaRVl "'}F[¬@yUmx0e|UH|2s"%cV9,38.>#dv/eID6zusE^Qj?=.t2%`0xlĻ eLh7?QqދCQzs91Q߻ϸ)ٹө,r^TR}e*U>(Y5j { }kC$ ŐM]6Ame%r"YQ ?.gPڊ% [ջ+HB?xhb́]_*vH+[tTe]G{`+fvfe ?qߗv/0bt1KX,YZCV!#7%N!fBFj)ꯔ~:{VvwYۛ35߶{R WO>cg3MCZ0WTOMH t:C݃@>RIp&ɝ ·T[&xͶ2vx>]q'VEGQ'VIMJb^e`VWIhbl` Nl⥍ݢ;H=  :0qewf:8TWH%KY2kCSnN[Z ,HՐ|W( /=Hé|ЅRq!g7OwZ!HyL 6O~&;(ơ~ ]v`D$ўn(VYڟ.|^]jy::zF"{Q@uą|^^tZ\! \zΊCHƐUG0삇DT_}b9HHM $z({ ybojȌ~Ȓ7]37Ly8fU5+$YI$deR/EΠK$Xrء7nSE%ICGqnd!%SqJ )xpiJ :Է]6!W)ԊE6|xvE d }ZFHwC|L铆;/U֏Vp-'܀4h%=j -L0 !\D` endstream endobj 46 0 obj << /Type /Page /Parent 257 0 R /Resources 47 0 R /Contents 48 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 47 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 48 0 obj << /Length 2908 /Filter /FlateDecode >> stream HWn}W#x45kigy)j̬$jIF6MU_xHذ(7SvNFv.~ W/OvͲIR()-#_^4 D?2x:"wsI Jg%l_*=~v3C$Q>a. Iw GNE9dl{<2){1yvS\TE+rsGlw~ n!÷Q{»˂h2 PWEY>>APD1 (u9$B_=A="vDh yI -16Q.Sg#G#>y`!R\X}?CɿptOhv>EvȒBcruln$}J\Bn1~Obf9eIIV"M%_?tv6-6x]#/CQMm Mx$ezޓ8`F5aZ@~/Ivm <"XLOsWmɪ~=whnwUjYlV?-iXKhHcZ= gvB DAT8(y5~T뭘 GO['&|J3)5@>1$ym21xL(,,xI͘7騲N,({J!յ8LZkO״x5e].>c᷑,&$NE:ZЦquO3X$d{lׂfpKjQљ?iPX9""H]ZЛ@ȼШn E%M6up>eB@%pXɃF1ݼ a&4P,t( }є)սcidNYuzK/t*$XٮV`'RVrLJBglҵ)eѪQl6&g|*o V'8A֯3%/r_^pcO8iVۅA^ u ~w|oȘ)m)p ;~\-zsU*(Qhч0 o 0 TYf5߶4Yb7CKfUoGHq ;Xz%&JlOgfF>.P gi x̿bם ER|$#LkصF˟WGƻd\Z 1{k>>. 21H6f0|Pl~'q'QwH 8 A+f*j s̼V`S[{=*o­Pz tZvcZCcV/ zf%mMm̚)Hh(#DΉ5{G6U=46э! &b-F F :衎ӊ@Ȃ].9@ J,@%Cf Ɍ-+ּH#)!JUsbXѥ$HP#'qe(}s.׭c?< FZ> endobj 50 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 51 0 obj << /Length 2452 /Filter /FlateDecode >> stream HWr+Rc 8;_YwʩK3,h HBRяMO@Nn|^.Ju8&(_Ko1g1Gh՘ 1jVEޚlc+MyW fѺX[865,:/')Cj:my(りXtU) [kr{M":֋9<=ܡOw9Z,RTe\~:e1Mq"2/d@F,+97dZjE@YId>gC<N'HQI|ESѤ XW+C=)I'<֟fO&z=1X6qp)dn*X8z\n~,mD-#Տbh $,Kai\ͪwyRPQn|C6yW݁|Rr`|CG!2NaDyccԖCQ/xXRB5$HB:Brtb̶yӠ|s销8Rs0 `v/}9Oڰ+9:rSi("HS)wM x"i׀&O}Ws#B[,(U]Wy  `bĐ/p(K  -fPK-Io3Fˁєq>zeG;ii$KEY=zM=hD*mUo(?|h%`ww1!&݊&jI!5CȅK 2Q5h{! WmJrtNf/pM{Z<@N"ެxicPQȺZ* hCPӴ7@iӹ\McRj8A;bd00S'ͤ?b ~PR N@iIULihbI@ҟΤ~}Oc̛~b~i~3IQXSUU??.<-=DD@I`~;bdrc/ݳO&S=xK43k` ץE#I dv12i0db7jNED]ኙ.6nw&F 9.`Vxvހ\0`lLtq5 E8AH  OoQXAx\6Y1tdh\QGMSeش˶h6;U豂'q!I(Ut{'0':RC^`Ӯ͘j葔XJ{HoߵaNJU[ ^"c#V ]վWmBm~gNp1z *ocu?bN":3v=ѼWu!\tΘ%pI %gZ6z.a|B!(jaR?,tkI~L鰦E+s1;Ѷ{{kXdrc9$@*Ng]vo|k7*[v҃)VkQ(? !BHV>O]z]c/JS4mm\ EYhإswqO;mj|`~BE`HXh4!kԲO9 JeNGo }P!}P(e!}tk}P&?Їcч߫_T7NG2.gGBV:OeQ¸} tG72 Byؽ@VV hx޶jנv)DPํ@5b!Zeu7\4mN8ݑCWc_il~#zQUgwض~ N [H.O;&QuaBodw@' ?4pzQB%u!5jui7igu+Rex}:Ivd@~9PHlR?0n@1q@IF:x?3t50$ÔFAPLe\{)by =/*G}d@7mqbA޷U ZiIl ]AE-fS/c5HdٙkБ9q& ^$D#`Y69A"0d]olB|_C}dӁ&깘**QSh祪 DJA\}?քH6U?zjE-|:t2'4Z}mj֌'|mcf Vl&8@QJvz$ܪh͔疯ۡ1JM?B  endstream endobj 52 0 obj << /Type /Page /Parent 257 0 R /Resources 53 0 R /Contents 54 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 53 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 54 0 obj << /Length 2014 /Filter /FlateDecode >> stream HWMs8WHOE h);{쁡 "%8ڿ(")%5t6Ma$]͘O}N|"+$—$-goo$|bj#f64w _fxJzjΨEm44WJ>[3{ IDcIcRj>b2=}P>qJI8p|hM1|EֆV6@=N94 wO4OkӞX+f_ZWzg}jC+u;Gޠ}qL Q(T!fQKjA$Rȏ 5p@oUmi'(o Z l- E` m4OJ=HM$ݛNX#Q/5J oF!{oX =v-A):I]*d 7Ő]UgDXlB%|Bn-+hwFU十 rIl 8PϬ3.ֲJ@sZc/}`==H\= 7]t#D,-{i4TF9]>xpfJF~Dz2Q`TKܷh=M!=TD^q01%T0`ÉX$$vNnG厂HL3 Y5oF0Y2MQe#`WH K564?YCi풴֦y nko(ÈyvRB~]lw|_*ڴ#p,o4Y}`* xRÀjÃGຠE[e8YPaQ'c#k_#lg\q׆2N.v4j@տDږf`t> D^74<7TʼC5Yr7Dj`@}J+ q^M 羷Nc>@Ai wTxZo=G+(MS@?f{ 9 99 W7LYqD&)pKyz$`8,_ۛaG)&I8!HBރ?>R<';|lw衊팦:o*]oOרhL*/}<iQ:Rc9F[qT+ͮ7j7PÍx}~42`mk]L#Kq?`ǀ:5V > endobj 56 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 57 0 obj << /Length 3238 /Filter /FlateDecode >> stream HܗMs88RU+ @-q7'g0VۘuYWg+|C il s1{}06M%u?6кMUZ8Mӏ`&lBb0b})nMHGR>QRl-zM!,MR̻#*|mm jU|8˛V,b[oڅ^˕8zyT!u?vx.֛ Y,|s'^.n]j5`D<zW0n{neLy!lTΏllj?!WOdXWqr0+V.8`=+No>3n;Iku_mbRMl~.wJhi4(/sVKnwviCA|Z(銡qv Avk;|BSa\l$ *M ~i-8}<^pKVv%p.@]ۉ ̆Mzy"櫅8o7 |7Ҩ*m9nX&ʃ@ݗNC~,a !MROXv&X@̈́{*qSeRpGUo%XK .5u3\|rhp TlV~gM?A՜AR5]-\aew{НV܅zD_3U..t F-Xk`m\4 9+-&\஻\gcͳM58҂C,pL+S} 56Њek^1IXFXl0dws\bkQ*2\úH;).)Ѽ@O 6v H)-P 5-&5S e춟rM} }Y lL y1dp8 }emSuXx&"{4J7P5P 8-e 'n08AD U03MOAOO->]vV})l;$.+N']˻صPp+5 ^1u߈h@tJS5Iz-k3J+S Xs&ڽk,luljHk M 8%.bx:ca 9\uiBc]&T q.&ܛeh&`hvf_hZ<Lky%d %lSC9|^ؒnGT }!]u('_' T58AbxE:* G|P[Є0=*c,"HiDr 8) RI (habEr [Wi -h2\DbGå?śpQ:>xgXu]z"uQ2(ۥPk]J韈! ȩkC!p( i%)"Z@ hi"QF-M(JREY hẕ(8lLбH2AQHk٘J(c(toT Q˳{_ξo3'\O)SHPB tPBf蠖)I2!:(eC+tPA7Zo0+}w1`>: E 2W ::ejhq]|(.j1MMUݴ?˦CN37>iσvϠl 'F_beP JRA61(iĠ<- Z4%2()e:dZ&=&VJGbȌ9Yh3Qg(zVgӃσ~gw?O'Os4y 8!JYLr!`oM.0R:Cf̱t?j oмy -ޟEZvJh @}  @@-Re@J3PV& o^hlu m}ቡv)M$[ֽ0hBtd:8!sMg9*2GԩtY @.C+^="b6ljza_q-vV L$U8~ڑ"j{*q@ؽg:8h8Z;xtPt+URU3,X닀О/ {0G:t lvyŨMDE?mT*"M NW X1 O8(-t&"~K)%"GkX&8(5hP$zqC>Nn3|2DQJz~w endstream endobj 58 0 obj << /Type /Page /Parent 258 0 R /Resources 59 0 R /Contents 60 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 59 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 60 0 obj << /Length 2191 /Filter /FlateDecode >> stream HWrF}W#e1cjkeUDrAP.\ (Y 3 :Il 8s$#vlj;Ć?)^@HR.>^d`U?y%|ݶCdӊ^X2Yea|20s%A0R{q $6rc:>,GQ#1}}C_~EH;4tK6~FMHB bvHy sBq1;JN |vWC j6+f[7%)E$?8̇c;=VX'Wurw"5`2Ĕz/4-t.BY0\К_9C}z=} yd/qgDaS "m:L>y$o'h{6pJ) yS\`"D ́i^wL "{BGDA.#`Vd"?M'h2rII|X`S%9깄eP|Kvci>0ڿ0|;xo'T·i;3YҖcyZcy1;rKSs5.c'XSz s>k+SDLU0g #O,0LI7]7^\U:% ݌́M#`Z ZjA)#3+gM/@%m-#/Ox ldMbpo9ͫ=xi L?;ϖ5Ժ-60Q+g_iyD6KnfD~V@%¦SXS&- J_T$*Fˣ3ƽg΀8RELv 3tV@!},r7ʛezİ |Bo2A-4WC̙UA_ V~hlգUƧ~ & ~÷)p˅7(d'h݅ݜCHAĨ>z0J\};Q[_QluZLMIjFm<8 WL揜K=̺=\“g^ɺB %\&XM9Gmwg!ژߤ^PG!3Pĭ$mԶ+Vv?baG:?F]ןl<0Xy6x?v/l"3/Vba~7MS&:Ẽ~ftYs@}9" ئ'U ) ˩8z;-a֎8tKDZ~hn kj'Xlqj5ȭ {_p'.v)_Vb ;1k*kF+*lFl6"w}suwKּy\y 8"lr`K eEE`po3EQͱ}/|?QLpUJ݆uC:׃$rloOUJQ7⿾ZlJ^p9xޯ[7Inu2x,ɼXߠžzkZ*:r_xa1M F+ r<\|0d6i%2XsGqC6kC3vz<#-5w)z5ݢD8eZRmGlldV/qBcEF&xI !)WɠVW0eZn)-'e*G|q EfXF Vo"mIJ 5 #ml7ჶ"ܝl?\0J'CC786>1ǧQ@Br(Mܗ=o=/:1=A eSbc2,ԋ!H䞧򗺁v4?y=!ާ$v,~j| endstream endobj 61 0 obj << /Type /Page /Parent 258 0 R /Resources 62 0 R /Contents 63 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 62 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 63 0 obj << /Length 2833 /Filter /FlateDecode >> stream HWr+Sd 7feyʩX҈L*V-)" xJDs}n9^.=Dr=!6d×z pZn'׷<@ /؈'A|2m;DD=x[ޡOb9JőWl˧^mr;<{(q l[ lq-t#[?>|B>ޡfy~)liܮ+zAtIBdB6B;$z7 +j\Fncǒ1 o뇒6`C_;~EH— c]4X zc,2Q1ښ䞄؉ 5@j $' |vKH CŎ*J ~rBҜVi#J6Ep۞S-=o3 U1)x~ xmVLXz6qL$-ֺ,['L6:8=#{ul*9ڲ| X Q+ZҜBiI괽,hQIVN ˃IC??1c`=[bvZIM7tU,C9VՁ)B &hl:([UW*;!8'^|XQ"ubS%A;YH{^L:m hGKb%5L\8Q۞AB뜃##JfJ2E\삕y)ɤ6sa4}4Oo4G"I=!6V(VE#ό<WH+]Тگ 6G:`&IX߾r&FKWS0^1DFy$P.Qq-oO,a]lm 33RK[xe嚠<xC.z#aԲ:1nC$=[{8Y^d}lq<.,5I!tk!;B%E=f{C2>~US]߇dgȭngEL ?}p ]|s%-9Pp7͏ڎSuʲpzzꐣc}?V8ŋ]Zp"h }q!!ط㳣DQX'r=턱VZpy* ;6ט( J~ܓ@9vmbjNpHqǺ3b뉬J̕3ѿJB-)P(xe3A1:0 +]nNdUyu?~KeRBtJ{Bu M%SB+my(jaWJ!/ɔ|y8<Hs) ;[ՒUk=|O5'"޹tB!P^8rO32l3vSM4;tSѡ(KaCɊ),-SIx3Fc/ fc?B]zG̡\| Nti#pzw`ݢ!v}>vA.xGݳ C.G+R4܉p~D~F[W˞;HZЩ.tkU^CMԧJef@hèVƦ|ȉcPt7vrrG{I[aи es1ñv #RcP^KብmKsׄB^/#'5}` ''nX;T+X29T! FaOp7;p{0t.0V&UVǑvMZ|gIy3<5;Zo5|#*KPgA *ޚWvu0W{-F7\sq Hj^0~('ɿ~bbju ěq0P$qX =t^"Y='dK:gA*PX -FZX9-5/5L.f^Gر$]2 -5Ș@ H'&"[^J/W߬OOo˒ebEsjEY>FJZ\EsN6X nar~xn(*7H*mF^`gY22Gϖ3fRK9<_/Zfojvy >͏x e~(`v%8Q#S{wA2LHBóaH֦@wde]Z3V@8 l S GTC*Bml֜J!ӤrLD[\2jUTlbeq_A O4?iV4I}y'L8F=#6^at *\U(] D)> endobj 65 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 66 0 obj << /Length 2829 /Filter /FlateDecode >> stream HWn8}WyfH[vLa@m-%|YdK-=fU˓˥$,N8,& ~)2d)YnO^w)YuFU}-'>g,#>rx|>$OH.8ѺZrj۝kYCD"yJ8e9i 1(ez.}(h  ӌs1fh= .?'~y.,/_^/u,[ VqԉdmByVx!- A/l,g"6EB<-6h@]q5XvMwx'ɷ7]׬lVȷކBdtUzۭ!>T =tzZvV=D M>-Ȗҝ`!D"g۲m,nn?B-u}^J`SkN^}[UP#K@G՚XmJG'FtnU!o0d"M4U%ةgPUemv/ڶi;,We{ٟc=d#1) N) 5)"bN!B C!$G8-ߡ]N9S&L ц7aM%` B6 u@BuzԴ<014 b79͋$Kd --[꾂dg-xc2<6у-`MpµDBmq{%v<[ȎX%V!k"s -pنfhC# azΠSx8g)/@蟪mK" <FFp҃+|(TDdm  C{KojVj`j mYCC҇B#_-l߼"C5;J^Wv0סZB+@c흐ggx}ȡWkvuU/'<ilN؜e5:^C[2@"C"ެVR_aRH%%ڡJ&4z΄䦋9=C!siYL9慊(QD_Gs!S>Lgm|F7g3u|F bE˄BY].~:DeAi_p;R(tIL f@ծkV{~ٯ_YƦ:3^:  3@g 茮!:{]-3Hgo 1Ve3NZE_6@ge&LVZS9 9lnS*5Lg<=r6DCNhI9'B6>ꊲ0g7Yu p!S*zBR)E7\Vou8 ;b.ARnQ xAWjЫMG^`(uu ECd7USK`hjY?CyEutW|L8'|0] %$e_&B=f3g32g3{}RN/':H.qk]CRD߮.. ]rvEiSfz!y]5 ezMTʦ- b-~LǎAqWkph̡\qќ k:_}2z|_$ :`t[݃EBR8KDgpr> endobj 68 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 69 0 obj << /Length 4626 /Filter /FlateDecode >> stream Hr[7z ,1.']%3^,hX"5$e> 4[Ų+V?h4>4пL.M&^5vatJ.zMTMfxg7cuRhvn.hΏB}~l'\9|S2j=\|er}s.2H]Nw1cP0j,32m.b"5ՇnOt{IV۹06t&lưcѻv}S{{E9ͮ{ͣ6dRM~6.F>`R_c/ф]4%0j~(Sg<}ޤe4eۖ]`d ]>ǯ<~] v3n?ޝ86i;_-7#ԅ~AX{\?h܇RRGOLiqDe$"S;0xk(_8\[6( .[su*]Aʯ`[`Hl竢; @r.I N >w9`Avu >P{.u.MС W>k ^4 X 탢H˷Q^*eVHT$fT|~ UH` #@ެy_$!`tApѷS2uگ΢|.$,\ n V;kfFÐW%?Ж6| ::К)GlbށV"-^Amg'<^Nwnh(ƻr$G)EdNqA ,Er"ȃV ٸ@'okXM_$?6QJ*EN+Pal}Czt}^CU7wpz2$<@|%N}XNMhtqX{PaQ6W)R^pi`j=Ԃ.R zxKJw)wO]'AR8@k%ʢ4 C:aPkIؽ)cV{%˩d֝KŽr.T@Зa^;ExZ:Dڙ?f~RMEY1|Ep 2]< >Ļ?W0ZάPQ#Jhhz*}"\gR|*6^=tD jKd 0V uҚY}a|N<}f"/^+_++\+]+]+]+']+B1)Spz($Axy"byA\l_dٞi9""%(r4DJ vXX+&,+R4ܔ!xG0[-7q7>a]0σóJ5Ҝ`ZY8X>vZM9m9P^`Yq5NlX mI}c|HoЁNJtV,@HṔwTUځT=Ӷs7tKo5"&b$)>8jd,} ca&9m9M0*2jbʁb!yC vvܼ>z ܔ^Du~~wuO^վZׇ>@}بv8)A XbI>_eg@Mv="A1ilCPdϐʼn(^(A}cPFML ZXౄNm.qvY},b\lכi]=Nm?:YY +Tmu]-_ 3?N X7].`lr$SIR/Z BR6saTdaŪUA=>=ERp~V9ϟ&G"XO!#gNzjXb}(s"A,H j"|,gb4l 4[:-W7lqCH"ǔb !u"6nR[V[DŪU%J{і~Ty}q!Hl|.J=e}q5F Hd@ P`l2D7M"V)Vx,Rd>zBnYazzM׋DA ͤhH.c=8 eP@qVhFYcl@ъU$`YM(`̏HD;OGF~R 20P+ Q0`|iPUV2h@h1fA5KX,#1 ]L?LjTx\oSEG;>$ xzN4ԈX z̆xzVJ;?㛖|m{JcZ]V6r2OQc^|Q endstream endobj 70 0 obj << /Type /Page /Parent 258 0 R /Resources 71 0 R /Contents 72 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 71 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 72 0 obj << /Length 2739 /Filter /FlateDecode >> stream HW]wH}ׯGj@<6{c)M`ԲAdhr'MhVխ?&E6bC.OsrMGI>pQK >wHy@S`$Ůgb5QgP(rDRL6V7S<ԕ9hm2p9Cp">qi LBەsS;Wk4|zq߾/<|kF]aD.gl.{?ZI />#191"<,P w Bz@?z#"AOiKWqL=XSؓ{2F{Qx}PQvE yml\lwtlCxLڢ=?$͉S_m_m<=D.xG' Ⱦx*}M75כN~-h;pfo $ rcRk׮}aM[U- 4B!듓5R+l'Qݹ)j?"k K+ tz+PP< Tԋ4Gxy,S\.HR@m עH)_!SYbG#2zG?&5鏆htT)$G]Y0E%Zkg]~t9'*deÃ$?q igrehÕTAIP^V%Q#7#(.j^J>?u0i©0^ҝ5<vjҜ]~^f &NHo9dd~ Eb1}a2!G-&mcQGSlC,or/t&58ZB $ha\?1< v:AK(xd.rDA,KѕS S<AI:j!MgFOt{j~{-<B5hfW ;@I ƾ*)a)ս׿ DΘ;e"q@Xh$H(3aavs; 4O$T"XI7@'eRܖ:lzTwҷzhZ֮.a/Jж`TChzxjD ޶&P *o7\CojQLgP*R +,5;٣.j!e*[L# u|obH0IȴDY9uP#I3'٨_h)H;yH2K)N &K]d-IŌQjC*h #^RP=kkŰC0҃+cٔ\,5^癈)#x$|V$q1(Z4li0Oԕ^ x?V!-biHСNQ3]xeP ;={툻ʦ+rjD;8|a Oc7{>&Kp v8 endstream endobj 73 0 obj << /Type /Page /Parent 258 0 R /Resources 74 0 R /Contents 75 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 74 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 75 0 obj << /Length 2951 /Filter /FlateDecode >> stream HWv+z 0; 8ʉ%vv-%"!JoDn<Q[]r g |I 8ZoQثREvfY+UIVUΊ=զ(,j l>nzaVsJhy[h{jM`ةU΁ ^ڔCl::ecoC^ҥ=:oTRyLeiثߜv.^rSn 僬\>!=d&qnT)&Ƨ|;R˝rQōX]A@x6PEӰNY5:{a,yqIo5#܃.rw}inl6vGn蘨.>|jY*Vݲ=l6C8',{b'GӭSS6;~:ttҐVJ&̵<4yǮ}q_/.HvvBI!*Mךy~l)&oC7.0kG]4 SB;6 ܇).)Z̿}~PI-g3R9qz39}\IH[I]t&2{Է8U4 Je&{8DX0\l=һNWGVo2WedqM[E"״Ed4MSCFL SkXNTFfxoIӤyB9},5fSlHWl z 㪭Ԝ@6RQO~FjM=TGEF a y*1xK<':?7dM=:KYޱ^pFḵ,06>̶n"~f>\09[ ]Z/쬘,lnSƶYɬt{2кݶ.1蓠5jiv<::EmUf4[G 1vݶ6sIBV66:Oh4zz[od5~Pg= B# ٔ/>Pv2~9n^N QGW,Ux"e}F>)Pݶkkؾj,6=M"`QpySX{8g6fǺ$9j&i#cy'4i#iz|VL EH¥{+jE5Imo8HBom8x$ITiN0Ki*Dj Պe}EL{c1;oΕeZҰ&eb;/ w@D<=J҉8{bv}( nkvزN6s6],o]BrRҒ+|Ӂ\ʼzTz-ЖqЭ-Pj/(u[1Tsգ-GcYpe#n,a  fnawE5J@hN1bvL^7 >/[W'; ԩ.|o֜ -mCNN#`[:+i~"B@y&ŝJk,mPKkӈژ]*V1Ҵ?qsˆʶHⲭY0j+^w>ܙ>S*jŀm'Ϯ6$F ;۸-4(?P=+[tY(% y*+Bh#Y=`[6qLgAL`N endstream endobj 76 0 obj << /Type /Page /Parent 258 0 R /Resources 77 0 R /Contents 78 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 77 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 78 0 obj << /Length 1175 /Filter /FlateDecode >> stream HVr6+HUE0 Zq*S2+{@S"W|qf,rQ03ܹ|71?vo8bwյTf/><)g=/K%bW> ݵ, J,sYB4y)8@~ў)(z΅Y3X`F?  ,3e%K<s|s} w?ޯnwKxvySw0N/BFOXXűA$F7M,b5̏qvyQbH]CPJjt*;[VvI0(\ 2͏l|U]W_Z㭭ԇ#4QEG{28;Ns#Wݭ<$i3泚uj Cx r|pqFrqKw[0b:{uaS]OM+X|r  endstream endobj 79 0 obj << /Type /Page /Parent 258 0 R /Resources 80 0 R /Contents 81 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 80 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT15 245 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 81 0 obj << /Length 3719 /Filter /FlateDecode >> stream HԗKsX"y`0ΖS|KbDE23CBiKܨŃ>obvl6fL۴jMmfWW:[ juv}թ>ty?$ԋ4>8_VVj_fy t&4m/f{0kEi6 WWٿCӇU4x)M%w\TnsׇoToãt|vx%u;!~ηtmט~%Se+Rc}B@-hOU``}\]Uvu}y|;i\o6\{T ߜU_TȳgϦojq&mL͟/7!}?KfЖӟpXTGz~vN U0Xy B]7ˊ'bebLUd1fyyMPn#$g|!H~o>5n,Ͽ|V st#_Z|W&yOq2>(3iE[)m\Z:\DoTXh=5`} ‚>&$ji&܍g"ga =(Soe|t9-'7BGuu>_7;ύ>^|Y_pӳ]PI5)Գf;C{d;V=eT1[M-K7еal$W)6O =~ID%KX+ڎ&nޥ)mh;%hxi^ph}D-0_rY$JjhRf8l: |jiZ)r:gU9"%ssz8]lnx=b mz:G/ٜy-*o7R%;E|#DSQ\1M 3MHrIRb>TBejPBgUdv*Hb 0b)BJ@h"7 tz*D޹O1s3#JeFT*Ua:2(f:QqbF(L3dWeQC%J(}b+&˽ .?9MstO8e>' $$&٪2 JjPBkRUւ%[,Sd%̗F%)@*I:GR~}A*,'O:|(PuLѷsUN٨pVS-WZ 2)N٪p);5Na06bՒV%ENZCNizq::^?tT8}NKBhQ!*[eF.reX\h-U!;\vj|96vX%#J (b4+Fځ r"x r #-{ j:8eivBk4[5JPʃ*Bg4;FQF Qlҽ]TQATQ% E 4UTiW i{0O{?;Ϫ^3+A}OqjB)VP>ˠP>ʼnFS 0Y<؂.p+u8tŭЃo߃hmVظQ1}47qZtܨ,jݣ1K0YZGw _٣ B(ɲԛEmĽBK$ $Ŀ/r '8ٝR{z%깏gʆ"Lo"iNpD2d}OFB6㰊)< YiiGyBPa-?,H+zȆ!W)gP5 t{Xs{35)@SPzV9E$Fb3mɅP莘ŷ$*˫ ^wVTt=bʿ^Kt$+z܋LN"g\fhҁӴTY7WWpnP4'&YJg]9ʢ:bek EQ&|z:=FU>>U ZZ-zdA#Nj7%|:#>X1X> endobj 83 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 84 0 obj << /Length 2185 /Filter /FlateDecode >> stream HWr}W#`/~j)ũQyR[ 0$  V g@ SvA>}٧8vx=Lô?)oC}>_0A҂@G f?,ʟpY&,e#v?fxgBaPzS#=}z~ze|/_cvJ=w= )cf L 8$h z ?6t"ϰY>>ض:^ xYFrz8AĹS6!J dU T$:( ]4 ;4#2?dH3 .m[YOزoPE4yUln$$%|JsED5|:KUB&̿Iy` /}`x˛-4 UkRBB!iUf? Nu&jԿ|vy6r/(Qpq<`o tmՁ*slWNV:ȳ-tuLU:}HF4Nӝ@kGZag;P발(܃A1e C(ws\-s`XM_Ba4A M7+JDdA#AU$^Eɰ'0V]w#{rC*[#[K’f€ò >b>6Q#J owIYVrYWyDsBEjhV4(%3:]!N Wq=5q Im Z{w ,j705y,t\Ӡ ERw_+ jp\!) Os yDvI&ۋh{|h@p4v(hGU+ZFRT !ZfI]Cا:f0BQH]59)?' EB U w1hc}Xܖ78!Tǣc)Lym 2LQ^HPξ5 ]FΘѲu&eC`׹htU8<.*M@Sws能#w9_p# ٍwZ /yqr{XGOZ zd]I(UF}ۙ]kaSS Ԉ aIk"I'ЙOev.,۝I?۽Ai/0w2/+4ixxtmaі~KQsΥ;dKR_ɦ D&AAFܛ <ů*/Aimt8k=qM8@%fi7ŮZ&҆@T:щQ~Խ?ݙsUv^iUN40Hft To|xiLvO)Ѡr5E8Sj‘xh_!k}_x(+G|8: %kUfE97g%*}:&5:WBjiM8`5I[4-u:EzӅ>M>6kPɤo] og\BCIT Yܠ74lN4O7,;#z̍.&a[?eAҜzJR䂹󳖪ʛ˶8E J endstream endobj 85 0 obj << /Type /Page /Parent 258 0 R /Resources 86 0 R /Contents 87 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 86 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 87 0 obj << /Length 2413 /Filter /FlateDecode >> stream HW]wH}ׯGSݠh 08X9y֍<16(ƔYfg1{b𥊨x ~"3e{ɞt0x+n @ۦ^%YcF*I/IDj,).MuX0ӟ:({#UΏArk}wɪRIY *2KPTHΏ!荮벭I&m$o&⺰~e Ml W`IMѶEX?Ȯ0p Z%ȧzYt$M̋&%R*lF'fSNvOG4IU'>XRn;'{128J\Fq"_`h;[T+. ) A!뺁*:wAN.) 2IՍ1r{¤˗]@]k$Նmnuem}M]$bĞٰ}%hWxQ]|$qotBmԃT-R&rP:TI0Knte:6f䕕\kD^L9"vKt/S{g4Sl? F3r3'Y8ݴ)#(ݮ5{s,0{~W#哳(4B'D/'N2T) Lb> XE6oV]ՏU'{B B QY0 S[NCXe^9lcaD%q/v߸7;v;$0sR=-hnSx*/W.@:Z}d{Sӽl|Orm$V#S+gLb,ۚ^wd䔩l+x xIS,&{S ^S[ERӘ_hV=gV=IgV=VQϬLCOf@wnDs.З샺 m(Ra'4踃&#-mYEG>C]g-B`fDfG_3ҡf,UrRFQ?kOppp\ ථQ$1LY87#oz.m}NѴϣ~<6yMSGLMW0LMwksZOUY?3xgM ΗU?`g7R:4*-_?tl^t:*$u`F\m^J$H2q#vO U(I[}?;ݼY7/FGOkKS\ƪy%C싿uQ"%:`]Փ:t.N4mEhls+ˮh0 {tNT .SD76YH&)H/CR.L[Lgps5I\z(#/l:0zr{ݔ+r=xzS;w6@micsn :kPrYYm''۱WY}Zme)PYG6YgZ$E4PL]=,SQZ?-qw*WԼIŮngk-#~8Ц[&| / _ĸ endstream endobj 88 0 obj << /Type /Page /Parent 259 0 R /Resources 89 0 R /Contents 90 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 89 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 90 0 obj << /Length 3831 /Filter /FlateDecode >> stream HWr}W#2ǘ+y%RHH(R! u>x{nHIik qzzO-N.0x8a%-9)//ˊ,㷓ůLv6gTv٬KnN.' I$uE&ɻőALQ;tdɼ֊L < :\Z.[.˹8<~Oή~}zuvAW +-3VjJ𭢬Q2,ŪRko@6ko/~#cpPw_UųVT6|UV5X |;gP(VVy}2ig;]F1d,* , xN9eT?ӑ!zT^vv7}ݐ{h>BTE&u>EP,g_riz8%LPSs _?`wa#wvuU4zv^Q9Hm˾ꢝE2( ndzݥnm1!*N=`ZH'?5h(2ZiJ*\Q!]2~uAdH 4T@%}]]2'bk6݃^ `bȃ*>AnC Qnݭ JnV6TQ`+y۷js wc䊹_T5q6)(xPT rwXɫ9NV@_Ë /^uᵗ_jxusZNlj]$Z+*{BNG$!)7u ˡ؊S'PuJ2p:DJ& &j B3@5w0{߫ZSV=6F!\}[xXq/6i3C(Ջ+Tb0SA*~{r\^PS]C\TRM /Dդl76dP FbZF'LåVOv[HƾXw=Vͪm6zV+upH`AZy_ >QLs4ݽ=t\ؘXe?rm$(G {]&fߒ//:j\`/X` 2*U헟` Y~}0uOO dRdA74ޏӮ.〷hoȏA4O9qM6Bvݨ|k/!=sO6k$]n7ץM.cyA#l30Ȯ_({t JE?Q=w˾-mFp\N}l>[N&/aEXڎ"9ΞXHVՉ9#nw]`n\TX7ՅՔz?Ƀi;;w:(XĄOGd$^F*^ۙ 3ݓ%s2<)畚 *%Q}ݷWPQ;D@I1v+*(*84ܵ(Ls hH3R7H*̩(t}#.MVM6HVA&.ѥnY |\:_r`D978TnK^vD'#T]L0'N/8Ep.)y8F̈ᬞBaPǪr'rȐēr\ r 0 =̚ a.q cn9}zشD6#V,2CQ F!x2Q-@3TC/|U*JՂ0G5\ť |JkL#aeE ²9QLtϳ!3lPd$ x* gU 0G\ť J\HG Li9 :.4NAL4t c?"d~\<s1ǥV\AOck/LGM7%"t#JNv#r(˴8qr" 3)Phdnuç 4k|QC1 'FnmELŢE3"Gl&84D?\!c#>~Jb K4VׂRef~mzKv;r}hqP0P8 \~R0cx2ݐ`P>*Ǡ:v?9mWt,}A- lz^x6Lƞ%EvrZtr ԓH| H󗯟>>w$P4,dde+&յ<"3* r=93IFa[ޚbOHy {>kHW,'l -&05+&92@)~($-%1uUsCg+Iࡳ֙&uP|?vI;4lMHD+jVXn~jV?>TBtCDߵ/NrZXQzwp> =vձIM4]t#!Ά4@ V2i6/DWm~YÖŽ06#,GotI+ ]2*ܩ]=nc ً:6߻[-XÃsTIů^JJUd|se|;`t,_fG7 hu]F~ 7m@$1ǪƹpܐQ o͹97Bn,s٤6D]*0QW(C򨫱WHtT*% ߋZ7H4.H[QJ1"l3RY.NJ5Y<6d|kvfcF Cnf[ 4p|Fx#> /(8d&@B I d[5;+lPD p?Z܄[ =~3L @J` *pfT$+ "% HuYA(!+֢NFƈ "D)x7c:DYw1ī+R/~k_1yS8! W7pc e WF7QVl4xǁcp\"{NoQ۩}iNäitjp.iR߆poc<ʄqE'lCGxp{۩߰wکZ^Z@}w܏jT?mSQ/{_6q\=أğ[wpXYb@p5޼Uq"ߞj/Kŝנ*xJR33 endstream endobj 91 0 obj << /Type /Page /Parent 259 0 R /Resources 92 0 R /Contents 93 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 92 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 93 0 obj << /Length 4700 /Filter /FlateDecode >> stream HMoGshst$cXfL9r]]0H BUTW>L\ɗV2W]hS<\mB3۔T-Go=i3Ǖd?5giRn8MÏ?o.;f2*kֻm nUjQ@:tT^Te|+jg!*6nӤ6*p,6rS"_^}k>W7$mmօt:j<[;6d`dB(]S{6d;ߚq?7ύ1>YKw}ݸElT/?`޴ƮĞhr`լޯs@&y.s{[kҶ u۴l=n|q3 rۣQhbNE|{t%S/0es/ONo\myJ*jAtU Smp'T|h]j,;tTB~hmH}7]~Ά5خcx8MFlttSfByK|B  9"Ԛ#A9Ԛ#~Y!G1_3ifӯ {% ɺ 6AMg/.MdBEJd{mb!% U;gC5֤=63;9Ii:5Y)H*Yck>j5 [(IodpBoQxVE_2;*O;/'w&(rqE]pǕc-ty7ĴPr|h8n^_^VgE|zhЅ 54hb;\ޑʢU07KQH±㥊"D-B4&:=g8\,ߗΙ|s\cwn:̾7Nh&]pT(W)UJbRHevY|ι.pq?7lnt?T F@/=6 CJ' ,j(V IS u)S_PU W)"vYE ipxYmy(.T pc9r D@0HM>}D4%x"U ]V ,`P\rRݝKC=4$)TRY+P@WL + U J*E.+``C !X a1u>7,<g0Y2@'@@z%2X+Q@HWL[#QDQ/%`[.~7/|,d2O/ [,^_BY+UbٽbRJ 0q wG2MG3C"CGThm~'cՉ:*+QZj`DZE*ؽbTW OJ*E.S:xv! b.eC ̬0Pp hh(V IdSu)ru* \j WPkD ]+n@.j֯|:áZ4uׅFQ y6UuV6:+)3|6<(Jl*E˫.ĥz:Lj>M?_Cʸe,Y@@Y!aH Vb"#^1 Po9TQ*˫I0%6bLO>?vqBtg%CpD)RY+տZV?Z{4U#&V`YJU|v̾g:.Ci:+Zjx`ZEؽbJ*E.+@M߸Fb?cH&TTd׏@Dk%*Uh`i %*xU]VgO~ɣ`gg`GPb::)'L(W)\^ekx 0'WS'n`jF>| ?Q'x<*EʫPuGG<>'ۯ^UQAJ'Ct GTzJկV@גu@M kXaoS/g i y }sj>ae>_Hek`9-b2: De:8M=]W"ɣduT@oVzU+!E^RI&#QK v;f+ŤZ;37|Z8R۰)hgD)RY+Z³V* uN5 A: i 'E%(U0*2jb"u 3ӝ Yq OF^  jeH  4"I@yj&LZXൄIr`*̛73ͫ`k,(?(D$QQ8R+e ,AcAтE$ PvL,̞iQ6!\#PR2{zNzjD+Z R`Z)"/V-"x-!!o&ٮԠ0tBDjN̪$ R@R+ eH5vXZX%&H{jzWJKKz~I=H 0IP/Z h%`@V 5!kZk>&5KQ;&=9;&ilJ k$x)D#@ D'F;#d<ue]"#.V-"x-!SU^˓";Uzz~/{#!Ol0wo#PQq3$0q3 G8O[KDUP-Daz >y|ˆ[It(ˠV"Z jhs2216hBѢAW0pq}r7dY>)A"+d_U^FJȀV#C A>=eJXxjk VG=aJDmty$Sr>eF'C h58Z)0W-CV[G9^?HZX'gB~ߔl>Y͒wOe#<~aȪIKx^JVCV@PZXTΞ9dz~y߄i $Wxtw~8>}KC!yxH;uV|%y껥|>RjçJ9Zϳ];I* '/.v,^M\;A % 7derV-sbҎC}ȧ~8y>a2c F.ݦ}Wi^]J_h\Lcr}t%j\mLI#+-]<5tz|[z[~Mx ݺ$v[4t endstream endobj 94 0 obj << /Type /Page /Parent 259 0 R /Resources 95 0 R /Contents 96 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 95 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 96 0 obj << /Length 2549 /Filter /FlateDecode >> stream HWrۺ}WDAVθS۩>x:Z,7@E)tS kYL>.!ad0<F4 ,v7uD'|uk=sߏbiM_g*Ք[lK懺LfR4IDRzR WF}e#WN)HXd!׌5>}uy{w7͌׋at9W`(Z>(d ).Q80>;N@lSAO?,c#A .bMr_hD,ڋE4.*8ѡQ`U.ɻ b~LZm{ Uy)"QkZEmdMKjRIs7RI6oU!Wnj19x[ݒIעdŪ{_oy!Ⱥ*w&`iiFjٗTnC\xPR, 5`f5dUR^JÈP {UYq>pBMR[uËdF\dYVeʋW"r'j]G)w!GzKw@J.sSjz;~Gt(~2Yӭ,^gW"NϾ/!\,%erZ)pH+G۠jXIDG%IߗUcaN8Z~m@6 TJGcpF9C4@F}n~{%φUi-|zf*,Vpw!_&ϔ[MQ98 /[@It1Ѐ2i8(V{4I5ߦ)U.O&f'UОl$ .12YM3M^RCvsUWry#m|*T\I$r<7'jJJd"QK\<6u 1RD)w+SL)ՄڹHqO(0StO'.;2Ljߓ̶ӿvE{9Q 8s?B]lw[#DQdrCA5^H[.!6&Ze^h;ԙteF* 7.Nz~ ^9/ti6UiCtڪw4*kϐ$c?tm<4LG[𻢭r ],aPӉg&FiUe!.&ڹ'N8M P=Ip׺ee.+ƨRI59Ѭ55thqf WR/yJ l{Ji¤M'Tkxsh >9 ϖ Q+V6l  X@uS)GR1#;Ⱥ N 6R7: X}Rx*+ԜE&i\*^{O3r׏h91FRcE ҄ &E$g$aZSGsd&O{GҺxTd/ٹZ:D`Zs9Sei{S{QNAi,ⴋ澹>{muP7SĩMm@K}T9z-ޭ =àS@۸<X*0%RmBjg:;:jOX?n˼!_MVSi_f2S0 B1j{оB Bs.dgrG\7rgr]KQQ\nQIZ\K%(uMf@JNu}dXZ%CqY0憃COz1KXwa r-ߣݡQPIY8r-rPE\-c93H 8S3d~sŽ% Ql | Eʛ?~&\|FG mT]V,pv|nmR6qČ?>mXL|ϱU+SXSj MYɯfD%@@5VҳkQfL< mhsxFs}js^1t endstream endobj 97 0 obj << /Type /Page /Parent 259 0 R /Resources 98 0 R /Contents 99 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 98 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 99 0 obj << /Length 2492 /Filter /FlateDecode >> stream HWmF_1Q33@6R*YuUv>`W#&X~e g!͉ ' z8{<WM8uȗj-IGf_W~Y|c8Rɱ6[3eáCI}0jLf/ x F6BW8r* vD6h(1yJJ516Q̖)}UI+"K=iO2/I9Tۓ4'Y.7Vw"R88Y@ϜN i>'J]D4%$"N:-rJt] t]m`~|J"U׮)0hLs cS\!}Ji#IE&* R΀𧮜A(kgnՠw݊ ׵*K{QCQ*E߀lIIsRql$kMU/ulhYpZJ,yI?}j",c L8Lcl`Ͱ@g$w<9Х8ᔫLw$n (&_Q_`{Pz MOՐ@oᛞW_G%X h2$kLi!ɮ.t2*%7}2t9_ÿݏQPCmσK!csV!iTI&l6jIB\P+^\4Qś mLnR1 [2* npApA4Q ?azC9'C Ȧ4(݊6Lp9O?N?*:5_$.}w2:71˾9rC_(1и-[4!pJECp ͼ#@V -E>ҊA80E'Q՗i81Gmj4G~}14 \'l+;16q52s s~6f03Iu**1S&b"OQ'4s3>ANQY&QYwEHi!3G<1'Gu3(Ec)%@!st W,btcT('IY X,oi i>=eqT \"#(ZQ}[{-ɩ,A/?f^@uPĽ;|2iHM4í{fsgzsn.n /(SPiu- `VDI8TP^G'`qpvW9c8; B'`^N 7=(%*~rڟ htި.3ӾcGi&bưew2.ɒXq- .M3m )5 r B2[/kEM An#p*0I[XJ>Z qӀU‚9Tu{:7;|Ư&0pha!z.gEméځՃosP["M2-"͖ę*JsNnHK'z-<+8ŬOsJI;ؔO ̸ΚA#fi}SǁD,cV%` -,ƴ6/x{QV5jֱO݂51*;ŴhKҏV1T#‹_?ҏLg7Dp!D4NsW *U<0^o c A)kQ ,YY̓NChu19Y2g82Ƈ$&S\oQ'>*LPa_p ɏuHջCZV-ŸtpnIHY?wz@΄vqn@0K} ѻNi@µwg؝OČ^9 9%-e#wEWC2peؑU>ͪB+5hOuԞ8UB (R4:ͬ\qn~qAm7 @Rɑ\"pdwV&lO~tv@I*>ØcȆ?m-ML <+^VķF]uBU: endstream endobj 100 0 obj << /Type /Page /Parent 259 0 R /Resources 101 0 R /Contents 102 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 101 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 102 0 obj << /Length 1513 /Filter /FlateDecode >> stream HVs8b?Fщ8sכqC![I p}LFo>:]& :$둮 _($ѥ_QXVղ]:|Fc%4g.!~݇zI3~h7 f%iQJxWoӯx[sy%J?U\Jml3fmmV>C˫S%˧*_!B6k-U J_04WYr┱*8Mӯʑ "*&9.L{Iq$>rLjm@=BUlzp112IF%5ޞ<Qt2 "P" |&&۝Re? S@aOc61ixV) g|ݚ8X*Ycl 1xe̩ƤC!:*L\/fY[4 ?2OL8&zԠ(Y!E0+!$onH4jv›ΦCߛ+>Pj2rL@ p@ N;z֋SȄ,"o)) OZacrY~%->4c7mZ --,ƿ?h]z${!dM8(vl k9M? _X)2DJFJWNxEHhrR4~)4 -C1'р;KM|+Z+Id "IIk *!&N endstream endobj 103 0 obj << /Type /Page /Parent 259 0 R /Resources 104 0 R /Contents 105 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 104 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 105 0 obj << /Length 2990 /Filter /FlateDecode >> stream HWnH}W#5u)bx1.,Zʢ 2N3M] AOWU:vuf%0D_(QhrVh]ٛ9,'D}Rz!\JٜbluExgZl:g,4NP=]*3kBPJq ~=gX+%HT,Ś71FwșE\\^ۛw׋%zX-Vqsn F-SE!2b(es;(]hGؔg~fTg)/Τ(4R ~E%Úb7hd 6Di(јD=՘es5jXJ%Ćg9/BOOqՐY~x y}5<4Aw/Wu*}|uvGV0˓q>DlMCb/Iwtw6@տri[ʔuwS5OcX*O\sCjהg[kA^^BͫٛNݖ@O%oPmRDaɦ*jsj;G4L` W} 5 )@|ͷ_`7S{79j)j*[Xrh?lkO@,##R jy_̼y{f@:PMn6kAJT? `8^77E^&=2[PǼ{[g bcL쫝b(fGCΪK0ٴy3A& MmRe&Pp >or[61ZunA.>ʉ4_.sȕ֩09ޠon> Z@>M81z  !\S#UPp*3W43o4vOt1`"5D_3Laj79Bj"2&Oi 6: QK≄ 8NF7At}6)4&B:aJ4FΕj0KXL{ *^/cRG0&a\~ $/+0Q)0hӕ0=Q T% ůh!T%xx%xߩR08%X YH 'An\%Ac%vx59 *t44Ԍoy6Ea5owѕݡz*Ms50;ֿCq^ Ps2)%8kXyB_APˀ0Lu넡!°{C`B4:q'f8!(k=nO+ PP@Y)zC23TC)]QC@DZ.O-MGՐM@ E Bz.-r)$#rpv$V\xG(O*Ѥb)xGcPSEyGqmujdui*~*?V\14Zjt.3Tu[/7@E:9$4T\' ss4"LoCPobX3uwBDLΣ/[ɛp̋%C۟"G8BV!܅%cL.~G4-"nT~Kes\t"8iR9pbf{=~RmuUn ̪8R\G PZLڵR)dv(uko`vU 4^n6QÎu]Sw6DA6˻܁G+w21KZ-Yn9l(L +41#2ͬCKk~Sq1euⱆܵD5ŸevvE*MR%oM۴ S&# M\5 e0AFmC`lLRDkm! d'оmFbQc 38u1X% $TCYH?R2:\sY7` ]:t6eI|>5&<J G) %<)јkcW&Y [ӌ4F-C{=PHݙ\yxj{*e٫кe:~mhq,^ŭm6 VӸM@U$HX  " Ycw@lZ:d$nn]߃ɟyVLy˻OT¨SD8cΌ\ |t.;d]@ gǗ4$i0<5T?e^zZ `u =9#$2vŘSH;QIN!HtL)sSb-C'8%R?tSb-8)NzzJqS dS6Bvc7ބh0eu!D`8ڷ̿RN_(9d=T_X0UYj .@d #Z=;6JѻT蓣E1"kQ?\Ms"ӠW-+úlG" 鍔gK3Y HK-G!&oQ h`55 sь"3Ѣ'D/b`ߤTu;'ha|tv>ni{!}1?qr:N:10ЉN(:A@'umN tQH'4> endobj 107 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R /TT13 244 0 R /TT17 246 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 108 0 obj << /Length 2769 /Filter /FlateDecode >> stream HWnH}W#:f_bV ,f%sF"䃷‹(,LY]NzFCEO쓄EۓR4LۏsVhDQb <>n3t>^:̼Ek4*tS:E#aΐ8`?Dt7]PvkwG& K!8-@†IC,}A4ȩA\ˋӋGhv>^whLϣ̇o9&R Bd"@` l ^~Cb@qL=󨈀r(a!=_؝r]GQHB<@bHek4A"AN;2(KL~pO$J{С!8 `H"X w* PrZ{6YJ{;=BY,vʊ]&S봑\wv"ž1Eiߐnl::Xx7H$bX-^u"@b k=hfΒB]K&g"d8#`V^sH_΍i%8`~?dz_Tw]y2#8!cI9b6c풐м46kPBԋ>dRNY (GV?nˤxf*y 2l/fO8nn\fv? CL' w ʴkZd #kz ȹ=RMr)Gu=Ѱ+Hّ4Hޚ얘.vRtFy3,5r5KW{ȬY2Ǣq=d,i@tl,Ƀ6j9Yč< OڹOpf^](O*FuIMA( `O[{-R\әoL"Kqft S]U45qث7t nS(KHo01}S PnI{fhYI8v(F{ތ!Hmu ܭrbe R }?SpcA<8HݞUSR^cVݹ!+Q8&z O0=II\_Е SpZ B~[8:p4:pFK9M'Gc9&N2^mb?KtoY= iLlEH fi#O>]*ѧb `|ʧJ7(^6-01\)˄w_|/?~A߯MSC߿lZH65pT pLm\ɦ=jJf ~](STl5%J7G.AI.rWl P8Tòi;IS@P¶lJ4i(p;O䀂cjFE&lϙVh{^Ocn16$^;pn &U%[ d ڜȗ~I.@ەM֗~Gt| ۜi /zKvAKm<=@Lgl!$ٹi(ᢹ2 -!Ԍ}?,\q)Yz&NJ'*u3b6X|bpk@m>zn&(+(/Y0lҾiτjN ɏM:)x%wF% rZPϵ"\~]ZrHJ,WV 5<>M P f~ʡνCo"O?J14 aiDes^en07!mfZE ~Y9hL:&>0Syi\Sf~aǒS58#UMB1 yWֲ!ѵY`awB1jv@:QUKuv_jaN9 }b:NG !Fmh!01MY„]N`2UR (O{s]P#b^ԞkRzQ^ moaTݣ#O Ҵ߄M(ʇ :CT@)d0kKS 57|-ܝ h.& SV}%h\ BlJ(˻~M%(ҍlt18CtSa,I,_]7P_B]+AW%UMc-j`pT~9h[۱a&iUܶIY v;<\S iqn.[PL[jN{bEƒf Nn:BtvN>M'gѼ4ƛם~KBvY`=p9$!Z)i9%A R: %0. ѫ8HWR wQ=0JtP!Fz&f憄; AP0PfHos 2eC9MIbQ% -LCn#9DgCLy驐J endstream endobj 109 0 obj << /Type /Page /Parent 259 0 R /Resources 110 0 R /Contents 111 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 110 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 111 0 obj << /Length 3790 /Filter /FlateDecode >> stream HWr8}W1ą$8ozj"$P)JT=/Ӹlg+ @tAfaQnƣ0,?J_%am7]ŠLXWg~[sE)|} ~cyUf뗮/ﳻX, uxi֖ d4g *M#.`_EI3ȕ[77aauD oȖ-%U9XE<ބ d"[4J^cگwf!Zfq(xfw`BZƩ x.4v" 3mE43 M:u2'*yhsБ;-(KL}JF\0BO#>u$%֖c7[}iL=;?ʶ:>:ڃ!R\DbT ]Jh:{|"κe`K=ټi|K^CzLj )i G֙4Kf[ֿ8A\.[nW=!˖Od'VrP5'SװےZѸUb46)ħt4GB`HA*ͻ)5',Wi R9;{>ʶEGDL[ 2%S* : R#/h kie3ms~ 8f%Z\9T͠.]^P:d?Ҩ&!AH8 m\TV0^R蛾/j_m%{ |hZjO[ :1nX8&:U=2:M\;UYe\gN.:Oy[5γ ̓at] k}='}e} [}.? u2(B[pps=NkHd}⃥z9ns] X$)*(Y@n~1]A=FTg=Dn! ~G/&B3V۟bUf\> c^R| f ]@!M$nLJ[=zD278BV HR]/0 90*SX=r&P +9V;XGxL}F<@ITUEhY=[ L@OHhf> Yj~=ڈLb {c"-TW#?3",Ƥ3"k I('J`; >t Ưs=kٮxs"5:jFgq[a ȯ[~4Q4`7RTr3s) ขE#^V=J@,lK< e^:7) (L(9p!ˇSbD9ݓ.9s=kQ](Nu֦m"~?7и`)H1}I?B|۾E׹>x%kC^%cJbT!d:t;'6g#cb\>ol jRl)ia)Lftca Z2lO'F 0"(<@^wja;KQ!z:ɁƆ1(AvH.Їщ+8፬2b"\Pt"bߵCa1D7mlݰ_&b/"P3JgQj9n{-v]g{L=#fm=_|<3&|Wd$ՆlqB:,eD`m޸>%~bs͕3UJHlH@\;҂wY3O|:߰tM) SX)=f"KLۗ9W^7,$BAiqdd>' 3Jvqb.THᰧs[9ޢ$;cxΘ691[1aA*_癭n*it92T4ĕ =̵̓.ku}sՃ[_$ؒ GP3;*/cFU 'PI䏥?ߑ?lJXW\oy:֯oO3ssw9/i7:18}WstwP1^%m#AW"C(S:z r 3CrC$mjiRcN^k&|{UWgk->f>(]odXEɱ:5A^5A\9AL_<9fk6W:g/m批ֺxCI#bR1ik|=:30i3.4ң$t4]$mG`$:&|Ǚ%يEoۑ̭  Z8v2cu<]WmX}ݽcSAUS"o1ꌟV*YP)C}u Ğ:0)}&wǂ팰H1 i:*Zϊ N'j=^e5t#PCQ!H{fT WĭO>%|X@ >;wXQvv;3VzRu2jzc^Xf IDAQ,gsۍϞLjjRstb$R0{Q7D^/TPG>|yk瑽jx'B|d#DAR7[՟qE1]ؼ#.I]tFnKu%` }Lu٨<$/D,aЩ 5 kff& xk~ɝ!,_ ~3BUwXT1Z!1!J"ܰn5rT@ܒ R+ fYⲶ{ pyikO_9F̰&EDaų(@D|ԶMMX0ޑ]'nRTj Ԅ ܶ2֢ }֬"cn :33Z+TSx\W4/26Dav9QO ]8;ӽR#55ro>jI6<:}KjZ?7l endstream endobj 112 0 obj << /Type /Page /Parent 259 0 R /Resources 113 0 R /Contents 114 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 113 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT7 279 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 114 0 obj << /Length 1891 /Filter /FlateDecode >> stream HWrFUf~HbfLbt@(:VG\I$hw$ّ0@d㜻}̞& C% U%V_QU?igs(̒ny.N'O3//D2i sGGe "bqPr+w:Ic x$Bgv& ¡}#}䁎;;?@o^]^],ޜ.*Y$ˋDٲqSe"zWYWݙ\UӢz˟Kp$ XL-u3O6EVQoR\CnWB+`C#UWtP -lS>45HBUs,_y|DH_;GbF7̑ աw:HZ_%eL;.Wh]BhWhc(2kqboe^O]0R;TײDi#su̝aqߎWҼd wI{?* ܂@$V^DR}~ ?aQ @u+/ڲ.;mVld NRT6BoTVJ zܺU'hIoAox]C +hwe tJ@HW(Mh-\Qj),şD^5Pi 0_Ky27,^?zۤvd&o62ոuH|$A'*?ǹ]t,SzQۙ8pч۠aL3֜m[굔3?whU  ;۷˓#pqr:nGG'=TQnwǹR@%PM# 79iz݁Þ+Yk/?0?}nO`tP8dT hUګy&N}ƍ0ۋih,DO;kܵ%4Ut)LlQ6ŭD{}> endobj 116 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 117 0 obj << /Length 3439 /Filter /FlateDecode >> stream HWMsW̑2|؛,[NEK+,l( t=L*u.~\-KRV>.҄'%{_eQaUoغ/$_o}Hٷ~ד$c=ce 0br4j^~/ViqS.l ŵbEsR䬫w ΜTA doU3c4 M9@'h\XWwˏW7졼,ono>*gUjfI le1̲`:Ԯ7aAasYh.H z ,i=!g>E* n'`*fY4] TxAh_YC&5FqI,a*?Y89L]IAمF$r^D&F PРIBz@RdpJ }3.Dhk K[#2xv/Eu MW{IƅJg%!aHMqIX`D pИ$($%A* tPpP,m Ȱg$7ȅ6˫vʖ;2֯贱EJC(! qhD$6ac ^#+C*4!am1u4:T3D"xwd%`*"@[CFi IziHRt Ib g5P"G.}i@ {fn(x1G&^S؊22(e8lL$+cDʠSue k @ yFR?2%- E?B<2"a8lL$ cD SuezFsXouW q Ph 4tZhVpa&h \MP2UP۲L!?V>+rDM&Fc℃ix( 5\w׳OE/ ujg r6jhl/5̻ /}9B_6s1 u~l5fhm{ƞMSOU}W}=wVVҒv->/ .Hj7Y;Bu5zm\E()]zA>od(wбP]%Vb G @d1W\A]}ag[bf6$,mUk4M?4A)Lвg$ *JdiI뭆 ӵhO]Mte]b} .=ϬkilްNdP2#(*S{CE{ln] j>B!X5W{XX":6zcBِu롶nÓ5>+f4[[07B 3Z7{=o5;rGjq1[YBuՁc!^ԓ!R_[תd};Rh{!q4+u2pگtj9;MnT^ߺjS[S=5ɳO2@A[(& Asxٷ#/1*']y,| 3å`]/{H}֛,I}sHm=TNֳx0T zW!9Oɦ#_1B!e}ݶ?pVj 3c7A87/.Rbξ)W[9’Ͱ^f#%?R5~)[\ƘC4.xB#lhw"`K8V.αZi\q1 i7aikpL[}KDrX͙SjxWͅp"9RK55EQMQ)ƽ.0 $XiIG'BeN9=<##0E>Bm6 mXQ-|,U'grFYMxIc%MX "eÆ`Xq S>G=\8(;̹>LI ǿ6B[}NM]9 hx,"YNXRCXm=l"j(>*8LiŌܖ- 3OJ w¤26=ʛ+=oX:Bvi-4&r&wCg7~ ,T+Vkkz~QMϘ`i08'~*XUV'+L$5*O96jAbkwUXe& g5o_ƪ> K %v!A`Aسx~9KMр0`%`i/%16߾>lm0QRNVT.BK20l+Upv>H;5RG,Q'$:RQY⮱S'储dcr)a7d@s~?S2Op/]ԥ&WeM# FolѤR>h; ךx "={ endstream endobj 118 0 obj << /Type /Page /Parent 260 0 R /Resources 119 0 R /Contents 120 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 119 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 120 0 obj << /Length 1844 /Filter /FlateDecode >> stream HWMs6WHT c)tj;c3#6TIڮ3Xh:б<>x]x$0.O}N|^BR)|IY-ɦ7?_3rW/sߏHK˧ō󚜟HO,^o-~O~O~Y!h(h, ~L*]K1o|soIbh$eHD&r_Һc7_"CpvyN./_^^urp+d<:>6,RȖXK(`hC{*åbpAauFsEU18rBMء1@Vnȣ kiF1? ƞE+sqFZ!Ȑ/+k놔t뚼,cU[]us6W1Ee1f^5u*6_ӟH5I/[MʗQEI_gtDM悅'%T }ا 9\!c)6W2Fh(eL[C(T4Vyߦs+iʪ}?8ӜS (Tɽ iAMS^y! %yh4J;M=y&#MIڜ8L<$%Y6r )Hag#!fm? a1V᳇o ^^Ѐ11P*NSf!Ov H3]VUVJ1WMP+ZC,@[(WsuTi(cWl &חI^MZl@343&1cXΛt/ L1P,z !;-PnҗKC3WW@8(c!':Sr LNu@jLy~ܗg4*hs=GjG]=F {Yw ѕ7uNB!\@)aB۪ud[Z[6K{x@bCֳ˶ځ^Z5Q&:q EӨCǡ]uluT KKt? : "1 uF% ɞXV4d6{4!@v4JgTQ3:B&d#0i8:ۺC;JG\d見zOO^aZ}9L %f$tN@0SI LJt?tAtRMgVE r}îI7I8PWx[Co MAtoev~M uqj F8v7S]:op6s滼A Mk?w-`hO0kpjuw?P)mx6l q[G.9u ӻ!ثƾ50qt MA:h] y d˸㙠Wql B0n7 $u Җ &mR;l7:Y; endstream endobj 121 0 obj << /Type /Page /Parent 260 0 R /Resources 122 0 R /Contents 123 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 122 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 123 0 obj << /Length 2492 /Filter /FlateDecode >> stream HWKsWL# 0r)yTr^rA&C*  {)yDɟg,!'!3O D($$e?IWֳ6t| KmKYe F`[ŎlN]_gL8"YBSI SҪS~ 9!^y"1Y$ǽ=314 A:C\ ȹF?.VdzlK,n!l$QQƔ%$d@ Rj"Ik?Ab1 ݞp"N}|A;~EBT`hi"LcG4mSE@FcӌbaB9m,<4q[H\fDT&wµl)PNQŢ- 0)v``h^սxcP&ܧk8_{+ ꫦ&_کΰ/8.c8 Yw[=q xg~q8^%rũ@yUU2ʲ`:Q)0bŻQ-)˥Z4]@- FT"V@J녗{oZDTapFAmh2 =yLX,RG? gɢszix84mO0k!Mk!qhL@aV*aCd٪\Ybl jzG :vB5m~^GOSL=g,38IFu4xb<-L3PG;@|Dg21'[Ecw7aD ]xJѠr{Qju_2J4c(P@ToʛD mq_mYdQ)tEYU0haQ9 *atEtW{(Uy ]e<{o Uen -[o)aM8$b!.Sr=Top70%it7saOEW @lZfۣq\!@L/\R 2G"Cb[70 1\Ӂ~S6.ja\smMܞu{h3nDm`n`Kul(dŮu(Qh8h۪+Ig\vJ 38(4_^QUHVbpH)Dt <뚘Pٸם%_ul8NF~oϾQ endstream endobj 124 0 obj << /Type /Page /Parent 260 0 R /Resources 125 0 R /Contents 126 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 125 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 126 0 obj << /Length 3052 /Filter /FlateDecode >> stream HWnH}Wn-8$HYԊr2=b`NlJŪ9U\*˘ V/DCB ┧qiO٪W/_W /x= 3VŧKv}R8X7j^}ԟuqY.1ObVdFK.TL~2ym-@ FͦfwaglrwώOhzk/{?[Lɤ_cc[$RZkeoMݳo-5~W}u2\ EO="(_ SmO}`GD=o֬iqE1*fn̒nnuŮYծY[=Rнfii143CCZ:sg4ɼuOL0\dSZ- S&סw#kT2~ =͆}jd O(md-D ;xY! 0AtwoG}Ӓݎ@Kdi,@֫Y14`w=K&t*1l w] ];Է~Ԣa>p띂/lUe)M!0U}G#? F"8R~خRG3u3CҰEM]e(ۓ:/!Ca8e͞ܤUu: !54 ʗ#,X75 i5Iadvy٣"f'}l )t53^(bB%O\HeN'g/׫Ic,ن+rUoZJXj׭ZsPn3ؒCI1BLʋuZRy%2JxpOB#sIۧb_rI}SQn  ȢPh8Oա1ȗh{DklkSa^7Vn>S ߚzi xejA~+8,luE<|['$KY,z\ı"dwu5M>),9s.ݪdzQ{؃.5d1|RmN1hˑsMwRp‹9M8̩s=3~fW7ӹU"pԇ?cD`P4tnqo2(d9i#F2S>a_ԝ+!S(JnS @ 3=: > ⚵ѹhpm@WZ%I]tTXc9ȧ@ҙS"l~ -< . X K5 "3Ikiӗ)s3Q/oUv <q,HQZpd4U% i3SZBM쁪~JH$+Cf)'S$3Qe>U5}Vr*՘>HZ|`$X -Ԥ-IHMRTu6/ۀ n7DL' ?{CfPnuhiυӭ>e쓇{9 Sx{]6"ejaQ枒!.*,LBGl Y A}`N9k-ȩ:qʰ\cZsJ*'-hs$ﳣq05Jy9VTﴤ9(-L e endstream endobj 127 0 obj << /Type /Page /Parent 260 0 R /Resources 128 0 R /Contents 129 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 128 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 129 0 obj << /Length 1963 /Filter /FlateDecode >> stream HWr۸+B&]l+I"ś,yK}E:t!rMnآ"}sE8gQ&eQ̐|?pmFazC3[pRo#_&K/8U]-vdҴ徙-CQHRk̒c9]gpLINy"1\Iq i KS mՒoz ]U|zxYs#Fy;"/'Frv#j+v /Ǻ&z2)츾LM4{p9㮡ݣD~>~5:ŗ*Hӯ 8j7CƘ7z]xwv?x΃d!P4M߄(x)Q<4a5JwQϠW<  A-촡jWuFWw> 1ܧ5o7FO(,ݜI1$0,K,|% endstream endobj 130 0 obj << /Type /Page /Parent 260 0 R /Resources 131 0 R /Contents 132 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 131 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 132 0 obj << /Length 2989 /Filter /FlateDecode >> stream Hr8z h|=CT9ksAX;L;@-v6I)lt>Yqd%jg}b<:yإ-6OJ|ۍ&p-R?MgUrn>=y4rV4uP kG xO?(q8g4i{'`De qiP2)rs\?]݊7w^|lz;|u2dLcdX-Oћ>:L/Hl08]6YhquJtj'FQWMH Um&qhb`v!] MulyZʽ+ԄuDK»)Yh|;G}゙ŬTrML7|# um.-_EzٯQUZV*TNX!@hLş|,Pr[tcOVWV \X~YkDLb"#e릁KsȘƅJ~}~n߽.R[Η_RN 8| %YFO9A]=g/gj{p6 " FBxݏW$mq\91E`+a>* 6xw'(v #Y{㌱zjbTpy{}wo+V/ CG W{uߝ/֯j(?x VZVҚ @ =QLlA8HJ%jH+>B(;Q.码`PE`SP"7F zE{ 98hDK%$rX ޲1<@&ZU7Zܬ֭Xy`vpsXfhP't2cFa&F2BO9FP-ayFqW]ErեN&;['|heͰx#99:Hg 3l,$1?1lȱA%XZ-!òl88n!Mgx>[_no!]+O j[%k'&@&g "][k{z BY1l+eЗC1t 4%G<Fq2jpi4݄t]iߜrG"&w- %`3 6 2^} C_A!Xᬽ0a@%NZ-8ilLl8N0P00@/~peDFYЀVZVҚ@=QLmJP)b); hiDcaXE8b3>bQCrqK[blΗp ziJtJ WO::AZ AZc'I051@! 1!~T>@SlU2?Rq+޵w)i\' M32Nr @2 Xb-݃EZ-j GB6nIٓ[sX:J _q?ŕT-(:3/nc};-wAԳd:`ywqDi Ҳ! i i5>4z(s;R JáXl#sI`#X!sX|Ȑ:T=nNemZM{+6]DI>Q:&1AZ 0AZc'!'La-ayDp>C:" |Z) }Y2l.&6kVaCv1%&"["hk&Xi"hk$"8"bj3 (rDjVKH<n/=Cե%42Nl&ff"r+;>}+hw ,-:&=<:2}e>e~}naVТrszTYtm#P,hdLNe\e5iM %+h#9Rh J>'GT./+[wcm765 W?y# Zj*Sc;qiiz{?{gE>J}22G'[{ʚJ>d8 +˻B%XZ-!l93NrO| :7nyVt=0 Fұtz.T޾vLyL~=1O8?p#]r- DHdFAY!jRx9HrV=-'R_1kX$.h\ !#ѕ k]@y0Ԡ; A*ZXP f;vJq^>m3?iq:ԐA@+X5d7帛U :盶]RV#Ȼeso%QBc噵>I8 endstream endobj 133 0 obj << /Type /Page /Parent 260 0 R /Resources 134 0 R /Contents 135 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 134 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 135 0 obj << /Length 2593 /Filter /FlateDecode >> stream HWrH}W#h 7rXDDL%YZ@~fFOLӂ:y;ykM0 S>$9.nW F}@I/ymKҐ${}ln#o,ߧ}4<67JX' u>C74"\_+@LC)琛2vC!'2)t+CD*O0caaJ/[rw:0$um.ҼEs&¡>toRDھ/y!IJM']|a{].O58i= _߾*4/u`,U5$m 9=~ XAE&~<8-3Ҝ_xbĎ   (ug,!NU[bc#]`=B|]OtqM$V@$ y/=šOk_/|Έhd';QC©!:ԟqn腁S| N<=?,M)o\ VyC.[ pjSX#n77LY69 h`Y{>A(|s> endobj 137 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 138 0 obj << /Length 3745 /Filter /FlateDecode >> stream Hv~ ,`J23sⶏf: DȒB NHIOi*|(.Lzy&rKT_mչeUoټ䬟oο{Ϧy㣀gd_nfd*yf~h_?n3?Fe%ڳe}␰fT&t L3_ :t^?xH5"+/ĺŒUp 7lbx1%ϵ†ַkZg(0\}dkG2]-}vۇ5OvN./n5~ w8xK%"{bi8cMT#azTDu]6}B~ʐӨ!BNnB ɇh4"G` X^8Z1!;$ħs/@e(l~ͷԳaSE1ad)r’·-|~]?\%%=XFg(x2,T2\'s5 Tv(j~4X jU>".IO0nʰſc̶]_bI=<٣=~M 5m  ,;:M`.|(JO:۸'plh{8ѾM\" ŋrn\j6 ^y _]duS,v@9w;/5p+uU_ 7c{DGpdZDX!N.Kj?ݽWL u1ap3ڥ%]g%XR]UkeJ.^_vq{\ooS5m]L6뺵*D1^ߏ`Ю ;RR:cqRJ%.ƾ&$&,\MۏA1.y@l]JN $|nG%tA;0M$0ַF)-o(QB nv}?,WWW]=n^| q8b-uth[ hr&./$B FRRHFbVŭ *#k&ms&!0jT)Kl`M7ÿnsQ_"Jv!swh{`%]F J0$0( I_u/#=$&O FgRrZ 75Dqh+!ڟ~CUpD :ÅWO 09{& PIRF $ $wRS mѣ4>BOq5@1 kVB5;0|-N4`SlIi#H :RH<ҁb (:jIMI!,7 q@#\ ˓g{Jr ˄drxp7 --Jg+I8PIR 4Iu$@1 B.%M)/X'(_*/4=?nx@i˖ỏ䜓 1CB&NXd@2ʐBoT#/ ғEpj^-Pi!i9 RASAcZ=APfMp_CQUzS%iry(i8eHDtkFXuDtkB$"`<$CXe9ڔy@/3@zԷ?E~U`pF}?>B! Zk0[d'@AZPVjeQ7*A "AWju,IPZ;Jq9x}{k}EC_u4҈*!Dbq{ՏCu3.v3>sYhӫ99E} TJN9-֬ 1 (tu KY#dI_l)#DDjT}1%,fXϥ%,BVoއ"ބؙ3|+<)ewnW~|H9}:xbNxϞ}>+߳t ]-/GO$Q9S8/OY_Q5P8?~oavն a^ QRIT"o4yU]-ղ$uZ sC3 1]p: .znVZǡj}S1>Tz=;"O40N,:A@MJL]-ղ$uZhx|1X)m[r4)qi2%)W7Sz_a5$)B0'Cs.9 A0MʑuŒƓdQ"<b9?Ie+mgkN*[:ٚCV|a* endstream endobj 139 0 obj << /Type /Page /Parent 260 0 R /Resources 140 0 R /Contents 141 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 140 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 141 0 obj << /Length 2268 /Filter /FlateDecode >> stream HWnH}W#D4y7GV]Y 0ZlŜHM|VlQ8[>Uu&BIQG=/("f7K_ճ[J~y1vO NQl?Db쟳U6gn4vPKHgR C=?d 0M ΟQ_낈Ӌ(2D<ؖyIT z# ue %,\r U3_0}䔩huEr ZtyHߗ5/t@+Y49Ӳ~wZdSmfd69e6d A|(:[*4c$9Fd3ղ3N} ykj㥡I]o6[N Fу *MV 9pHkWf o\CT #[Fu^L(| :IpbhDdIJ!) 2lH!ػ8aM/9jhzw][:5_DI@=a8rJ9(DXiGݫD %9Y #u(>m(oyvFjb 4oz)2] y ǻsa(2\,KvAxSUݘ\ `t< Tpa"[%]X]-wX ^MŜF͜Mm4i# yUvC*3VrД'Aws[5 ͩpnOxcўJʷ X j̏ jcl.UTeUa["?"0zB!A6o^ d?CGlUX?8,bCqD|/m.@KCسM41LiXEt@,u]A-^L\(> endobj 143 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 144 0 obj << /Length 2557 /Filter /FlateDecode >> stream HW]s8}Wl]Ke{MR@MMd,gf,L~VK N[sk:>:}f5Z"Bj30f< 8YGW/DW5׃ !YRX~d6Oyy;PܫW?Fψ8"Y⧜P?HI-GjL*Ї8W&p8F!ำ5&K$݁-rȽ)YWqcYaF<:n$2 c.#0IZ.@b0}2jO0N0]}!:^KpјId][4ȫ5yd"Q9hE.&> j$ʈCnKF3fZm%uo\;hST%YoJIEΏ=bqѫօR)Ju?԰ў=%wr( < \>^%֊li{ Ǣ~}ߐ>h\Em/t\k3Zʦ fh!뢑CNS'|/-J51UF #D@PvGa~NCEICzKn/dЭ]Ȥq̳'ҧS9.!]!P`m uUrK2:Or(8LP%|^%$I4py:jn܁;;z\LWZsf mg$nwQLKDa[ ,'%#)Mz໇ϩHG;$VnMUEٜpdZ6ES@vV;\} bD.-V|Z6g&xbKA;)uGv/9:%2{lZq=ZҬ/5;&iPi[6 -T iR;kȨdNAYcR]עU-=h\uܢxhb>( Hk`qA M,*4JgF5Z]>b'k4Z} xi%w gP#oA `IǦ;gt}CSfFj7ҏBJ/Se^EnOM)N5њeǩ+R,N p);zut6X'#{T)z%.^sPъ*q1i'` 3j7BGzw¥:oiFEbu~8>ʮ [뢡=1s(qo~{/wg;n?`7.D>,Dx4Ԃ%,ɾHSHX`GIoCt:U24f頱b=]7ձ̇Sr.ߡYsJ sf{FvnٓZb/5{NGK=y ɞ>F`s, $CU`i-؃ lަUi6 ٯA)b1>+pg%ϲMRau_os; >"Y5I[pJU OTA3I^ 3tNvu#rC ׊n,r:\ڵc}[ x _dE[1k a,(L8}au;ۃܣ XoE*OXXJ=p=`\_ @Gp? <}7%\ ) x+f\;e2'tc`ʨB(+-۶/ &اX4Rςm _&ajy{lGs'7Ys.X !Ys݈H nGӖ_aA R*=X@5=dI}w?{> endobj 146 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 147 0 obj << /Length 3126 /Filter /FlateDecode >> stream HWnH}W4e(`İd7v ,a^&M,n( )_3T_H2}nIdS>.>,l>9%I_FdهN+=vgs< [q|9|ŮgS9#oUyo]_˯gW3}G?AZu><eFLAti@yl<)cyǥnJH$r{_.nͧ+X/ndC}qIv2 lǒ@J>L &W!iž1n˄0Ntº:(?K5|}04vBh =Kfk{c_dt96e1 R? r$C?G5m2Na+{^kUV.aƸFIH MS+x{^H)lN0$5wګ+-TQUgv,WWxBѨI3llq{1f¾7)/딹muFu"xx [z}5̀^HBid"pqW{:ҠmhN9]AG#Y-qb(vp}?Zf_#ogVj]e?(e8ɑ u,-6SZuӲK2R[.J1:wf'73+r]`Ll>_}T;O릪~4wP]S3YTAarW7U]NiΨƝ ńAI?C8+ʧ'LUi mn{ [:)G(^ڳ$?B8J*4{E57Θ%‰' `B?y1E׃^y2% hfԜIOO p>kO}F#qI:&@\Mɼ.Qfx$Q3J^9ocڔIlNA>5C7ArOuq?Q@2wʚf B^UOjީި-gM(&w_%6dtfuSϭ{B5G#=U]CuICYy@n;Q5@P?skבV嫲zc3:Hsf <^$HҴʬ_#B1z6;W#"7ԥ^"DGTD?/E5TmU҄3cSe7y^Ttt %ȫ麊b|gc%N E(L1ʤ3:6F(C#,f/0f[<|ĒnȦ]V-c1O-u?jG췬1svC4~ɛ0 lO$d(eNzb7*N ^kȚYF_0LWmEAIplp^JL_?>NlGb>|9\^JNX7 LXV7}Rf3齔a4D3 C?1I(h4$XŲo~buhř7(!B2LˠɼhlpzoR9vE?r(koaqE/v "5P3-%H27 MW7Wn l7k]f5Lt:ǂᱹ{ц$uS@&NpW,xubƃi"k ӧsD`7m@ky&MG-r$crnDwrNÃ1ڮ@o#{YB+gƎRJ6G]Qp8;dCQBLMa@i$D|Lx`b_hO Cv< S\ '*Tige7:R9\[;Ri). ";$m'LOdP~פu|$y9ki* օ"xhDoMzB選|*%ᰝ] @7ʘM$0Uhe@L&bt`d.S^|-oD(LZMzd@,شcY+x ؄A,y#9Jl>U,B|=" G 5ܮ(@?7*/6"AK ^]?\$>Hl {.]gz?|)B>}095j.&"Ng!]ֹl4\Vs:-uO Vih XYS!q 5 Mmk$Iְ.-GL,yVjWZږY[M_ٳARdSrh& ~=vuW>61Cmo9S$Ƕ`ƇM^w:( ՘VV+'堈[x8qK؋$ᗿ͍I8a +VNP5sUU'aDH}gkHCs~emNV9́d2qWV2]`TĪZ1V7ZDH41LDХ:%UB~M5T&@YL2ItQs^- O?#ثUN92 [4txP "J"hfXPIT̒pD U!ƞ OǠ&0vguP#4v_o$7npBPzߍ@//$6֞L" N3Ni:G3*ު8$ju hapkȞuæ%a\̽i%Z3Js3BE7= 2YKNi'ϑ5XkunZL 4 f3Ah/#qP=hwM7x # 0+n#a2qH]DpGZ endstream endobj 148 0 obj << /Type /Page /Parent 261 0 R /Resources 149 0 R /Contents 150 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 149 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 150 0 obj << /Length 1257 /Filter /FlateDecode >> stream HWMoF W(d- EU$CrͿ/gFGJEx4I&WI"A0J(~DK!y\ YPhrrufLBNn69(jR.i$4$ZzdR y+0$M!=rj ol:Y,‚:*"3q,"C/Nq'@bca[q<**r$ֹT(w Tq8X`h`SepE1ΚEAMk1~x"phQ0N(Ήm1{)dr5:_s> ,iǾ(UxKC]UYaܾliQrbQ9-H e)sߋ$"i`r8Tы`uݻlmUkZ{Q]>fhz<}dz[:es@E$V9k):ZMYx7]TV־NR%#nDl0 f'HE{ıN掝 VQI>--M hguj <:;'!ǠcexΒc0/ՔH .ZNx+7ĝ~t'U+;SEIcЫG$c즎+;ŽNڙT <3r]S0coBY'C,ae\5G;7#q3̞7:7{/Ͷ!p{V&g{?jo>#GzZ6Bm7?KWüz2iD#Y8oNq s>=ƻbxgzKsPu8qqɑMG>Gc9N tǔ2 w:_H-eL_WsMv櫓!bŋT? ~ I-,k;e;Í8i@ifֹ_6S;YS?7X53!v|e\Yux׫?VN YU}/?J;T1ufkg*A+{D(| @`%a~Ϗp_`PC Gl#*ަ@ b> endobj 152 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 153 0 obj << /Length 2012 /Filter /FlateDecode >> stream HWr+zFND.k(y; E@ )gs{@c"i*N}~wq,BsK\.Q'p}λUCڨ .4iyCquS=8|s>-~^Ȯ6O6ߚ6;4Wu;D } ΜS<#DHr奮nWWg=,6$p+sn3|qsz6u_?\~x'\D')#3-Ѱ}Ş%H{v:6GB #9c^Ƚ@9/3/gY$ }5@C=FDdY6l^ZH(*<ѡ)ӀH Mu蘛Qrҏ<E=8):=İY +>'0\O< r/+ۺھm^sd!BeSBd3[n>XZyU!) 6m1m\;?j*+hYv s/U~p,ruA[5ʈH ܵ$/4^dk"i+)PWy9YSx6—oxzcyno|B Y'b<&< M,1 e3NO"ݣFģê@B | Tekjg>bhriS]"#0ħnx!>o&o iTʳ!yF8<&#[&!qeؙVWW N'Z(.z,ѯ VEu[xJͷN'Eڗ?VuYf53Ux1Z 8İL=万%C>ncBȒ&æa_ZmhQgn: lmC鳺uA"2k >a+ؒ9swOrf]x]Vx98IyK S,Vѝc |Yk49&_"odl%IJØwč" (빢4(I[-%p/aߣnHd{P掊EdGlP'>(T2I,F< PU d׭x\t0 4c6l ܛM_LÛ59L&T9L 1G&U|uIFi)mfEp\ȼ /%m>돷׫ؐh":,  !l`=yb_uym he|&?yqw9 j !~9[u,?se=Sq*6+noZWIUUC?T[Z2oĨJx@/D(ު1;1NF#(BXgFKI4US8At N: JHCi|#?[.g](xlM']{R`*d;ڭ/?[l\λUjhW@ q>֡è,ČEd3Μ x<\n> endobj 155 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 156 0 obj << /Length 2382 /Filter /FlateDecode >> stream HW]s}#h@l8 S|'dv2`KN>S%n:aāS;٧eIy5uvfv IGfZ_}-1]["J~o;o̒tg4I҈:il7ry,A9PGgykf4< p{^7rE4tmG."rG~,VwOeB"M'TҸ=Oytu;7n_ ȘBfc9°ESx8 cA!< WQ4>>ƈ] z49y,eENH]kȽRk$;!ƣ3Z(nMQudA6eѾt/,ڶ΋+ꊼ i$`Al@+ȋ=%CGݒ4vzDy?-rO !;ị$ľdB<Na>4U@5wGátڔߧGV&17c\[Q;JbԸ9(^hߤ]/ -8zN)[˧d`yey-rZdֆ'qA彫!cJz>u*9Z%z_ݔ[;+@S^(?%g1*35KJ.[QELAYk4v6*g2K5&Ie Y.,ěQ&Z,bH-(2yZ" > Ɏ]\~8YTZ! !xƎnǮt?)2>hȨ 5b8(dW1(n`>R/$EZO+M )&h|DvuYo%(u%`2II1H-BG莭.J+AC7*VCY܈TZ&wo??wsF]KׇŠ6O=OWe7d&/ɩʳj yC~|SXUwŮSnؓFuxi6)HF`.Z7uCm@uȪqN #<ͣ2:p2$뺦f%PHvp{KԩC{;guxMG(9ɘ2X |=T{M @Ҩ pJ:Ѓ]~'x%XDcl %!x|Kƕ@~b`d # -+:N~&~JR5{C4߈ؠMS`e·:"T!)2ʝ.#+ph |նCQVfIA{ʊ⹪)1Ow 9N}XgKM =IKo#Rvw,p?L #IH@tHʾhߗ |QL" Dg=ZG{Mv?Z`?\im.L.ob=^-!t}EBx}Lw˅5qvRʺ %qPhxk5[lAwQ>IaLHwK$_ԯʳ͚-Y=XUEF$^`dUS dHbVS;6oCQ?T͇E߷7HAb&WIX,-kBlˆ 4&_rN| K(]\ï]ԢA;Yx5pb+~Nd:1ϡy&s9yn6Jӑ!P8rT -jƁhF:"P݉h@?̈./<]E ]j]_Fv87Wx(kjl\},ٳV}{6{Ƅ>RO&ŐǤr'3 ,oëK%o^eOlchعlf?CR**}Ju2ƱR'K?yw endstream endobj 157 0 obj << /Type /Page /Parent 261 0 R /Resources 158 0 R /Contents 159 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 158 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 159 0 obj << /Length 2258 /Filter /FlateDecode >> stream HWr8}Wt!/ od5vR1yA`3-Io7H))R/}ޥia]z⟾_ \ix{ شMxcXAї/_ߜp\8 mYpvh\\ F8scha."wlP.-r,EB+GnSmz1\ڥ ? r5y%ܦgMJ n'O YȈVBhGNOB92/Um庐pqh NUKg-6Vj"?F&qjfeZX7_}p^uʆ\Z~GXTYR9ȶ[Y*} m[m5ۘAT:ư"tU0hf+9p@RwGȠ{s:!<nh*wOM;n^`Ur}3ԇw02amwǚ9}?ļ GڬǹW@e3*8މ8/3 칡Ď=A2gaGy慪." Ey~L1+8`!N\8x:|!8p؂c4PpZ{V UO_Ug|G`&MĢ[0U5TC۳᝖A:tiAv ,=T^ܬ:58-t~đ=~5Jib2 Os= la%_+ w%mtdR"GC$H "d,C,ͳ#`X#a2h^oy{֒ p`x=|ufWܣ0Vp6RZ>.XO45"ͩZMt_? m"=RmWpn$*zYu ִm7Áj\;ͮiefeϒ.ay`l+ -I'O@IŠZ[}T"g'x> ^gx#U,Coi)k||ye#pnT{bK9iKar$~Ѐ34i^%4z8o?lI׌e;Ubj#-(w:Z266H*q!&=nIǷ猼9Cm #O8E~Oi /J cݣ-l2 \j-e:UjFգR='PcF˞O³\&UDFIEXVrӍ%5SKX"K<ϡj.Y?wMU'Z*LCC;!7T#6͝TD&MgH!PN)Cp\Tu[ƥR ,?SeQFEx:9]φM]'l t5˔6=F&u )Fኍ."!f飮ٵ:f("`^[Ynu" pfNqPZ<0!oB1D߻'fS=&C[0@Qѻ$ a&OHR ,ؔ; :` endstream endobj 160 0 obj << /Type /Page /Parent 261 0 R /Resources 161 0 R /Contents 162 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 161 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 162 0 obj << /Length 3367 /Filter /FlateDecode >> stream HWMs8WHo97Gd<9.[a={`$Dy%:v%@q픋a5o7˥ ,'4K3F25O JINVͻ{J)|e,WCnN2/4ɺYUrz'D!\R$4 '˓,? p\,52'"⸲HS4Y u伏ȓه}f6'|1Y/7syJU "c&)$2)u\>@:j'@ltj8]T< 2/2ZX*}{‘d/$\2_*#l1.,d2Kv@  S֞v]EK]9ʐu_}7Ӯk p9^ecu[kp΋Qk٬G{$ʓ۫26BM`YRJf;EU24ceqEbL Ze KL[$6R89zΥ\:$aVxP(ԃYZaQEg9) _P ?8a H5Fͳ:VMXMa$Q5CjJWtxVfE+- w[i\m#-H>ܙiR "${*%Mu8h(a)w1mV R2(E(XHS8L'6|p3ol>oUjL>(Th뀼yn6԰VP@a*4B-[&L+8"%[1_4 fg\-3novm=n5xhX/KAF2uzi ^h#zBZ4^aV/ .!V ߂)jڛgu,FI0#@9剮=}aJN݀X~ġBШ>$('8 2 u ?*ދUUnJWQ* -qҸRdTBPTQ)6T!U* +Uf"JbF|lĸ#Ҟ̀Y:FZ V+0Y (/<-1 0Ie<+($0<dLyBͲTR;9Vx:#-׹…{A6" f1bҲ4t]핍z\GgqV&] ټr\ۼPUr_Z Il굚嗚vtxy~n}k'Anh}>o_Rk^v BX3ːazi}:?Qa6y 1z}6= "iBğ_9NhC\1XC +yV:t2<&t`,M r$ga(E* n7#gVŐ@(t݉a6>|D(ɩ=Ɯ$6-f8(J:+ Ial:~7U1$=8 i1^5l~3CKZ 0#<+NhWLuB: 4FzlM&7*>ln3ȟAW ==,ezvH *$sZaM6ӭ;LڦħΪr.xҴN@6ZϟЏFvӆˮ7:pP ^àjJ,6@G1ԧWYI\$)y 479\]>, ,"4krWOaLeӑOOErQ0 S!iprLZhi2Usqp1kAi#Z•`߆s3$(uGe a;3DžNZk]9#&!F2ƝŨ sA%.b7?xH̑}ӽ,`&Ycc~~QftA|l] À1Lk_4p PJreS(졂6`8Mհ326 9N:%luᆭ6‴=$zݼlFpyʘ_ޑ_/LM+ZoO znZnbPrQ%h%իr!x}l^3؜L_J8TWtqt 2R+ ԕ[6\A+] n0UU,DQ.ՅڀKagJWW04n$!ܯ_1\g7q#qLr**U+cHVIЪ}wgb+uflmEqxXC/wyy#Th6#)Eq[GR8ZiG2U]^inHMa:ڠ^΋\"Q{H5# u~7 h٨^>YfCpH~DPt-bKt$Rg4tVTZMIHE4rdy֔ cQ<,!wϿu?ԾkS^0Rx!t+=5h)7iaٻlO];#;TYM- #tii .RtMiH%e4M\[J4~ l`5Y14F!jw @k΄ Z[2U@GUcv~"P]z66ݤ@aJݖ7Vs?Ga3>;ֿJpu\\W I*Ia>~KlvPwi7ob^+\}d=_|LdžXd I W=TWB$+^'3ɨH@e((p>+ endstream endobj 163 0 obj << /Type /Page /Parent 261 0 R /Resources 164 0 R /Contents 165 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 164 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 165 0 obj << /Length 3503 /Filter /FlateDecode >> stream HWK8+ttM n?$[нvQ9 `6?xS/@"fN;=ԗ2S]S|; ?HWSspF& 'FmH>}Xd׌ư=gF/އy㞸>6ؓՏ_?F|Π>$KRRv y& A@瑻ߣq'3'4S#7v3J$@ C)t>yz$b2#>=epDZKy6jF0PȀ,XpI {Lv9LbM>gs QҘ%*}~zfwH,U8"?LahXFjMZҖgT$~QkM>L('H"7)  S?=>r'ӈɎ"#W|f]j?1z(rMޗR}'g2MsyXaz,3.=8MfntP U 2ۋ8ڶWLVpb5VrW1YEc@[_I[(_`l&O]עh)<6mq\ Rm;NؚSho/5'iqNƂ@p rRH& <;-^27FhM,+Kb#[xsVkh+V翌;¾y߶u }-,L_#iBULRYP\yinK<ɋQYRs/7dYwr#cAMŶ8[B>Kؤk[g\2R[%P#DZH FP1wI)2uJ4#4 U 4 u|b1-ӾE ֽ=a+;4 =hrnR (eJ$d*N⸁Z )^LD |j B4,\0e @W.,L4 6ԦǺSHl[Q Y)%_)m t+Y^quc,4 `~ȡ$Ƞ݇Ui}O荋+}cEеZ ţCUT5f׵BuilYlD\fA/-t ZBuK~5l ZҋhTpWVRKS}>tX~"f)\Ksm'q[ε6.#9rѪ.`7Po0UKAK~Hmum_z7XZAu&(|ʟ̧@gHvJd )q׾uJDrA0+ 8Vjh]QPUs) ꩊA%u ZApK- X'WhxW^bw,ZIM RaqcF|5i60^0z.gnlXKծbv1zw`uN ya=yZA^"/jkuյ䵐E=1P&=tT=uB .4Xq-llX{, {l,rp€ah,Ԣťz,Aj^w/CUo 囮i1B܇'>O\aCo8,I.(B Vu1 ӴD3ZYa^Jf酗F ԀuH X'fƟN@PĀlQ ?-(Z亻-mZA6\Ӱ jfdýT'|imԢťzm<[akfst=o{_/: ZёI125W,%9TײBvTŀ%O)uO*#A :օB ຝT֫ઝ@Vtm PO¦>M%=.M[t9\oPԨF]fc7F}܆X7r߶u܊i裡+]_UE!qk397 Iobof8d~ DR>1d<{:v{}Qϵ M[熔 =G!s&]O{ X ^H^uV[@ꏢiu|`l-  Cy,Z9͐!6ȔA_YK< +^}24;;0=$h(_+ kC[镓hP2hz۶ ]('M^~sL 3@dULCh+v FHsBsy͍q.=?# )C]@׬ s5HdiAJ\bǒ<7.8Ua#'ҜOnEmW5W3rn 'Q_ZU6 C GwO.fHsY:)ΎC~$ŃA n#'?\ZɄVR&i3bWZͣ1KF{z9Tq+|'6|F}{δTG *MxJ4=-7$țc&la*-k/,m?O#@Id;VpbO Im%>nqQBՍzcTNNEE%?hېg:H;ANt`O`+M5:R'n*_ St&nBS灚K]b$-1tĩ9$ Z|t9/d)ay 8\I2d*CșC$Ո$շր9")TސIǹaq47捸܄7N-95H.(V80$zf7aOlpKֽ(ACI⺧gKC 0;e endstream endobj 166 0 obj << /Type /Page /Parent 261 0 R /Resources 167 0 R /Contents 168 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 167 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 168 0 obj << /Length 3364 /Filter /FlateDecode >> stream HWMs8W(oE H{*Vފ?d=02(%KR3~h@$͆j[3Kj>CCu4Zq8 м``?Yܞt/cw297ZG_O}Zb֜Egeau;oEuYYi2[0qduEnSɼLQ vZ4 LSp@sՁg7WWgjq]oY]s<$)3搘E+cEO0Gi_b UeəX>vOLܪxenxJh.3L;6# Z8m͌YδѬ9z)~Ę< @ATHTCT4&KJЖ(4e8wZA"#3$eG@ZUƔ()f'@2r?jCOfO(t1J1ih# dQCv ,}"{'_͆urz篍Y1#ˈ9KIQ Jz%HI#@树莈A#="_th{Xd0r% cFf+!m6eo crl! [/0VxҔ%\Ȧ-HMYDKd$2Z"MXL50ܗLFt4"wM5\ ;Ѐvt~I:r ȄHdt@p(j[_Mh`0:@5mC_jk _ [AcF#O'r5 %H[#S LFC ل$1hBM)?ȖBi?"'t uufG&P#pT+ ֒Vz]X>K۰*Kٿh=#onqV̄(m(گ`b[ySذ_[ux]o6[>g2yc}Zo]M\ q~cO?PaO9v J:}? ~Na|u#y7Oɪ@RN+!,[ẃ.N6eH3x3191{߀O]\^/X״?ƙ{y~޵n\ZopG`E]TE'e9r ;-,.nW$,;j{gml?f;@C w[J/{}4_m6O4kX])~{{C1GBZ39I h7* e_R.,]rHOw><+sȈT-Fglv{ n,:1Xxt;|ԟXgwŝNؗ\\\1VՏ^?Һ&_`1(ۦ\<+LމLKkhdqd;|mAZdo-MZqʊt11 N:ϐ MJ1w]ܡ hv$s2M% T` }ǰ$)' *Q[7P`Jqw& ghhZW+:QעƲ^OX Sܝ)#O,hb[d E},%(LYnf=QE2^ۗ7ol[4NFChDb6-#B2XRFz[YąX⾓9|`˼%!DJE4Z[;-BXR[z[h_£L>RM/ 7^׻ _^ ;a9),P6!,`I Є=)i M$4-~ڿ0;~iZ KPVZ'dG)#Ŕ(MN $4-t[s]1\Cy^oZ7@6,3Յ/~D&dGXorZX %iau(Y~"5]x 0n;ZDG̉Є=T)q%!CC i=HڐVeuzAS~Q~JUr0)M3s:Bg0vqe-wqT(םΡ3+)&ݎ3ߎ  M8gII>֛'Ep֜5& >]~njoBkv`}*J UՇMEKq;6؀4ݧF=>3g-+8! [>PomB[rǟad>ɸf1ljIDB5+^*'swhHpF,PS,"D4e^ΐտ0\j0T@?8rr驉NG^ QJ8 䘂"$zZiWڭЪ\cV |N_5hn4K5Y*U9`סt,"Rtzn7^ ")p"C֖A c)AA" .X$ u-sRj%auݶh%${R}՘zj_*V 7Fuz4H9"oh}JA@C ⸂UJuR~\.~ endstream endobj 169 0 obj << /Type /Page /Parent 261 0 R /Resources 170 0 R /Contents 171 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 170 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 171 0 obj << /Length 2084 /Filter /FlateDecode >> stream HWnH}W6f(^D`d%$ "7K'0h69U]?OGө MI )B)R ⪕0k) vtiә}SndRS"y=+0yijٞ{x:2{ (r$P*T#@T]P4%p6 Tp-`҆SNA{ O^_7WcL/Tr9`TQg"o3B"1S) z=@:,ƿ$V"#,1.L,7$w <"'2105Gy3/F( gMsB?iNXwСA# y6ö\VJ.8ER0-*KVvb[GURE2(pfVZJM~Kk ?/YYE)ObDqǁ|Be,E_ٶL۶ ET9F )|>4)O7C%'bff(E*L,F"EnѓWEUGQq"%r qzl0p!3<v)?H,8Jb!~w&ā F >%м,?Q;=_!S]S'3E~. =3\ l{i\+"B#eײquD9BYDEok8 ;Rn1rJEVAQ3ZSSrJSocx4Q<^91hM#LjîAp] Y2Pyv1VyUeqT7k}զm 2e%Ll2  !v,&ȢVja89G"XX,bYˈX6"V֋5+Ùϰ^v1&YxtIw^]Ay xl?*οn<b[ g-#yۈpA[/ܰoPb - A˽hܚI}0,Օ}_b0 79X > \L+$K,-e]c2&BQ̸{>\ц DTs&mj{_fպ+W E={9Cy_3xmMCALqo0jatPdP`[γ,\g<.OIx ?ܽ~?XnB--,Rv0ޘ<2d^7U\nWWZMׂzSc*DGpW5rk<ޗ/srH@9FfvJ>m[`zuq}ON)Xqdq2 Tnlb ʞt:qqԫjS87.XqCf4KۿiCKUKr@v5Yd;+3Os}yM(&Ϲ>qUkpѼg#u.k<VâVGT0Id\t2lRt)Kj^4_"H^Yz1fUu{ǠRFA؃*!g*ayb;ISCe>:pi^n]Wɺ¦g_urrw4,O }Ӯh'~α39YU3"sL8?s3ni3wxh%vc:G13JmЂk7ڙث0\"-*jUl\c\D3-]~0jۣk>7jc1'3ǓH?(8`dqXweY14ҝ 2V%Կ#QBk֚mvg pJ[9>TKOxq=-GeosdaM=j 7/NΠcDt(T nىcFVЬrբ٦ꚥ GlpC2;,u^T2=7M3Lb ;> endobj 173 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R /TT13 244 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 174 0 obj << /Length 1024 /Filter /FlateDecode >> stream HVn8}Ẉ\Eo]l.})"Ӷvm)IY/I6`B&gΙ9sYpe1PH ·_I I(l\Mq"0E\}^Rؘ`ۣHBV%s=Iɔ8\EiL YrDĠ$I$J:{Ơ"4BdSEd4cL> endobj 176 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 177 0 obj << /Length 4612 /Filter /FlateDecode >> stream H|Ks#7, ٦ܜp Fڷ1@ n%?M?ިxV1mvUُjM?lpNu Ih[ V4 76X n-cߏJ4B[F[C9tbs Nab!MV) 9p-플%Z[x.=0wN:%F(oUKwe)(9]6 㡡]hy< IE([x.+{; ۭis%w sZ OJO%6P2,Ӏ(t qअ,pङRJ8pۤnh Do-@CS3l_v@Mzl߷'/Z<ܠa&3жjwJU'M @klڙta" ˼/FLp$H(GR?V[Z @V $D U,+uמik o-s'_tqWn@.4M !O%|㽁n@]hkW RdtTBfrH)mRWZb]p9:_sǓ g[-[-ئDi郂BB PBEJTڌI%,حR̈X$Dk, [^0u~[l:R⛓] >{aԣSOB!$SjKVJ=ͩ'zvaJF$0\5WYc[OtkAi.LOR (amw`l?=oWm} ^vSC4ɌP`"#0#63BRv) r(#~Y]3lj\Xr6kn+\Aړ B\ ^EM!,R ^)Z^KdUBkKa!o-HcWFE|Mm?L! AR I%XiF )%mR O%xkqeJ<=|z1@GUwP6k( z DwJ8+2Yw \R.Tf.H)mCB@6 8@h2'.^:XI 1,֤i U`j:;*;ass]@2'8D3=KO_ K=M2({ 5t<ԆvT󵣚yb u#`ү-; ^®Q.gQ,bQ+bt.FWN,C_D@Z2[*ԃ̔*dY+_C J9GQ׎jFd0! Ȇx1drjܒղ Sby|6V:֣͖ m,h3Jdfai,͎jvT32uhk㺉kOwAa_Ry74j|l;9&NɚM W͋%uR Be8NaR0oN~( tNG8@YwL=y^MN]9n|P}ݫF/AB%iۣ,0^ԀEFf{f{ "gURSJu*N3*Nң}AϚƸ 3&1:ζ'H'wNDm^iF>'=)R͛{z|(k-pv젚`Okҡ2<,t1ŽcHC:KwL I )stG%ARWIݶJ%^R6s9 endstream endobj 178 0 obj << /Type /Page /Parent 262 0 R /Resources 179 0 R /Contents 180 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 179 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 180 0 obj << /Length 5257 /Filter /FlateDecode >> stream HW]S}Wq}kaނ]g|C~}W W.uԭ3[ӾBDբ֋|&-h~yrNo),o {zT?Ob60*iIܽm^_O.JgEdB6px? Hբ5bq2d 𨬁8.:Ec܌ysqu~37ongg?/g=6>-uR "ZS&1@ Qjo/.F9)gƅm}awFdS &t)vƍ``^(~rz D|jwiߕdhuyv>5<%Z۰^d<욯 QZ_'6L&J\hq毲@>F*Ʋt@/,牂\4ԫiC<ۢnn_3>Z@5H(i$L0 ٔPנ%PV{O:; @ mv}qv{ZƟxrc*v蚋}zæ[U20Y62}昖|w  ^guApk[4nj-,LwnU׳~C.d#-52>bU/ןݶ&9& u[ED{E*J',t; dBVCd l.5"Xb s:wŜoP1GJV/'BQ9*!uĘ >oZt0Auꭂnx=ڦ>/B// 7a` K_%tBnې^M]=50x?vP//7q=Ϲm-ԥu/ LkVg,0xpdg~T߱tIɘ3Z5V:kVE}M ̩3gaHR2yޤI q6Kz%޳Muz&A0:m3{liZcTXIxk XL-0Ɉa/twTׇ75Ӱ-#;rC'n(pCEYZ|rjdh4S5npǤŦB 2֨[Kĺa;=t(، %$Zm".Q2 o5r$]`@s/hLZWy%Aa![c!h"QSlg7l45G[xD<bovk\T1Kɰ^@ b <ɯw4&ͰN=P3, >'*)2'EHc3) B K )R5 [E6xc 5h8>XjG6qfl?O̅ VT43w08wfߋ'/&'VNGLW9ԯ1b{pU½qf=fe{Iɘ۱-o-n+qOh 9.JwS4C3;$uUAAS]PX!A`[#̈́ hQ) Ɩak%ZZ"bVXL#}mncbi4_³I;Q <аV~VOjYl)bs Z+?{TʂVIV~Z%"mIʔ\&d*\m>/3*Lf&^~A(1< y1t'wELO0*JԧgT ĩwh),r.)iй ҹX6_^^֋ z LcO6ad'% dsz6ĶiܴJMFnL?~7VhA_5T"`8\bPKq,r{㛯 ~M_'}ϴ#P4gO ~f3Q24iL ?Qw@J~ [¯!3z%h0Ѱ~v/ίhǎl?n#~kb{~/;FUHƲIH833$OH-氫9UذxAEeJO&44)NҶp[QKG-#Se{|z?Sw:2'cfʧg."E%xUq;~*οW Sn-{~{WPREyZM QHP*CQz "qz } Te]VS֝e\senGE>D(t|:ꀲ3)*WVvluӂXܿ/ o3dW㧧q!3@@W=̷obԋd6~iYszzLҔjRa_$:5Y|ٻcYl͡k:@򬘻ֽ{A52|cUkсT؄h˫DJM l[CN%ӆVpRѷWۋ{}_{^%c7ܩQٌ|fu^`zB:o7nk!#ĉ>:ń#~1!QX)4ҡ*FKׁSN ^1ѠSLe\a bûO%QL|- ~\yL@Ncm8&1(]cGU:@KHzDB!{H½14*zHr#-YlВm>FZh[k\ꆾ!~e7l}D'Z?,UDf*P쥘? z勣\a0uwN.nܹ帊U67/Tmub9SH!z\ EP(ZB,U EQFfQ*ǐjG¡9کh38jeu(d:3¸~:_l2mPXq{Hn% [abt/Ruߋ]Dc&BD$Z ,UDFf*@]~Q z>/ZFpY V̘qnv+_o}7vHٽ/jW5bgP3)BIzHb"JH4"%]HO9wu;= qQ.YU8ͽ Kk7cMF9龋׻ӸݐK"uuO @瀬ޘAq$j+j_K\`;VpWfQM*V9TP)SMO [.TNTu^sh$"i~7}ZTޫ?YP#f, iR#xN?5?ZG*aK؁Uo=bx8, Z6hIavZ\Y-Ine0tAm9[R;05TjĸjI+hM*mbj&``H_̴K z(e\A1г9+(D%ܚ@/RW4]Cs/ji~,ިj3.|p'#H3QX<J HU!ĕNr-YlВ=Zg.6l2qot^y"} ʣ6wfYUˡOxROxPjixp|  j8%1xC{W~7wz?PMk_B! .rGUD>]zVeۮB^髦' '3&Gz< E!  "tBPsu{SJ{sA5$ƯG3ˋTns'Omv/|_}n LprZth&T׽O؜[CՀsI&dfX%a Kd.!u~b鮙v1ZmZdӎJJMc%KJ7x endstream endobj 181 0 obj << /Type /Page /Parent 262 0 R /Resources 182 0 R /Contents 183 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 182 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 183 0 obj << /Length 4829 /Filter /FlateDecode >> stream Hėoo SUaovll'pq[hB׎ [IܷH>qi%>8-!9ϒ&&nj??&A@ֳ7WZݮidF5|rͩ:UN]}[?ѿ&;8Numuժ?9i!Щ&mڤ*5ਫI6@4nP7Ɉۍ=NΎ/ǧj2&g]/{]Z2xfȳ#mB@mn[= mkSNWh}Iޞ/n|smEt?@ԭm;=y7$`L=5OnZzmmC M#[{_{ [RLVooF,_ 51XC䇺܇QX*W7#Wj4/tdۏGzbJu#5:^9`6ɦ&o50d3p Ckl_Zc_&AږRgG`2#WbB*e-T3s? uB#mwOf8ёnsBiߡnHN%ۺՇ!OӔd٪_-=ɺjqK}JN$` mmJǿ0痿*=m(`j>W8PBuQ҂?[.JP<3:^ߌ ;Qfc!mcHkVfmJ!(p+,E!?x9:ò>%R}Pd5S6Җ k*wӏwX": m]`Ja)P-lXB &2Tzxe~P) &qb | uK&~'lF 7f_ycݒ |rTY!"J)T+k)Z4U<c4x"K6,k,p[ l~y#mS ]rqSr]D%MshC?Ӌ~Tî>%'vm-c4()FI+1K3$e(+!7/4.!(z:st9XQgVW6V2ŭ$ދW |&7ސU޶:}Kܦcvձƪ TvMPwR@&o3TcS븳wBq=3~.,|8h8Q'|1xgT- q 6 5~z۫3X|yxׯqpgVuOfmƚTNo7*Z|뮺o`^Z9h/k:[L:H+wH.}b5$J#`M[Ҷrq:A 4?bA ՂR/FGR:=-D8~Wc˨nfLmU*%^EKFT7&Y48UvprCL0tRl[E۳#aUX{׻t )#u]z~C# D  hh"$ -hdhTB B o- -#PrX-cS+A:T@I%Xilhtp o-ٔDCڿɭT+\ok6t$pqGp,8#dyed%̠N@"3 34#CJ.LJXxd(![8n+ c2;84L$LB30Qtj)pUJ #) zKM[#aFdGQ HTD$VBf$H*!JYh\-^p&oYcq5o-y$lw,7nQW 唕J*JVQl:QJY> =%a?ҔR i %AV:vYPb r1R{;+9m5!f#l5QZ^a#jir7D4j UV?%r=*7Ҝx[0/Ĭu()quwH('Oxz? T}J5ͺԽ+k}!1g1܈F 7kp{.FD]ͫռ"7-M3,ӌ3jCc5p%.jwH -))ȿU8v;u[* ?3|IJQfϥhd663HKN4՚AcřjΒL wѶl$j^X,'ny[ŝ :yZGYi&o@)'ϢuתyAcoPN޻ag 'SLYG>v[ ;6P,~ ZmxZż/&4vy`9?vSKWteԏ}wo@z 16? Z8T诫W\#z=~@Uo˰O|]!X]&ZpYh R"?@0u"գ Q=YÔϖ*~c.%.K+,`/C;}zT$l5j%N#GauH(תyAc\m:?y<=vد6A EQsWuGyEGKF3K 8j¡z3P[-h0roq^c<,e,J]ƪ iqY~f&*[r:7"d.P d aU$g">4R(Z@V*9-0d]Sõ'=A IT8TmBmEG+OP'4\V/ن֜mhV ՛6jʻ eӞBx肳=j$˓tϪfc8ɈcstU(VsZf }$+" -!c+Fq1y,@]yy<ϫs,GS,M ?b^^_Ac3e]jnߙmϖmLF"Q\Ko2t;Bl󝫞Wpد8 +n:''_RWOo$GJݣa>?o.iUEůOfS>HtK[ץj65£vδjmQxh8O4b;Ф 9~Lb(IB `͊?\I\ΤdԢLYJ&#v'R\qN endstream endobj 184 0 obj << /Type /Page /Parent 262 0 R /Resources 185 0 R /Contents 186 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 185 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 186 0 obj << /Length 4274 /Filter /FlateDecode >> stream HoW[) f?IHl7p؇Xw}kEb6#&;?3;%mQ8~vvbv0Bf&P詄˝)X|XB{) (JXѣ_eB&[V?Cub05. f~Lt5~3UNﳋ:/fF`0,@WG+Ϯn.>˻?}Z/-?^.p[kk\jk 沬A"eXY8ʣ@Kt[*tJJ'0 *ʼ|ZvTcG5( XWMh$)Dɢ̥|egЄqP̵!:&"Zm0/s"3q6 k2/1;rU2x~k:wG Fkf-q!mF;a|RqL=:Ҋbﻯ9o?WrP2!nŠ5}ـY hxz˞+NO\}TcHͺA-kљ0f@Z|~ ٿN :e^eb)KdVda\wUdvga-W6=,j{X/~ & 3(䄤LFqPЋѸX9X=zCxݽEH=Az'4Ky\"eV+T`YQJCU=r|3C{z>~r=V(l}yd++؏eUTXUƫv,[ak!92b 8A/:iM*S|KcSW~# oGO'tqR<(8٠(w*YkikԔ7En UOz=;?:oA\-ZT. \T*sp_WS/,i - 2/^5GziWB/K*=r1 8m&X >ڟp7?NkjZD=5Agh/MYiB#O?yO'Gj9qRl]' |YI/c y8PΛOoWM?#s؀ц`M\ H\5m|k iH6 nt+[ڕp 9ܒ<+p5N[Il=-LGZn\Ӧ)qi"9I}cwh~j:eB5I.T٩kB:R b;Q,63@V 5 ^w͕na4*ö!`:l8Qqqaaχp{Ub7B ⋧pE+'.WMǡC2갛}rhы;3]q[G`NmR`3tVg8`,.k=6}Sf}F^c Y =$H)%tx2r1=tBWԛ]OoU#~B8Jݰ/ /`&/-6@BkJx\bf36]C5!,]b.N [ow cQ=꾆m?&|9tOm/w3vrh􂴡Q.~׎Ha~hObD\=@5eCi#]ES\[G?N #Y˄{OZ'فn6xnclH8R6Xvvip~.W(X 8MKr@F;<aÁgOp0Y~Vt k79cG %6ǔآ 2į<i dSNQ,c%Eic5xZji/!w,C2!@ vvכzE^$Sa\x5V5lz㶁0Wt|B$EKM ǽXb/JLz kC;3-?k,|6o|\+|3UEh6s̛E<:/mh:H&tޑ*X*lf2i[mC{l9ZEΈrz0geYKG*?\ړ5hcd,*mZ, 5&[$4bE$X/I"L"ޣ)C(P`V5XWal mVfxߊӸ/w8MF"M }IDu}pO?]U ᪿSt,y8>дtB&^ C}?[X@?3 Tc[@R'ZѺbs6Mf7]a #p n5WTiRϟ*mw+f}kdfE4N=@1ܐB`;a*r$Qm{|Јrz0'k|v>n4 +0ff/luo<'98Ӱ bK-T)Q::]%Iv6|E4HBYI-!FZ=90^; ]4mly7c,Ks2)|a,|_bΗ$W/urUCqТ̈rz0gVA; G`8ux.DsonXb7k7nÏWE,:qp(IYı qcN$W8u|.ǫ]J0c7bE)_#(*ĵ5וU=+@W@0Olnp6Tx pxLlS Ŷ|.#s` OmeJ`?q+ՔGkMDE#*RQ]NEP19WQ}r7LQ)ZQWUD !Yڽ] D&qoaFrK R\|uʇS:pNQ0>%_% ##%o5ĢI Hi* 4"0S(ۏnr5f03j9B\ČJ0#}툸t*ᴮ| dp7cV鮁2OdV,gF%tKYwC!spe+-4%A22U$IeeU*A 2#Y9Oi! t&z40.pi+pk%.1pD%.g!c+Le4c*%X3:ՂNݦL>L6<=3pyrcA&`WG*`K)gہ7 e>} lW[ &6o,MKT/Wq?O*O> endobj 188 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 189 0 obj << /Length 3567 /Filter /FlateDecode >> stream Hės6WL7Gr;X=H;ER3ѿ ,@Rs]|X[. x ^r8WӶ[6{{[ {yKp}sm/DzlKo,6Y4U,%wkgw3;S 6_svQB+: LaJWVj\ϋWnէ|r{ywuۛ+wV[Fsxה.g=*nm^U4/7?1 NզE8eq}ati*'jdY{ow X׶HB*EOBEUCcV9 ^ Ip;&Cy\7/Ui yD7_ewͷr.ڧ˺A#H縿ykynᑳ\!g \•i X&}SHIdUXTX w`YRKf7ii!.v Gvnu/sȇ?)_RYw/2kaOR(wvx~uvU2a/h`כ ⊈k=vkAݯ~eɐ3jmUha]:ǣR$n\ 1*!G)3*gN83dVOMP(1 7b$5zKXCisXC2 $HzXQH2n+o;0dWGGGqƣP0& +-4GU~|{ Q`0TgY+@fshc豳v̑:3 PI9HHK#I(PJJcl! r+ 4x'N#V F k(?m%)hryxnb_o*o+%U\QoyC%[^qXEWh$*aMɣ\a  iu:Ĭfbg]];L{ RU\EIȎ(Xݭz¾m`.AQOWL [t~,g˧k`:5ѧK#( Jcl%cۧxӀ&̂( JӛL^m45ʎvF3s(\\],`x1^u 3wp -~R#eiJ޷۶Omnwæ Xu35|jФV|f}zطR+eYX4aׇ-`?Q\[?nprE!s;< VY+ˡm)uRFظ=7:بSѺ(Ju폯tbm`0"А 龎DپSVtFyA!hX`}'KߦK-woPjpPU!$/o'$h7&o[X̓9t t4"-$ hH ѩ57%7—Jq1|χmflُ0R*v,90otQJȅԀ,>9T"6?`;@ <ʐL6ȗHdK }'^J|N鍃 |=E*s[O;X6Ń6~(B+EaɧE x"swW`Nvx OrD朏BxGocް@o{8tATJ-o$\AHJ 2J 2Jl8uUd [5&l NL+b|w7V[YWa窠w-.7x^ I);췶]l@;lgA\EYiFyi$GX1qAIhM0pk8oM>̓j뛺թaAͺ|=:(J.fl @)axA& a(Ja(K#a(% iIRh$ aDwO~NO;x-ְ%ʲZ8jL_8S548\&5* PJᖕ&PJᖗFPJ▏`tYu n5z&iQVl~mimRQyr}~`Ӧf&t8euٳ6],[#VWQ})yKcl "t] #c0`.YXim1I2TCc%L h^5czK^m0+94l˖/;l롗X5. 3?Rz(м<>epѢ355T-VSMPWNO孮?=n8h9APDNSUBn;cfqѰ$2M2;]?qĊ2+"l%VHݫ]}ğ(j^צyW7a<}ZQey:C]5Szڀ/?)XxtD K>DK D)䃜˶D T 8R+ Hdcx&ΖotWkk@XL`~}'{Vu&یgt?8P!NN|()YIN<lV ~jE^e|DVl&{]@`SR t"f!G6StnjP9U1É#rb{˛tL;| sx k[`yNϛq;J Kȑ8HbE DXIUQ+DUpӎ=e#WQ|$e1=0\]E_1ID3oOS endstream endobj 190 0 obj << /Type /Page /Parent 262 0 R /Resources 191 0 R /Contents 192 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 191 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 192 0 obj << /Length 3361 /Filter /FlateDecode >> stream HWr8}W1@T;ٲTԔ;ڒ%(% @R`N,ntNK!LyD[ U^E%go޵B!͇^/ZLUgهKq=27˼l%&=Mqv9=5ܔs[ VlI@jaP.ZrM}6n򺪌e! o4+NSټ.]"]".~}Zxwb2=^^_Lї[k QwM.&2D81$VUE}K kucrFV}JMME+Sx2:o,O_ QZ Ŏј. ,6sm1 $eQsIj/\5&`( +(]tŐ_^3l7*!%u#l͏ħMˏlX/)W ddQ|L%.-81h݊ךCwfb> _e[[gHuK%JDW۷s(*[LQ. .pˑ@Ŏ9ɶ#W+f[3i0_Ow-v'>m!Dbo i:p5O"i'!zy\|>rnߊˇo>%8C'Wi'W~w`!^daUIi2uW;m_tz$l cU#jP+%df]%AFUd`[OK=NK Vh8v-Q)B2Y"Q*OQZ$ʹcN9ǕZ;rG㒶# fj$?J|N,bKׂ\mO,eEZ.ܣŦJf'ٺE.oqlZ;'v>.zP"/.mڡ(O;*fs\ZK,R6*EcM5MBM=N"aGs:Hw7Db&h∬`YQ`&eGt]Ͷ/˪ HidpO5euH2j'^'U:d8RI:)R)RET M*bhT\ee[AؖH92"2qXgDXB`4zOt0CHOkt ^9VH}܈n }J\g|zfR\o -sJ-cwTAv~<-0qMgAl뭱#)`JQYa0l3\Ix J$7_j_ߢV ,YYh`ͮA6 zp3lfPc%)@cg uj sg궱y7Lgb֊??\ ^{*nb+}O#b \Rm i@f1X ]Rj16`=T1Q`H>HVF[CDe]){:pnwа0‘!G./HtSB [\Ưaw1ZTqɦ[bk[Vq\ s x72VN&a&>qYհ+cYu/t3X ; 0 K%?$m3 tW o ^hٷ@"b"|j k0ʴ{d`uʬPq\ ՗ f[Bl-p0|Ե 4h]/!p]C^”՟^ma^^D7/5!&*.dJ˷G&POtSDeP5:H7SxQtل|:Z6'MK沪@q2k7 A5 h^B RIi:kkO LB)TL)š@ M( ͕ M.[RHVL[CDe* A}_4Ps'&nq!z5bZA-D/䇽ޏ6O`:(>nr8"\ Gߢ77Eh\DMэ)ŭ!ܸ5D]6N7sQ^%l%ܬq: R;_y'PC j4׺CqԢLZgaGߐNIA)8%cظ9gЎtݦR_pbI-뇽M'S' Xq)훘[YSZI"` 14uH:I1d8H>IIP._ڻD6"jqFm>rch!|ԠyIo ]6[w>JmnɆ`0Vye5|<8x`G&U | ?b#S8<6 šy!r ߩ{cBo".ڄJȶLXDG/f;_֫7aѴUmҰpFi8]vh7*`ωhi.Ym_}uxWj>+pAc:a}w%]Z5lږKH(CQ<4R"(eJ-Ad 1",PG3# d}9u|cqA?W%Q כL@*GR"\p%*yb r TDDE1"-?/aP=M|5>-U=RuU xB.jw 4uL9|w%te:(<~>RCS'8Q7l40g-k|6ݶ>zO5Zc jr'BdmxMW$ P7I@ԣonగ~yJ: wm§\U70~/Pԕw&%ҽ^&2PnHp (\py k,N| ڰE 9N.,lE%\~dX3wR|9H=γR'CddxHn}NSGr endstream endobj 193 0 obj << /Type /Page /Parent 262 0 R /Resources 194 0 R /Contents 195 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 194 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 195 0 obj << /Length 4429 /Filter /FlateDecode >> stream H̗oWbfvĺ{IL#'}AwvgVhp;/5Y|i)("B ۱v={F7 Tlx]y=0?ѝkV8|Xmv9\].Fw*5aqC:]TTf}vw1Y f0Mzno_^]47닫E6;B:w =2xfг,6ƭzrҾ ,lL[.{r,0SSq\@tƾ[oc0mǰHZDi;]su:v3X9idR8s2m޴4iмp44zyS}hjTk)e5iZIufZpл̶y,X}?Imq砛kӰOVoYU /eV߷+`JȰ(ŇY@,6ay<)+l`:vE2%=e>NiIPvuo8q"dWw؋"[Zb` %qlfʝȰɈ.oF%k|\o{?0ig߽1ѧES3<㿆Cs;'|~F,<~`L J%Yi%<dce?Q"$Ycu5Vo)#@PBJw6D9n(hl+NӻæU8Os8 Q\lJ2e(CD/%P*QJ+eD+.%CQ⌷VykV@Ϋ,jLäuH!a(RJ:JRuRGKԱ28]"O%xkViQUt@g;*QbPpmqSi7;q4TRi*;ǩ u vTBW:E)*+9'.}Ao;նq3R~{ZVXAX760E'vR) [a'E-'I*'P]6UԔAR85HkP(d *%9eEܕP&c2{Q kHxkViy}ws[9Tn2?'}ʉLG2 TW~E)*+~DgYtGF?J VV/[ Q֗N-y)~J`:z˦#)yH+(H@ R"z, NJ5x,ZlQz kβ 7@pיoPB>a} x\=Ӱ⨵SW0,lr;.7w0~潒Zb[NR V‡>(aX‡N_<ȈuX<kVi qEMsVݗ^c90aSEDߣFhj6;VlXGGJRbkyiemXq bSŰ[zN+ _<7~ c"wCtX4e\4P G)%MrM|e \3l(`#X]J8 euR˚\BlX>xka%$XoYU;P[gT"$Tl1$3F)Z!vZ)+t#?w;HJD.Ʈ<4,BJJR6rCKܰ2U?uA0[G8J?U|0#RM!`%g+yO+!}PsY`V8mm4Jd[z6XrƼ䦂ɔ:8Kz%-dS"RȽ yH*+'R DJ+0Ɖ+&)hw Ȇk_[+UktZt6L>ZWX>M?%rњǙ&bW2K?W4dT8~wO.~c2Iyj5ude;9[hw|x‡Cs,(\u'K.b.~57:/jo8}gdtF1uxk6vZ!h7ֵfxŲ*z.RL^;5<:Pj3Sjm=]t}ky>qX? C8?IH(" tSRa5ݠRa%RÜ af]Z=b@R6R?8fzK|V>`?}f˅5aZ)A.{m>YdA0# 3_m1W^챉[@QM{I$Ear1KleoHA ʕ  feBhN r^.Ci'%9B@FXc1yjۣODѳ_۔h`$YbyŌ1k;, cİbѫ$|m$]]U/݇͟96v viV#ĥYϗf J_!QM(wbK"V#ʭ(lubBՎa,hidb Ũ=UzY\[#}{ן7}tQI=SlwOݳ|R$ŕZ l.}TdžզM PV"@t(sˀVʬ (h bՎAL/~/o뜙{|6XoVV.aӕe>͋__K.~9Y/Vņ%ExgPWIbP/2;~n$}_j?4kҠRJyYaYYAkWrj V^"f"c Q/V"XY͵Ѧw'm0?=vgӼOc9pxY=wyW H0I4/[@25v i4h"+%N&,zNOG`XzTZt[^Vw'^C*M%Hӕܜ*2թ2+ Z*Q{cHcXLdV^FE𱄪N旙ijXW:>oҢ*l/8?|-$ǒ&C^_m͓<7 GL {lh WuU*-Jr+s˰Vʬ +h b4Eɋ~U¢E>U&kDIiI?OjLeʓzwfNhfq;Oj@Q8Nf45#a # Asd`q0r޺Kֻy뵛>se:o\*.Nvw endstream endobj 196 0 obj << /Type /Page /Parent 262 0 R /Resources 197 0 R /Contents 198 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 197 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 198 0 obj << /Length 3584 /Filter /FlateDecode >> stream HܗsW#9c KfdITTݻ@rԪ'I"|z>ʨJ[?>TO۠|VjQ\uKh&o,LٙaxT7mwv_'WI^WWmSŠLtɛQ@&qP:-rS529k&Z;H3`6VNbp}JWW^s)Xkں2Mˆ Y kt9z4}f~F֕揭 q\anjJ\ژ Vk{I nKn;etS|?_koʶi ʑ=@~¦b3cv0=|[󇙟.fuBҥE]U;i w ,SG`mj._i\t׿mJKvܦ r@LjoIZ_!ڢf+X YwBu,,Cm~?MofB3* /r}`oi|i'G@Re+H"l[چ|aWi iscʑ]r~hU.kcGN(ߑA6V/*tj?M/rgzJ1=(|ҙOg)O)/\.5r2:+[eJ,2E,ep4+۸)~c/ezM׭2ĩk%dn @5Gc-{l/b۾$0K} OS`ۼF6k-lSlXǰ6xyD.˳]v=Q0Dh}f缪V]WbSo֏[u}~2V#V:Q|`u,w >]-gi>_or$ OD=h޾NeZ"y[1[b.X!גW6jeq#KsOyr\0$x}$a; jbc+G8ݴ^Å7_ftтu43>R*Fo7-aczv3!g==vn彩vTmԹw.ƒBzX+<7.I}xL&ڣ"Jj WKD!uO (=p#V`2.qeD2pt[-S;MmGMWm}OAJ ˇa8?zuE"נQvC@g,Qt\}/kZ(ʫ%X^-9(ͨoƁ8}wSԜ4-ںh@C4E_)׶b3T3#@z+aV+YyLҰrjVXbe T$.ۇܝVf..pv^v[TW lb5R%>sř&a.KK.9y+1[9 ̱Nb,0fEPEJKX~8; #|d׳z-V :FS O2iTwy˳?|7n/+3,tn^c\7Dk=6ûK҄z[ŘJդ[#$ HD:ZYg!Hg3Eml<쫨aw})V^h%ym7`_Zl+Y"'y+2EV)ILyL<@&tO^,b[R`*Ubq> !y%x+1לyga95bjkkޒ pyD.+7ihѲ4OH:+[& 4B,fEpMin M(J4j WKDMLN[`ؠh&ǤyegG.{7 | /E@(@5p0pT/(zQXYm`}'2Y[!3)`ft4:P쇮XyD.ciںgdriֆB?6!+сtѡS KXBy/ݶɡVZ]A} PEܾR^b'rVh:(XE%uՍ.D 5* C#;xu6 9  l=^l&fW# uꚏ#my|yxjլ+x+mb6Mڈajbq9DM5GnH N3&JQD2z0AT0q5jD0M6O;L+`j(-6v/7?_ _N?}{C};k5'8#zeh){&A^} 3~4).a, HU)xnέQp;oem ަmr&moS;;^OoMͺ4ʾq܍]N;n `Qgr hR\B@Y.W"zδQxp1,Ύ`8oO_0`7qm@@No «V u&Q&KXkDDŽ \Z#9{gzzqӷecN&uЙh)F#(Fzn1|H㘽v[%A10Bq\k1scQ_HO3`GN endstream endobj 199 0 obj << /Type /Page /Parent 262 0 R /Resources 200 0 R /Contents 201 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 200 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 201 0 obj << /Length 3915 /Filter /FlateDecode >> stream HooFSՁb/m-| VMz82noٝYgԦ EÝqv|qvOT۴j^uW;_vڭ'oSէ)޶]X᥂/'/뙯٩jl}Z>Vw_wn?O.'y 8[Ů RMm|* cgTmpu5[465ǜit-$Αg7lqy}yHϢIZGl Fu ")LkћTcS_ D:_FrTiq]Nt:Gr&tә.S4n&߬_m OjF{5:*Hh4t6%CH$_3|zEc|;3݄>_\~A|B+L)QBm0"vXHC*n6Or{.Hl`$WY^úe憴M9OBJFMk m`IjAK׵eP7$!]Řl.`cГE"u0^]5. ;\yt_^@i|c&ڽ2gRLYAZqHbePqJ *+.8h9ˮZk?39w>&zT"ɓG^<葕Gw{%`=~o<{Yc++h!J79X+[8l* 2`UM W/CY >O1xjF#`NC_R"6NRF7z%y+VoYٙb1ݴia/I0vЦpf/d6$֢f[% uP1bHCxKع/& ߮媎-vg 4muw<=כǾtQ<jz?gۇ_^fO< БۺX~J3$)*ZV76p.\ham#KDPeVAP*xiX\O)BJ+2*@1#ϻ]<vgkI@A~Y*IÒ+Eed[f- V$E:[W"7lh Ynt++wt©n9,㤪RMU/3@*l@AwD-j :xRqʎ@]i%kSh&8?r7 YR )aw2%p(V 8ISs(\ 8ngS4_7jD˫% v?ePp\P~d~S1YA]3Tض@YOOl4Wwz?=,_0)IrON'JB{QݕCB$Wes<;~}:Wqz򫵵 &(o+y&XUajXX(b\1 mbVND6i*Xe 7?]qGl>6,(/1 #d}d 5|۰P|o%'z%|x+V Z%|عb ^I+^-jg(B*( {{2-l'||=WұN/J|V _EK|si0`RORJ |u*,- cb? yV.IDZӂVS`w+ ?OL͚ *Z;3hX35"Oh՟/e_#:6wǎ؁xlA$t)9 :7Òm:Z;ɧ\z\.=Cetnu)蔪3qtZ- So k=ΚcMKa ^6t Wnhpc7X⍝+AD-knXe 7MBu[c*TimvxY#2eK&xJP⭄Z%XkA"PbiPj;h `*Xe 7$ۻycfEE0dڃrz"Iu0ok \/#$S IHBDk-$QI\st4^J$ eIP)^AaBیۇfn: Jɼ߭r[eCٲX: g9=6Tz2dxO:g$YFY/ "=c{pAH #67!hy~z'I ('ʵ=Syr/X'y OW)> endobj 203 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 204 0 obj << /Length 3624 /Filter /FlateDecode >> stream HWrܺW`,oTɒr*S6,,J[Fw~}spnUyWe&Y{; ǝ[`mgg5k(՛+v=wU??Tբg~nwvﳫv0;&lnggI7Sn*pfg vFa: Sy^ @ 4 Wo/]w7߽>bŵZ^[#[ed]yv¹཮A9:h)>1 ^7*<6)euHxy׺nA`ztV2()j.z&r/kCca3-ħ ٌB몛+T۹bgͦ!AY8sď/&<Ъl*=rޅmr~mf/Y.s pX , [*)e(8$xj V ]i 1ǧxj̬=̬ !6 i!5JIH`}2 `Gh x\uWq 7kNq1ϑ 99gH H c{gȀ+C9$F9S|j(YMP%ki7b`%_!Qj'-#{pAugF J$WO޲|lz@5Ŋ PX7v>+bU;6/]na0MW'4IYC~{kgf;Vpxc##%Y>;5Aj%Ii[:cʔC cKg}?.01>/PȊGpB,e `^(" y$ ED"BmBN)'eW N4 N[Cm .RĐ!=.b']`$aٖDpvJU) eZL)CerDn28)B ų;V2@ޚ[Cmp΍%1W?bQF۽ !'9ݍƔ=t\RȨGKxf> endobj 206 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 207 0 obj << /Length 1154 /Filter /FlateDecode >> stream HWn6}W ]"V%ۇV)֗ZvIQ2@4<䙙3CnZ"ꗊQB9G&ZRMu3m5ZaEj[|Y20RU2W=/34i܌ƌH~]=@˷lլ(!V#FEznwv~ΐRFjZ!ؑI<:CM10=sM<<' -I=_-'+*ŒRf<2cj czLP>ξ" V8E8C4 \YL9:WFuYlwFl<1X`['Wb,(F aa=V gpw!CQ`>D]+v}0AfaϏKshf -N9Q &KpB. "Z@m|ðĘRF1FZ0Jt%/ouMx%!lSr%X}T%}.gGd 81 *[#Wgb&YtA Qd2e됱 ń:4 f;:iVw¶469 Z4vSBƔܖ1ђ|bcY|n$mߌ]Ӯ# l _aS~<)uYdS h^' 3z/갚@SPkP82.}jsPP(Qz35E)c'Xyk⛷&Fm3W 4Rp.o ^o:t͈q(Ox (wfp\K c$-HI-\h&r uS`A'|HNms\yr#܈-)7%fIr0Pɖ%歉oޚe+W _I q)][gxGv2R!&30 -,%BKr";DdI-9dKG$! .|Xbd_}cS+kf.\%\\U5{yyG^>Dʱo@e endstream endobj 208 0 obj << /Type /Page /Parent 263 0 R /Resources 209 0 R /Contents 210 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 209 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 210 0 obj << /Length 4301 /Filter /FlateDecode >> stream HėIw9<捙R$EI{^_yy\v%%|$[cgӋ\O xЍtJt#UZ'Y'FoN̖Z,g7ΕYѣGջ8)ilz+\_?Fn簲28dĢ]kmj*1~;QY~%(LA]1znu::>|"ߟ}vz8~79v8Vtֺ׍F*߲.g<ۃ|\x?866ڳɅ0LH]V9x\ `Y_]7I 6Zz;z ޠc0L|OTq,ߧ^MWS1q}+]vMne bR5Fjr?3WzXPWU̗b*np\#x 'PN&t+fG@`B2yゕƂW6+M{)3v}{+W|O$O]մ{=<5JzW۴uKƜ[sbykN,;-sNc6-;Sm:|lVkLt]~r[iul_bޭIv<0ȸv )Ghfh¶2 p—zqJk ΠQ6a-0):"c.ޚ#v]rxZϐ)M4iI,MAX/g DãgXJ$-*JrXFQ,K\ce%XBfgykvq! Z ;# kZOo_q5r.`FkW|{+vz9qu[%ML) đD/MҼ47HYPclrQE. ݂5yOcgCj. u?kjEr4^lx|slXRSK)@wY׼, 6MYJK)TIK)*e!?Xbfwyko0).~I*Ӟ)2lq#kR>42F$,`%xeUnʮ2C,ClopCd,1[5{N[`ԛ6a&L,UJ̾/dxRƠW[H16Fyv@ OZ>"i )V"1LԠd¤8@KD,ojͽ{N5O׾Peǧ~B}!k9Kb^I?|{!{ r?5 ңɕ[,G{4K䤙AdžJYJh{; dKiWeC}ݽU&s{2[eFs w7Z6GJP2h=yN%ՁTRha1>]pOW q|#BLaF;ίp+Éz799a;\yR1Jg6jY;ӻU*|ce3&-ؾ:64A$-6EK'%c5R&Ti y 7nJU[JPsRj@*RPUM hw10R ɇ"Z-[]T Ƶ0ΑdBAn0F[4Q``^PBϏ/-I)AYxBٖ ʋKWiL; AKZR%( p-f-9VcG=V~ʋZ# FNŪLMЖ aVnbZ ֘W5䷪nꐮ7|H{s;}-{]k,ݣXvu`/=83BLJLZq뙲J}f#[ϴ6ᨷ z,J]d+liޘ|vG^&Mis͝LT_L\t_OFK&:t endstream endobj 211 0 obj << /Type /Page /Parent 263 0 R /Resources 212 0 R /Contents 213 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 212 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 213 0 obj << /Length 5411 /Filter /FlateDecode >> stream H]o+j/8IX $ei"Rwfgwi|3G3'SW4tqUms ɫM(@]l˓W]6붘岡ˇ/oI(iSv>V\1|L1$*P4UdqzPz~f7yL896_ЊǛ$PA4]Y1r#/߼;8O>_ӳS ᨽY_5mCȌDvJu1z۶c6@/[sa 2e@1}Ʒ>%FV}Ï7Uk>1>z5/~kH>i0QMVM*1#4@6AD4CD}Tĩ@$^ "lM%fF+*8\bD j9"g˩'3r>[o?~nP e)AiK(ECy?`4;LIVJ PѪ񄝉'q*8Ag);!M0MBMXb5GUhD003A9݋o|E6qcC61j_B&f}OOҶ6ץC\C@Á*OS7O+}w/3fol/GInĪ9+[ +F-jhAg+935:!^(MRꑈZ=j9 =aTаvxY%8d9Ss38īdU֌hTe^ (_ C݁hmbrG+6Un& < W@vRx(QX:prx55! RК)j.g$*`)j WQSDxY.T݁.sz-ޛ{/;Ŏ:d9+Xă:{~/ξuɉJ^/*|e+ [_[+ *9u _"j|a5\V>raz(Xx"Ԇ͆gs]g4ƱJmj.^XkW2*lEv&ĩ`4eZOKD ,h#*`yfXAkU}s\ZƝ2]hr?: ^oWClbTt&S 93[…Ҕ 5W;-5j.j=U Sof?&#SƭbAI'QFA])`y:-}eϒT&U Z3^S]pm.kDĚ'`5\V˶^>qGS[mnW"M^7s%d.Yáw(TT&U Z3TBS]>3P,(XAK*<z4"+GVx1H&\{Qz4gW2|W\EI?%’x55 KКY%s8.VsDpY-WZݛnX^ϖղN[f3\(׷uu6.Q~KN'j8ak)Y54Z!N0UrMSpQ 9\ N| *XLW^fZruk0矫u1O<#fs" v@{tU0o'?T/1*EFv&ĩ섬4e皧&VsXe/$mYQC2]GƟT׮Ə|jw;d&CD8NfRF0jAg",9Đ36:!b(M}^>m9Sqt0wQVɖ! -9 %9 4!)>pGq.67 t@A=°jb:;VLa1{GqϳRklit>38d(_EE=qa>qnt{381 Zը:BAQ{%̓+-$&HBX:p$^#lUpdT.]>S4>+5mL8:ꈗ:8\D WqWDMQQ*gT-)I>L0ׁɱ Lf0a´"p mc*%Gpjeh/K >eSQ耧.S[/*|Sdotx*OjuxViB` d)OXpjebJ(|Ғt"vQu@P=\D|cPz`#N4| iԁqѣ-V-"CO8RPB%"Jwls)@s:>s]\xDU .N:ʥXeՐ Bwy"LFWO}Vy-5u:ף[$: A4Y!K0UمH{4%̓ +-CRh1;E/+/ݸ41[mP  (Cd/.h6OP [9@X:3nӁJTتPՁ Z .U"zXaŪEu*Eonio̗Ú< TVqfvu+E NeYS]:_uTg&<3mINت8 Z q.k$Q I.Xpje1Nc:^O*V= Hrq3L jغt3@JPت@ Z ]}a>'GpU#:@ugf+QuYRɊTSnSeYe?dKn٣~~xMkKG¸jV;`Ew, P0:|9NK/T&' );6=Χ9t-#CSq*z9|0Qg [pf]T^WAGist>J\EVeԳC8,iw䦙v}ZR1՛w?}"0S1$bh꾝^~&EgtӻW5#Q{oUG++>ܟjTjWIЁE-IpoqHoI a-mGF]ղoa'?p ,-i 4r_QJk%v ΙaTtfG3\yEԞLv{0QE~~&сΨ;Z!`?uךuLXinN3MS:nL&'&(ʹ\9oΫj@xYMRWٿmIru3~={׳p*:z=[ʼnO7Sr }S[NR]ԉRc̊VEjoEHTek\dQɢ8]ʁf T+w_ZB7"fҘ"$N!za"V!h5&+D*Ж\D@ªU.K7\>RdIp/=mQᆲ"]np#^lUnpdTe48܈qU \ՙva}SZSo{NTWE{v?'T!1:4%v*LtXNcɜ%@[u:Ut&jGhjeJzI Kj+3a6![=v>8 ?bեSvlsՈ.}|>ة: sB|`ecE T"JU\'ɟ>06$nd||$OT _(GgBbſaK\ЩlӁ 9 Biʪ`KD-ZX[⠉-!^N|{>tM1MOQ˫kZaUUˣsy̋37q:` [,:dA5Y!Z0Uم|1ZIȂ E-`UУ8ae5Hj:xǽZRGvG& eѢZdtfx=URZS]( .w["zpaŪE|Ţy !E @T/*|#o{< &5bQY%bJkVBvH=?q|ץʘIR$wǧEigb7j0A,QXVpA̳Ow endstream endobj 214 0 obj << /Type /Page /Parent 263 0 R /Resources 215 0 R /Contents 216 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 215 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 216 0 obj << /Length 4909 /Filter /FlateDecode >> stream HėnH}Ik>/" ,EtىynVceivuUw봐{ɪjQorGʊtmlzHe=:ǫʉn.}(ޟ-,uq?MMm=gy7 khѺBU#VasH?TrwYk(?΢iquS ԕ7Ug7W\L~+նyk+x֔ҵY\em^9C{{~'65e]VZ\ume\_x{mgpelԥS {oL;/_Sᛤ+exNo [K$cd| 2Y5eV2\/JY #~[͟^f5)a VA/6t3_.GjiҝS%?Zض.Uۺfނgo8H;H7mx@7ݕH̚񁓥b &Vl]Ѧ`chJ98UkU*j []C>̮E}bb ~KUu?[C*%XcchJY|,ALSv`?R0vB P%R}e"))Imbjk9hk&eA @0#.OS|jo;ʄ ;(!ZA(;YICmSRhءYښ"e؁&؁)⶟'hoze޼H躘9Re *PˑDK#J4g&djn]R>{;ant@5h-Վ)P0lE%l1Fi|uz|BG]qHF lxeʼApߧ~Y#l !|BP+ |6̗ x~5tyuS>Ч6DlEҸ峨Y#fނcoU|8 <`07k _7]]8ڄ\iwmf_F!ښ0rY,%g;+~>2e/.jʟW7Q (Wt᫓k0hc mLq')ԒC# d܆ɤ>= 1-tzb,CLB,KIoT԰$*VL+aJi%dR*FABqkqǕ4&_IcZ #O7?ݩHq~xGU?KiS?88y+A b4"FKs(iB,KIȭb*f*D[5yD.KsZ s; ǯ+ωk+s,T9j%0IͣJ%r(҈-;4$JVCTS!G)ktFeJ 2e1ujIl4I7,PQHR'F<ʜJqJ&z M5yK[G MY4èOq+ۦ=rG% :СZAJF\_@ I R/[ "lQB` 1zuJEVb#r фKEjnoC-O\~zr>~;/sSnbp}kgMe^PbAi9 e bdAV"LUw!t^*2b#nhLRU] Vg5(=|8̅"mFBV2zP\b7&:ȰªGU. av~=Y1;69>b)K3͉a2/X&bbE8TuRh E-IPS^+$sQYPJQ{"ޜ%2Pz<(U+D 0l!,`ªU.KxBNg'DYq/'pu!3 ,%6У^F=ZYjTukc,QY.Xpe =inLKQ%)(Hr_@Q{yѰ#IH8H#28\[jTud`g"BXpe GQ%HKEz%lR qY9Gj$+; #ǂN;!D0M݁{ ªGU.K }1n^Ƹ8U]A$ON;?Io0sSKaN32Π0zrU' bN`" ,V= ,,$nb/X`Ih)ms$8ᄭ#BVZ!P0Uzf@E ZtWm{PU[m~q.K: Je(a㹶:J Q])5̒&ZD𲘧nQqwkt'G݊N*X^^a1({Hmyl'giM91{%G0[=4xM$ Q=\zDpY^^ۯt_v_zW ``X0Lajk{/a~~rB k V["CXB˜6"+Xpe CY!-TDq}w,m`Fa2 (z2:AXhe } :, vnRx$5yO IyOO;Zmw 9>|VtZع:+U-n@s]$hªU.KЊT-Fǧ`+{}2 \Q^+MDH3FIR/ [/lGXB` ql)`Ec|AcG$p1Cs{#hrcQ7>|T}̗^ή8cۯ W`uoi7_q("'TsV [!U/B(XXU\@&xϴ*^, ݙ>Q.iv:[~~<}S7۴>1"d\)mi$4шF#EVZ!0U݅.2IhTшU\xwu>-]ge"c?_ܸi? DڸpH,VF vй:kNLSwxB) ϲTeU\7˫?..5S.\O}r^`vV !i <fM6LZ1аO9>;83܇ ħ2WRo3Zpk:9O3'>2>{*af\EpsG_es endstream endobj 217 0 obj << /Type /Page /Parent 263 0 R /Resources 218 0 R /Contents 219 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 218 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 219 0 obj << /Length 5268 /Filter /FlateDecode >> stream H]sۺ+pIy&;Ku;t2L9: %Ϳ PZsNl/_bwpLGS#G*9W7VZ[1;][1[r-GO[RdYCL~7zFQ҈ %Jth>:lAs?\&?GGt֖FT8΢iZLSWν"/u<;xB~s1O//]1[폺49<[Jj2dLcdG˭ m%F [u)u.ker}/.tҕ1q6 '*. RKW:)h|`L<ߤMʝT}&-^9k?O W㔩d2d>. Uxl68f[R0柵bHn^L~|՗qJf [kYԵKq(|Ύ7nl[qq%|Y1mMmnu0ͅH-ek,MlKUPRV@bBs UgaUc„2j Znؓ>\|] Tmzfy׊\\-F?{=VP)uX͡oH}`/*ύᨰU6VS/n+Hbq7yQLXvZMeTvrXKZ[x67ku-Zuv6u-QWOҌ-P*RTEXF #ЙjN:sA9 $oNx&VߌzP-az_=@+'xC:#O eMV2U*}bAVS""en|${L@ĵH>5C[kMV2UB^ AÇVS"")kx6tvkfmъ﷝Xad\EI-okY39B9h_SBA#! m' >ce=,OvfX4Ѓf*? )>=>+Nj-7B"NFbz6b5t/׫닯c8u C[iURX0/>'Mn6tܷJ#˾OpSO\ fd7/v1Zlf:j`OXO/hE RLPCPa;_ࣄ?Gʃc=!-Zw_=RV;gTyVSi5:@ɱzkΚzb~->&Fk[W/8{&BIuB^nqdQִVr!S U0uS(25p5FDKe*x3L r}oW[_Bvv@fw L]ɂ&K'*:i!"Q"Z)T FF7+Ԙ^E)VRLPC20XuOkݮ q=kg-Llo8+2Y;]?wUbU SK:aE[#Vu0UքUo%"S*y"0+9h5K)"rX- =h϶VN ]x6PC=ۍ~ KݥӀL*0ak: dUS]7hb .V5"ATKG@}>20i:|k}vi`Dg) ^:ܝsq4ע [&lMǃJSB` mU}(JD.jXՈMT<-̥h9$M\rc^Vwo ں Hj6bA{-UtDȪD%+$ ʻ8-]>h'jXՈQ2}~a~d=L9LAƃ[Fw#]|";H  B;!̎ܩV+@Xhe ~Op5n.boSx]}ۮNmGG)PĈ{;5ѢodŞhV -3p*Y Bi>eŢEV5Xj@pYݚ~ tno~WX\~a援~9ȟg/"}(NM]>쌉~f*ak::dUВS]ҼfqE ZCFz4N|VhVw]_7˕m:BMT^aꪦv|+VEwXHcL|Aژi^ &l5*L a]p$J*e TsOW(~ֵ4V\j8nЃ]'8VɰfsnNVNwa8  .V5"SOJ~w{/NȉEyyY5o?!s7꩖a-xO>= Srq|6wG߾+V5"M;6OSqd x}8Er^^z6D v 3ЙN9uBn`mrU\Jϓ~ߐ1\PۖBA66b:!LPrBPdMD !jXՀB5=;JPp|L-A,Hip^lp5;s*BjD+5Aw爚Pw(Rܩ_o]ۍwեG9r-sIrSX)LIHV! [ $*ĢV5\jDpYjR pPwu 7_N~cbA{-rUtȪ$+$ʻPxhU \3ң]CSZCGBMu֌?4\S[`"YbRZ(ahSIR' w`l8O`%T,J4pI@f[dk^2SWf̌3C; )v6 L4`}#8 ,Cbjt^rX3*%N,J0pIJODTiןXWVWx [ qwn͝3r sFcnOCbݐFY,IN|8(>Jh8%s%< endstream endobj 220 0 obj << /Type /Page /Parent 263 0 R /Resources 221 0 R /Contents 222 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 221 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 222 0 obj << /Length 5220 /Filter /FlateDecode >> stream HrH}IU6'^ڎVlOEdRDv)%q 4ILٴ~j4 'sLtUV]y߸ҙʩɫS]RzLn*KpjT )bn^;}<8QbCV0ó8tzUtӁ2c'|=ÃmW _4d3Je/%pU,Θ9+6tn?4WZ^H"f X9J)[BiMJ%'OY VLO&+UBaEMŒs561xڦC>67"\p1HU'8ʶ8)]'9+[ND5[YحR|ND (^j]V`Upgi~,6@_c) D`6y#*Dm;;8 S|Q]^ << + CNJoOfxz+ UʂuħXcYj:7nrgVgUr`9`7*j`F6 .k޳ZJx[*Ԉbj=Ҍqh8fC[Cq>2*N!*: )93 ,6)p͛$QWsbhj3 .rX J a eTȐp୉dx`~Uvqxj'Q85˫9"vY<±IؠIO [ē. 1iK=O;o_F' ? 9d+![2* Z32,2V) I%shiVg:OF'^/S;[j\AB|'wE$oUB n< Nkrnyj) OF8P 2|#oM%k[,!2>%x5˫9"vY@'fbA>Lqw17%}D3{/nX Ur Wb&VU`fVUYVحROgD^j]V`{ݑZI;*&U<ހ>ǛQl2Zny_Eˍ1˰h-fG; #%Rx5ʫ9"vY!Q̊!VfDSF bg~J~鶥Sak  Msx(.i׭?Z %JUSz$TKdpb✙~M)n1,Z&ǧQBr!DoM$@k̈́[,K@!@HSL};:vPvF2 EPv3'5 0Uw&.)`:3yM v‚#PADhy5G.+PQkBVS+dv4zQizO4yX#Y%Xg)98g_&%@ML$J0jWs@LUS(u13jKzq$Qݿbi`L X'F,aAI3Z (zjS-$@luի- SU\%=(P+%Q藣R!W'8VE(jXtqaRE )iFh:UtMC*M=RsTŨm.ҙV*PVEE*jXjB3eY :EWE 2G‘/ JI|Nԉ9QÉcN̊9VqK. LQ*jb2aXC僌Oʲ_kJh|4cA|a)u:X[ :X@aQB,`҅n.V-#{W19cŐ*;+"62zx`> 36$'= =0jb2a0RǏ]|f7~vtzt!rD9(ŠD1'1:Ճ:u: RbB`PҀrx,V-!iK`^Hiyq>T2TZ*hijqNKw1-)qZNz`Vh5bJTH ,U4/xɲeqa$6:ϔ$$h46hACتhAZ K.ĚC Xtj)ؿag ,TO}fCx=bUQC 1%*$*]3*ՂUK @Sb20ΛiW=L=!sL~>V}@-ŀPWqq:[:@RB@` qlڜ⨚jF8@판z(gO4t (5aG䍀Rw3ߞ"C7eבV!v4zOą`bcE&+HUbNJViT+T4<Dª%UKu'mT JQPR4b& JQQ7y0NQu={Dv'|ykN(z@aV(h5JT,UЎ\1RY󈂢 EtpJ|e4zgc Ge,(qduԕ~\;:,+u:C[:@SBt`҅sу.V-#᧡RQBBzfڌ<̛Ҩc\{3; R ,JN$z aV$h5JT,U$Ow.;ZX`XxqUIy#Nlb=dTzKwM7ͥK<ޜEFwIT>-L_GlɥlYu endstream endobj 223 0 obj << /Type /Page /Parent 263 0 R /Resources 224 0 R /Contents 225 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 224 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 225 0 obj << /Length 5143 /Filter /FlateDecode >> stream H̗Ms88RUk?2ފllM2ll% [L4_Dx9ύb~=M(?q3ɋMzū7xi/REDO]Mdm⋘n7>9OFNȺ bM'/[IgA5q&WA[{笀q`Bp7QL>r"ώޞoN߾??|st"fɛy|W[qaw4𬭥oɐ!2|\^{(-G$6֪JtpJ٠OIޮylnC ?]Dm=Fchb`R|1 |4QL3m)ת#ĩ D@tgk\[!mEuv;nN,S /鷸Znboz`>U0o45u|42[4D j+ ldq1 5 M iՆUI eTۄqWBUfTN_WY>vHn0rVbr& SQi&09x2+N(H<28Z,*hsͬs*[NctmNx4S\kZ‹ƓbWq"e=,P01v(a:Əhu $I?*"^ZGVwy חPqM6'Lk82\ve/cEk5S7X)bKOS4:R,b!ΫFB$:QDpveB,G':sdY,d(4O(#t2㈶fhk6p[Iȩb|IZ¥9,k`7@Ddut V?HaCkw.G@c|,Fd0B/mֲ.`[IȩblH=%FZ¥9,g]rUrZg(P^Tث.W+[k~XiaV߇Դj 6XyPT3CE:E$"06aBCVKZ""e1R&FDsPbt ttm(T h`?PM*xHl#'guGmIk&r-}1ܡeh߰sn{z8m-cK<:jy1\뾨n$׹̝qp^E'L, ֒r fOlG+19y1.-y Uwk"Ҷ]U-xѺ[ܯ*fZx$\Oիg7̀9mNP~7z|p,nJ|?xkjotS?O}*dT}i>H aCK!zj 5 9$pOK0xjLJ]Ŷ3)ge5Вa}l9fTᴤ K3eKbۺqS,MKVYTYv*:*@OW{:OO, %A?$6QuZߺUpZ.$ XP!ł 5B_~[cQO-Nywbu oMt&cӜFGVdz724Iuk3ʨ9ь3MKo&C~/YR rzIU¦aHO1|Xhy5I[H:Zmk"+ES,[;|YdQF-jaó. Mmb 7ΘQO&$7 ;A' z9nhk憶 AY 7䆜*f+9I*)XIDC tQ%vqw3#fә*΀d&Jg 3嘡گe-Vr'K"AVKZ""e!75rq'`z7Jknrc^M[YmSZA'C z9jhk@Y 5*fXjP䨡.ajtXtVxGpؓ"(-AGnT9idžFd("9Zmg8Iib%aE!Z-jaH<(_O,?M 9s(@7Qq<8pVҙRM$ P&p" `iDp#0dT{l|.=FKz^K_kҵceN"rLmz+9ÛT5%KXdjQ, /'Xaӂ~/ C%QPk=lƷ0l'n}9d:(p^3ښ9QYo%9#eDb -Xi1GCIsE<)Y9hgƉtC9 MIDN3t,a L(r0jVKD Lv[|q PՉō-KP+Yj--jij742䠕Ctfrhg;ItiblC9Z9,CgÎ ϸ_˧m#~ C9щNfxzȲV#߾Fr|=>.3? OUKۍ<9T.bDx9qLb [*lӹUVHLwZƢ*hTPXѠ% 181[%_n~FvR.~{fkvע[l'JOBz` NKkH 5AQcF4ZB!wFnq`|x+Rͧ\[]^ 4; k!cBVE*Z!R0UޅZ*VIªU.kp2s{q_֧4wiۥ5uRWTVHS^1TΌn4Xb KYh,Ty\+`E%jXՈಘ%ѳVFdrif'VeZ%X6|q9\VN̈́:EF/NLF`N! :''uT ipmJD+Ch U"\5InV~KOu]~s1.E[ndn6gqqmvؓ1-b5pNpNet"\`piMn"Z`UŪ5h)UZB< D_&&3z[[q,K.+9{'Q78-<ƮK4HcE jOYhTyúXP E-ipVѣz~jvܚ×wXZpЅxՏڂ#aLXh*We 8 kBSCV%,Z!a0Uޅ2h ƢV5\jDpY:Z^SB#:m|qs܏{8 tkNܪD+DʻPfZ W_.V5"NA/x=^\ҏuqG(TbqS2no6{$.]ѥAI Q4bE QتGJTB` Qy߱h#jXՈQyݳG$#Mkt-h+7R2q&(fCj!t#AN%H &@^khU\(S'ϏfNǵC~<'Crzʦ0eZPStO^>^?)`g<T| q3g;-|bUb|򎞍UV.Fw 5n{DPM9mD Gi`^ +lurTV+Z!X0հ ySA`fpE \Ҁ>n%)T{Gz:_xW^/]vxx =S%^*TxR*TVDNwcŢV5\jDpYaN>6KzG8^U|r.'H[sq[ H(1Ev m99sҦNHLw][cVªFU.kV801) w 3]|-8k,7`{y*,yŞhV &3p*K aBi5KX`e =δw[꽏KzbjX_TM8[1@MAnLv$Ɏ>V['qnU|S |!=`QbŢDq7r䉁DQFL{ l \>+L|؄!Av18\ /0pk:X3f8,JX`&]PỠ9nH1Nh}C)h7 1 \,!1af& j>=nԇȁ)I9&$']zugnD>Td%ԎFI&  endstream endobj 226 0 obj << /Type /Page /Parent 263 0 R /Resources 227 0 R /Contents 228 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 227 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 228 0 obj << /Length 4906 /Filter /FlateDecode >> stream Hė[sG+'bԽX5BX9@rH`Y5hDX0OOuy ҬXj8Da`05DxX) ӎk+qzw/d%&+q+m{WubW!K2Y Y+Yk+" ʳ0)dUUIUzz\z!G$ T5*FvVu9"qB`<˚CXXDpXD[؋w8NYlTpbtVu8<2ykNXX@pXH~nw(9tJq>jgGT`bVUSB`< 49cklS(DS V%.V%Vɏ9op/+:i=4 :0;a{5t*4+Dʳc]JT4m!dAC*8z4-9j'OgwO>zOYqLb{5򄭭G*<5+ ʳ༬R(5*q* TJHq4rY#)S>Zd|KYƢV%.V%V\ ?A)4U;+?8!=2a7**(jM5)'}vP \/dنFchߺ 2kbeO)S 0,j`UbUa`hup~$`Lq9K~zNj`k%[[CUiV,ga m_\UbUa~=TC~H;a{5~򃭭!*4+ʳNVHHb{(?0__S0Z d`KY)U5l*q* ?~j'LvơzU >֊~ ӬX*ςBâV%.V%Vѳǧ\'䇤|U;+=ٺ8!;L󚥰âV%-V%VaܐagޟrzNVv\QA;Y t >W :,j`UbUat =TciAǺ)dc2;xثlm AVY!>TC5|E EI-fPin M/s?,4gC-S heiǖN-jhakE [7ڵmK-৩.Zm JXbC*h](:4*EE)NLQ*)VH khmERy(ӮeUT(EXDpXqH#mz/jY]:ث1)lmMBVaY!STdL1UUIUl9sY~^_ugW?ϫ(qFIDr|]˒ bBPjag:[CS'3mFE JZJ"8O9i紮<zsv?wĔ4%,-}f)Z j[)Q*DطPA. 5*A*q @nS!͛Zioޝէ姯Cбݟ?2hnۈ_Q>%\)Vm8HWl>$ʹ$lBVgX![0U݅&FRхU\VKVv͠ۅHS.@mڙF["2(r,+P/^r(Qx V=\zDpYLQf7C+J?k6m?oxM5$z/uV [y!U/jޅ0^NH%E%x n-2R0i[I1Zj%d~Ri`' ;Td\aգŪG%\C:[~Xꗳ%G1ZI# ZjelA崀*NJS7S#hªU.KЊpazKf9V<σ$`FBVGX!Z0U݅VjeE Z`% SU; u?lYݼ_qc,IJ54s^K:9ST%AF6hªU.KtPz}Jq9J]VHIPR/C [ %l-gRB` au:H@zXfE(J(}1y|0TAI8: Je(alQ*VLUwxUIEVb#jyaz4oVru'=?\/]u/+6U[GYsDj9Uj$Te+ ; *,Δ;!R0M݁R!U\#Ռ]z'=tW>F}|ox>[&jJjga1ʊbv^Ng"a(Q-,ali+*l6?N?}մx>X|;6cSmtE sCVcɊé. @BYdPXѠ% ay5ot[Sj24U(7ڌG%O}$F@T B?FR1U\`'tPp@KYiEH` AORtsV[ˉ T B/ zX N~zcZj?-eJҽOЦUzdc&頒pHVFva991G' ; X:e32GU.Kj̗Rmw~ QHƇJיxu8_+`2~F V.5tBN˝.lL`xaգŪG%p&]@f#vyjB*)|S#AJ)4p:R Bi42 )zXZޝ^>neyKWYK1J7 U 3ȞNBzMj8ak9!duS]1BѣAKbFyicX=|ۨe^¨o( ~9FMM$s$aN+waR9F0¢ŊE.1AZѡ,5axIǾv{_8uj,hlӖ"D> : Cبa 2@ň)䇐^ >|hbтKb|]iwT^+^t}<>^S OD $9[%a})!>DL/'.-A &Tc?\%K=1|SwB4sS,s3|da H endstream endobj 229 0 obj << /Type /Page /Parent 263 0 R /Resources 230 0 R /Contents 231 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 230 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 231 0 obj << /Length 4952 /Filter /FlateDecode >> stream H_s:)Tm%K!ş\^`01!0XݖZ8 $qXݭ[ңу*tUVFUo}mуo԰\=x2j8:۫Q%_jzxr?>ѥ-.jm?l~ǰk+]VGwҾ L UPteS458 4M[6 1r#/?=}\|qgj68?{~b^Wꦃ!Sd#;Ě}4cz^R¶uJSN{>*c\[&y;thʮ Jԙ.qc4!0zz?IkLRX{ݔ #TӪI]:ҽ+].vc䯋ljWR}OśFݛcMM ?ѵmS)[BSp@$SM? k5UE,V%z6jm-4,`ܞ^@iL&jű i񉷶Us.-MnSZJ<(y3)^*p3h5Nۺ:XtNdI\vlI3w6<:$=hd3ʉj VPS@xXֆ _JYPwR;jb%ls7\ k"gkET ӇɉsPѠ!jV^Cy|v X^ǿ;BVeff?& QpZ3FDb@{%5,!k&jB`\ ML@E)p# XV؋ӣ>_ ÍH7k,AfIl\Gw~wMkzMOΞ8{J9Ù3uMf&=?on6o?Sd/ؾ~5_\-}"\4xW0纪pKpD&L Ykm\SsPLC(PEwuha?]~X;߆vXP M] zzwaum 'ZS MWYPD`5Oǥ)nS~UIG_GvYM\R*Tgi nT,*,-+L:Я3oNE` ܸoZ=}k;{'zs?qs?s6 'ߟEtv@;0y LAو4SNؚxq55Z!P0UC\’$`5V *Yn/ӋmO==9Sz(=DS Qؚq55Z!Q0UեtbMPP̱B1G`X>EFm'ݼ$R6;m(SS5RڅlUZaM R+3NJ`kb[y@hT J @âD VsXaptgQ/~nb~:l8mA #{+GO2մ!=kHn1\$U\Nfkך.*WA8.%j+UѽΧ:48Go3gW62-ɱ/||!_Kqg^Xj9"8,fؙ=M,_abf!Uu`h@bt&s!0NJ3v&8S5+)  ='*;+[`$HL`gYI[+" UhuX})Bc.VsDpX'iLJ2|$#S>ؚ|Z3>S*.(>Xb5GqtNo(? Qb;7rq";;#X!-DHEBMҙ Jf`߯lWqHXi^'Uv}Aj`xyjePâE VuXe jtSSC{ c [-rSxD' (+_ xXn!OA϶Sh}~jK||Q+̳y̝OYՆ, ʼdr4(cE eOYhP9 gr"ZaUU\@_khВVq>Z%%7^]ZVA [Y!-*g(Ox"v;h*Kb{?x":"<0 27#ɝ aV! [GGvlU¢C,d=uPrk;ZBG{{xG;{.yoaS*|JB޽tew0o:% Jh>CL U8'<\X,?V|}ݸK.ٮ cO ɜԻտ9,Rk%PSצ}I,0/y҇sqhV8ům^k+98w~DV|sK(R$y /uV1|`PrD'kp5eKPuXe pk\wvRfťa7lD(i5$kzkua-+<<43e#|aRp[-Sx\lE' ()[XbU75r+b5K~ZBկV`.`uӺƐԜ`4(cE fت'YB`:YAQ E Z`ƤmE S>=? ]=-/iM1.iM6^NgyLKqRkN7ZaUqV UhU.VuGpY\wNCusC;OtC:w͸5Ŗ,1b{-UI[h`PC"<5+,^(KbZzT:W_}K%HGPdoTr+7W&1?N]m>zN?> a'o2"Yf4X;}dMEbS\z%  7] ࣈY*1y3(;ic$nQ|P{0J0@Lz/4i2Y|x7M endstream endobj 232 0 obj << /Type /Page /Parent 263 0 R /Resources 233 0 R /Contents 234 0 R /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /Rotate 0 >> endobj 233 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 234 0 obj << /Length 5027 /Filter /FlateDecode >> stream H]s;+t9ڈ\8lb7Eg)Ԓ9>*xkB\|ՏN|6MkϓIJgE %Vד7à0hhi'5Ǜ$@ԭljpAWoޟ>tuyB'gˋ[)ncBַvoTMG ikjciJj^]|F@a[9);kkbw ]Rl~|Di{ƕhB`˙xW! %hTH_W]!&!GLA&Vby-^|^^fChJ8鮰l_l7f\ojVF9].t]S6>Vku٬?]/6L_L?Km\*d/"2d d#,,7:%R-i $)$PmjmeFu}=9P(sӁ#VȮź^l3 m:cUaCA :H5ój>.o!u뜴kbWQSi5ךVsa~ جN)SMk1j}]U'}>*=kW&ո'&j5 AʩB%'4Mx\hw VCg?*YrZkL'YJ+mh I(r$jVsD I΅Va]\`QXrbMLY\)UiyA S3!E;H'&6gn(r`i5D ~0"M(g_yg!L\Щvx.M=>v`-jh |ƱJ72{`Z-t2hC&hk.ʚ*V-2Ur1lE9\Z2|~ hX۝֧ٟC[ ̀x t# $myɐ^$ښHٷf$LP84I(r$jVsD4IoL}$0`k> ֲcnxp`AC9th_B𕉠|А]BߢI" X5ED@ƃz>8pp^> ^[\~M/'X7 2 \ۄ)@DI~H Dbejgh@"M: 밳 KZ*AQ.=Ȳ T3Uqfp ΢kw\2Q dw1!k"n%F'cr&7{^Gkh0Wt\!% 7,1yS zUb3Ec\oջgʉ1HF{UJ&-K2]e68w6B7/&p2 /u*<0!rt)8haN|^kJZF=Yly! 5$_b7Ųe>=rC]M%6j4,a}YnA[NSoO6K&GЛAUNxiܞ _E1Pm(5(e[J[ j\ȬWFM2jFǶmeZn媇 $\l1,+x~=k s@3MnI9XsZihЂdyВ-y+XQJFUh=/Et(Ẕba Ƃ{lO;~Oq M~Y m" QG4=AjyzЃ^ښ衭ٷfzL|P.a<0Rz+PJ uG JP %Z憲fDLu` J(r(jVsD JN/hQ71n)58Lsz%B#CVr`,E93WIbEpuhm V(rXjVsD VZ- ;tovH;AN2E#Z9Hgvy$)&45"9h5K9 rX" Ƌv$}q 7:h>msGe8݀ |2qH@dB/mM[3EJRDUzCQ8H1J9jH }oik[u6W0mj77+"n*г*ͦh]>ʧvt2Ð& ikZʚ1,VC2ULG*IQ0'bs4Ԑ ceB58de%,>w$G v!UjlnJIt*t2T&hk)ʚ*V*2UGWAF9\Zh@{;|{-R+[l\]v ,l{(tdxZ=pID'z9hkYb%!#SU0m>dId c.a6^i4)/gzk)C}Y28q"Ms0;SqR,ib * ȱ˭#~vg\Ph R-(h$CV. ٵu(4Cl!g>qHjXՈKm^b,Ue_4#K|0JAC6(b :%LRrBPm2PbB ,V5 RCҋN`jMP3*Z<;Ri^lx5*= 適.40/Y@QcF4ȡy ]]Rj-z<iG9qB [5lMgZB` JPcB .V5"Zل'bt<`$jOHlhO) J N@ZbPULV Lwm E AjXՈMK(|ю,>}z H=*bk:WdU bS]hj-Cs'QN.Xpe k"#a ȟE|.";Ma naUljdĪ-g! H`N+ZXa`霐UJVLwvZ@ucѪnXpe *zVΓh44QBO A:ҫр)Z-SX:%uB`~Hb" -V5"AK$=_/>G\.\jUNW]O]tw|9/׺btŞAhV /3p*] BiP+àonk[;ຐzF/5[);`8UNL*;)~D ˶be,e[l׃KNO+esir`?5MDo^{:WuYM`Ek}Z.*H0%&hqa]ƹ@ /ۯTv ݦT^r];9sWG_s)q8LKX őj|m4"ͯcU.PR~CGwG5'?ٻi`jFВ9U8\`ptcVZha:t*Y`4yRXz$jXՈ ml*R҅Yn&\10U |T #P^&3|@U..PP'R'aр %Á>!I}$^Lt'֌E \#4_~YmoOg?"zۦʐP5U=AhMj CFchOOCԠbk h(abQbKbhZz?h?~ʏg~> endobj 236 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 237 0 obj << /Length 4873 /Filter /FlateDecode >> stream H_sۺ)H\3$\'[I$;L+nɕlgmU$K:8.bq<=x5UAYVǟjq3SNNa@ֳ7ixQj:%}q){s&.'enYM]\דMzp:=syTZgcA~Om^;gXxJq܅Cgߞ?S'ɩMONMbU55yY0dLsdX]8zhyi/N?J6Uksm6"ommcbu6!: hp~'%&, 3ia:׭BܒljW AM-4ZؼC:ǹUW7]NV}w\WeIW/nớB}&.* oJ(XMb5EˉGT;QC"E$Q안H[ *֗.(%jN+UXR'OƬev'к@H2Pl V'_mnQ$r앐ֈmք`Ty(V-FQ)\wy֊] ؈f,#jMblTn۹J5 0]|N9Jakd[Gܚ9* sXMb5E34 JW0zӟoVy ՞U˺&oG1AYHMNJwGa'/c{#N3fEX" To5|e>Gi[TUwu3RSq?-q+nC]P-1.^c!pc~]0b^;{6 U :#f9p&'d @;{%԰j N+ilw]/j83Sӂo~t[U5lݡ 2$N;Jak[D`TymDYL'`5V ,;kկ_.ÍWNo/Ym*^G4/ "%jN+@TpmrjC=t—{bTRXo0* GeԔ~9 HU䈍G*a#9E !i ֗0"%jN+@'> ?eoNգK㣆;vl?zVZke2 f F.(EalHv6M:6NJa,Ja5V C}% ya"V\AKӎݻ)jjƶd]g\{t @W [#Q:l&+D ʫ`WT$bbM)TPw^*fWL\OqٴU=ńnE9^tfBܞG{%5ⅭÖ!kkB` U}g$I&PLB1E4Ԥ/˚|E҅ wZj]nSO'/mቝOx{ y*"U2PX VSXMiӇuRWu+uՍIz ?E#GS`M%Q w=;+ lmsk"hB` 4z,Ja5T4mi}3=p䙢r-[ŋ~;KǓs/jZXlNl*҄}, U\`z!ua( %1VAiGga:NY"!Rou1n7limZ# v8b0|S,U;3[Q'H*9 -)\ɕ~rZ)Er {c!G@Xʲ-@)JLoS=3bjتAC;vXsabJ#Za7P$)$ W }NH QbF^搮z_y^o= dej:>r Sj;\|EA!ej i $jF)ʫT lhq.jDЭtcaޯ׫ U(O E7]9Opüŭv2k-TpҲ?H8) ʫyO(Q6*(lpU#n b[` ͗oW?-?^ԍ&BNiϥj 䞠MhR K's*USU-Ph'lpU#n G=#|FJ_uLߵR\b>q9b -N %HA>تbF!/7yz_}?5O;kQII{>cK-~Rʲ+@%d T h!,j@Эw+T/ov/ۻU$|<:?c02}UO81e A#S'8Q" K5,-ZB` Μߜ=F Lũ+.ajK4EDTWaBTbW^kJmoyC> endobj 239 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 240 0 obj << /Length 3984 /Filter /FlateDecode >> stream H_o8)(*"E=[nzE{-Z8r`)mw!)&ѐǙ}U儒j{E4c$T\2${AFuw#%9|eHӒd4͓Mws?_eueSH$494W۫ՅATQ4%dVu5/BJA`GscaD7J 3-4so93'-xp{}w$u]Uz-k7\yߊedespȤ4@}XpU)KcI%<τ1(LL:HKe7`iخm,ٯ •( E"fܭfbOzQYN"IYT \4ݦMnI䌲L3d@YÑ\sZF(wA' 3/Y$eXc1 Л怦BD]LMe{tx9Me4)D2ҧ)z &;o };ÏƼ d ٞ ȶ4ԟu݆Ua8Ox:Xa[ќѼ}~5M7`8ڎhs}6ͶZ-Jnb4-sleq~[fjej`+> zF_q[|R0$ʄ[u]r !!%0T 90FW6$'~0NPW0^0N'R=Q)"RD|[%dʙdzχ+֌v-891:1Я\͘A I2T'iNs <I:R73iʯHG PN"<*yG:¶UHJ̐fu|JleJyU ɉL@U˔tP 5tY iJS;ݦ:M}JbEHd K0N~ u! u]º^jtlz$\"NBI^E!諞z H %PDJ]؛kV@RfVf8!&%L92[®]#!w.`K T-p,:zYaa_b-ސȶG=l|=~]TO?-'淕G]ݣAL0˓fV"[haPrf?/dyJsYɴlR|؍YUapRX]|8IHS1QȩKXPLU)bΐRhWFvQ7+a56(tք \>8!eLpêI>Lo3PIL| >!DAX-RqBP3B :BP5FHXb-Pd>EAa:F1|X%GW?on_aGٙ~]\M(-IYt#f$`H02T/VYK`7y[TG!√Q7cPሪ1êGkq Ǡ\'Ym"tؠY2J"oc̡H8ۚ~mXfj(2? jޑO|X- \x!&du;ӳ9vVA-z f;ԍaTuءj Z.+R/ܡ0^Dj KEm{#GNO##izЉˑ sw+0+wzԑJH-鴅8ԝhtL[cjFC~AU Z1b 1b-8,ΚЖQlZLCK=&7F3Y|Zf~8gR(x6=c6Քqf1̲Mf3+Q(auclUc3jٴ jK0BgLF֢1@R8 EUO\u=9kI'|l=k@'EM渇AShR&h ,b1*Z0V7fXՂiU#`FTLgq̰%L 7,uY_]J?/Jsu\XCFPF.݅.YZBqӯE=X2R,4řn@aa6Zf G)Q[F5pP1l4c5-hnࠟ@4VAПp|i|j6fM82&skR<5/Rp!~Ka8O6O&=.SNvˍR\oˠm#EGHr] PUa̒4^r1 8;vt@ieQ(εCwzoTQnFw)Cj)7A妺1~MiGyq;5ǿC){1'wo=];Ĥuo1#`-Zڅ-42"|EeF1-폯Gs{7}9EwugLze}~__%QC|p%%K}4F"2؏jeHW;lZcasdaeɜ]N;G(!qruVk&s@L<0BATb7 rQ~Q 0I<2&}fcZ 6`LʀR3fZq-d D=_mRjD4Фi Maם@X ͔wNQCo$Q2J)%V׷ٺÞ;~s0Mtsf\|w]w(a!/t˺#ϯq:u{e^l MumȠ Sm'-$E$Ǐ‰ȟ҉\Zk۶ M WZ2[KI Rk-c2Y cbn' -H6SWf { raeAHn}x_n340̫$2B !VaІD cD=_mRLd_()+JS"o~65++FX֙ZRT}"EBWRLHFI)XxƌxV+̬-nǓx"QG[FyI]@|NEH?C޽%Ldr4Gw<+ $e@iĤ ,uH> endobj 242 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 272 0 R /TT4 271 0 R /TT6 273 0 R /TT9 282 0 R /TT11 283 0 R >> /ExtGState << /GS1 290 0 R >> /ColorSpace << /Cs6 275 0 R >> >> endobj 243 0 obj << /Length 4917 /Filter /FlateDecode >> stream HR#9y ]#%J?Nx''vnz6&ܦml63koRrtNdTpq2j=1ViGW;zN>nZn Zm?_u=k԰K|m~>W3ߌurC]?owv'I^õS)+6!shJklCjY @Ҵ ywnl~B~ǗOz4__ЬioMHepfH,h.A3쩴W翩NAac62em>"OnЗEM1hoЅ~nK׸AC$cxsi&6 VU啙bR6f=yp9N]q~g|l3v=&&m>XхOnTʟvaqmjG 5GTLs큡&O kPθc^<{T[kkq\\s?tASkXl| ]xE b knƐ?u̯1گcj] ._ndos'~;_޶6(;^*agRIZ_'U x%+5Z3-eIq*BiBB󇙁/v]JAWi u;Q2>oldTp8r0|OnĝpDFMͰCxC0}Ϗ9z_}a9 KŃծP® EKh-8,Ux8y2,) Ձv v/ppLӦ!(OBj\ZZ|:bҷg/80lYvbDahf㛧bERk= i3z 7%c H*)+K`bbW˓*`;l[KX-eE`H?n(t`>ipK;.?!ؽŭ-<_adrjy|)J$J JI%xiA8Pbs2t6GAi/eE9qMX[`m]PcF0`sFhN €#nQ V"VBH*K !űDk. ń^#,,n%%8\v-P7Kh9jni8)0Mx}x\"EQQ<\ r- /eEFtDiW㸻ݯWjǶ%CJw#g"Or2 D+tT:c6W*Cȿ:((!D_>Zˊtx{tv^i|} l@ ([XID;`pE.dAfU6 +`#TQI,eE*}%sM\Dbkl2!2Û6x`D>P#"BJVY )v%N<l+sJPD|b1q+R!1$ |@7:܎U8w/HN@\Bw,!(DH+!J+#$X͕Љ*6Puv)  t6" !K3({s\=nַ؁b#X|)5C]@^X0td%i%XieC0TK Rקњh9T`1nI&Mp^dS=<.tYݩ3'G#^%+T ȇ޽ء;umxH)CZ VZ!/-i{)<1gE'ͲJY6y f03+XY`mF-x>ilDx}F.K\„u^NKPVl*GRJr6Y0 m^&G&M.M6'^kx𭆇Eps6\ |,Sg,#ey1,5)&$2Icn.J}:FVeg _MheCpLi8x8+Ƿ81 {|e"+Dx}F.KeZIQ^8}ƭ6I}s:Aaihv;{Rqwa, TTZ*V/G\^#w۽9I,WĉV@;Gpt8G GZZ#r8A\xQX025?BƱ rKf/KDC:) FY1q:  q3 ZZr]1NSkOd'h Ѷ'9kAXzgJ-I AZ+%2J|ab3L\ a(D5F/ZX񖤈r¡~FΉ+#ʸrC9ϣM,}~!Ay˩SYU_|=UI[7߶+]Up +_ endstream endobj 244 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 148 /Widths [ 278 0 355 0 0 0 0 191 333 333 389 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 0 0 0 0 556 0 667 667 722 722 667 611 778 722 278 500 0 556 833 722 778 667 778 722 667 611 722 667 0 0 0 0 0 0 0 0 556 0 556 556 500 556 556 278 556 556 222 0 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 333 333 ] /Encoding /WinAnsiEncoding /BaseFont /DBPNBE+Arial,Italic /FontDescriptor 247 0 R >> endobj 245 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 120 /Widths [ 250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611 0 0 722 0 0 0 0 333 0 0 0 0 0 722 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 444 278 0 0 278 0 0 0 0 500 500 500 0 0 389 278 0 0 0 444 ] /Encoding /WinAnsiEncoding /BaseFont /DCDFPJ+TimesNewRoman,Italic /FontDescriptor 249 0 R >> endobj 246 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 120 /Widths [ 250 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 667 667 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 444 0 0 0 278 0 0 0 0 556 0 500 0 0 0 0 0 0 0 500 ] /Encoding /WinAnsiEncoding /BaseFont /DCEPBK+TimesNewRoman,BoldItalic /FontDescriptor 251 0 R >> endobj 247 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 718 /Descent -211 /Flags 96 /FontBBox [ -517 -325 1082 1025 ] /FontName /DBPNBE+Arial,Italic /ItalicAngle -15 /StemV 93.856 /XHeight 515 /FontFile2 248 0 R >> endobj 248 0 obj << /Filter /FlateDecode /Length 19600 /Length1 29628 >> stream HV{pT} !D,^d@ MxM&:y.J#4(ԁ$ЀbKXP~w"N{<̂JHdUAot$SK-@ PkFuMi} J ?^سT.py P_j5_ְҁ 446my)F`QL'ǴS^O-BC~,3 .Zhb%͵l;m 3FHS2 5[" k~Qa7X8 ۱ڱ˱mDg1A4`~N}`G? x X"KpXשB C:Α"&RlK?SLgc0%d-pE`-r#Dԋb'ψ9q^\7] xHNrlyZ^T_d+k]~}+-ֶeee;ND{=`a!e:3g9E!Jڇ<!%*>c;Ib/=)/Cy1A4C e t pH(= GyZ|ѤO7{ԨUeǞakxhCh+" NbأFd"Vm7#&c=akryUq'.x[#N6Q"O%/ p13jPmg[E9U։D'ITүy;S"loӗhJ MB< &XLAl1z]د4Bz{7e6Zh JyDV%rOP,~+Pz|E5OM՜є1cηc(7y sǍ3jFd=543c6إCRSOJ'.6&:aq Ч>CԊr,\3gL*-c[2%'%#INu&dM5: 4*ʼ @+WkxFV2#HF\.P=) 1 t k192ːC<"HWFV1RCfx5FiSrd_UЦ#f {መQZ`~Wop玫j^C- n[`$/ʇ}.uO\Bu}jl+>uYr>uEFO/d-VS#V(]Aj⛧-qAt3]Q^eLNCIg6橩99!gBW6C}㻁>==$#m*VW@ZڱЫDzrb-0׈\n e׮}ݛ3aVs4Æm f# >NruZЩӇR/眻\VUu䡊ce WQ~yYwsϱ8+sz}o;w̞_s@'kЀҊ*Guxv/? ^.!.#\a JNpDq+F(N_Q׻ U㋨2f ϟĶw;v6 ;#^I¿oC{_95WmBYSDgVo)'*% (@_ 2eX+ whMʷr^ =cP/r(>ir0̣f9*K9*޼K*B7ΟNv`n+CuQFcm66a߀nЏTs6rN7Thx|}zM!y<m]4Gs`U:C?<clUDYoMngq3δUw o:,TWҀ7 ܂ѨF%Zp'`13ҍ^+8 msCq_#mD7/h ,6 n9Wz9phk1c'.fo2-<1<'7k8]KB#zvv<>Qm('Rk'ʴ1 )l-̎SDqZU5_y]4"<,*yHxV剖kP,qk^'R`Rn|U&ũUs@f3='?)jj2O= rT%_L䷰؃j 95*"*bm{Klom{ޡ7mo?:1N'q1!3/;j0!-4T=FR G TE2|GdVPf~ܿ;߃{p w񊇠gN'5\A >8!QT41Q):5_Sm!OL;ΤM4䏦ulgZ㔱N˴iFwDv~߻DA[2"z b2{d7^dU]0R#= di]?=b^Pw ߑ۱0A<aLg}^C+ι2~g')ںϦ#3yglJ89QZ]w נ7k+^tVP4 }7uqIHDr+`IqV$p\&tqT L2m_<Tx['ʶ"SkC`ʺYkHUDn D&4f_\Cޭ .0ˤu7" Q(;}@I)aWWɑhZTHfLwܦ0Dcn1Hel8@rD@&ZXlH$ PJ^"^K͈5nA@1 "L5@14`,헉CZ[ U)C$ e4U1~0mBئkJ3CH }gX/>9+^êωӆ.bWӤo(<쥆oP>0n)@M%f_X"4UI{-eǶ㖭7 ?mK?~؉x -IP55i9g` z}:ie4MBm"H}U1:D=M*Kg) NٻoŇ m*~? f eWY#ra'Fo0*&Y[S-)2X͜,˒hYSS&ŘY%Vu(ĢX\fiD %8ܧG7E > ÛS|V[n^$llɹyWU .Jc̲~ޝs7혳rvK̷#_+oAIYwT.;ZegoC#$RY2jDÒ 9J]T3PA2ˤJ6,z~L LȪO\;$7y?:ִ=wIbw4q$.cϥ*씈;qRFHQ _˥9{&I`#̈{9at2_;X08g֏qqM(yB`lr3 ,|e|l1lYM]a.l=T':v[jv)iUivؽX3]huAIjr[%*rpISy"[SJhL4n&OYl♾R\O,~cի/9oѴ|w[kâ6M[}Ճ:vෛ^;@ g_rmu0)B)u] 6av]J봢V 9X50mFV j4 $J}Pmh*Gxw*{Q=v&O- Pvw$Jy#f,?)>(Զ|Bi|#WĻwsWMiT2J.i9~Vj)eaAטzښ5 Sbav"e6LI7)"2$A",ID(( >Quđ <3$&{!#6_Ѥ(J 敖}%-h#%,=iF5i睞׷V:In=~phfmG(,.DÕ`Eb˸jlfUЅ6!K~RPtjW]Zm~+C\ '9,r+7δ1_RFlB\ ЋU &Oj45dnXm:]gjXJpu?yoYO{{{x;?mw&~}d]-PMw~{ܛ<+"<"ZekHөhI1˪G0jĵgZVusR 7v;MN|?! {`4ãA/1Q!B`UTZ` xBERuU+n4PnsFjм+圙1L$D5%j:i 9Y#Q!iPv}.!7h) :,FJ=ϰ~{`MDaCԢ}^( 5jd %H2}KJT$ to?h۪ͣy1-zeqCĢ;מ~wscƯyeD5y-);?,0G|* j"4Vj#-V勶]8muVc#q 1X <duD8~srHN`n+ZMC~ꍐ ǧ=VtkW7 Ľe*/ R2~N͉J ?Ttk;?iM}{_zvwՎnj /ywcꪵwʆwO[?< OV׫YSe`R` M$cdN ^¿]#4h  + 1<@B ")iBNF3ҨCC:OːFvC3p^Co6#ppu6倍2j%5۔G x_NBC3I59Y4 f֝&Ӟ"hBGAwW% QtסRa]^/(-|愤4]B9S$z=m{k_p WMiLuَ 6|@/΅+,菱7 }iš?_۴yucn/9aā@T =Z6'_ŸYi]N_ݔΤCu>nW5 "8=@srC~˶,< 5կm%9.)yx8yt4)jޝ~0 OUӐMGtUU+h.m|`?sR 4EWS9cgGXyc߉ݫ|O>^+aI] Q)ZK&BnF-DZ\N9X~ybKĹD4j b$ˬuV2ȿ@WҔ%ЌQb0(쪤Il *864r;-'h? ?릈'K^JuhJx!ǵfX2w➪ٱmp)r/p ľ@S(HDZR N2J# FI .N"VK cZ%'1:а,MS,^GXE"~xWԋ |FSpS0SpoU|,slbQ#i(ϐFI(]G7};6b33ny.,=Ix YtvNZlViNB'FJlNVrؾ뱯c}$ {ö=I:%{h8ڌ^ѽz;v{TYZЃR.i at3ZuJ[ &;=>}v\1'^,q(9JA[F![x-6H$ :Vl+Z)5Ftժm:V $;;/j?R#R3]/+b'ĘGn$2=%˓NSt5@ J:]Q5:u@(9E f!&~ %|屗=iܟiTKomޓ˲@_\y"8PUYvk+g@!T = RA'KZF2YdSPB".Yq H k 'RHiXBμ-y#ὓ_fycUN[RpPY+\߾-O7?ד}gߙAfz̳g?{+Wp^,DHmE XsEu@u .'Q!>HRHf>XqP"c(QT$K!OZ1B dh:…;N[2w\5ک 2)~T#nԨ1#Jȧ..^ؑUvlYz“X; E-EgXdw|be^1{}$M|?(M~ndycz̲w'ؾQCmw:C^%XC>E^ %.wlx^?5~kܖwQ!W%!VKBӧN܊2)Xtʣ\UJs5D$' }VB+}};|oZwj@-r4,O acP,/t9^I̭ݨi՝zv&Tn-TZ+8vqd/dpU"@%7哸_ k؍G?*-r7g7?=t+H1qKJJo&}w1]&+m6*o\`n[ 544 yGQQHc9)=\{cwrvB>]<w)wpΩ#)~붿88t\bβ2 Xf/Ymk:^J֦mY޹³αN&\ [,in]wy( |1]D`D"5 _jq.]N'`reMtwa8ɒ6J أac@tR~FG"Bh5o_BȆRX6No"u dܐmlkK*^=؟۽4NBrP(>ɎeIq{w):xӱ˹ϵ_Z^}/[s{sڶ7Rl?B|XYi{0^p _0&ۼnCtA<]`GbΛng2e(> ō(X ͨ:1>Nr8VIC>CAzS(pH!hH H9;F QˍFք@塊ي#j;1WQWXgޯR܊WvL->ƺCx=M?mϯoZn'ț{7ZESz7Ӏ_Φ/׎dg-Մ&``Ѐ\^luϱ+T:9&SVzːmk"[w @%'·Լvs\mv곛E.RY%5F%%淳,Nxqi@Y?Yx*J)@2Gh!a3 #H~!Sv'Q#7/=ey KZ>JFd}jiEК]xjkW m_aU_$}F1̣}ßba/³Ta?aotQ&")qfȇaa\ܡHp1('jAQǸlHq,߮.PSEM{q%RX ~jBUvK Y`B[z;H  Dh)e5hX<'^yF"*a[L H8Q3/n>;ӳeK6;}_k\ߵdzƑٵ/ W'%MUC[/7ݩIΉM+Nlxwq\"#8!J0KS>FQOj B#7,Dte`/b-g T6b؁OۜL3LC9yY3BR {vs.ۈtR`DA2NF(cy<ƙ1߉p 1=L) O%ZAK=Pep2#'1[FR [L@(ni<}{́'ٔ&ٰoh_]V{u2ܩpǕ I:r˙ϓEmu&KVh,,fYh!l7b֐Dxŕ'T0*VGAα}cĉIc.II-mhFp)d\Biiщ0.]aRPM @%mUظ54MbQU'~L4fws;eOpOCs BC4F- E c_^jkx\0M(L߃g# > zDEÑ<ˈP1 U[uwcYsrQ}9Ωґ\yC]*9.vctqQ޺O7_5fKhk;u{Efw=ox!:bZp9~]- ҅ )aԟ_Pk$xꆺTPE$QN4p0*:uک';󕂂&N7Ik(WAjEm\ƨ ȾZ, ZM91 d H l*(mX6{G Ui=x%|v̊_:YS]v{JP%E1g&zw8ZO'Vb+c]UBY*R/˯C5&0 +TCW4p)B~sRM,7rSuf8/TB&H lI4r#~R֫D#Ak"vQչ0raW\+-Tġ|ܜBo9ӁC 9 q %QS҆#ˎg# C,tz4+8 @St/y ))%{{mΔoю._ahZ{W[+hhwi8rG|Bd۰e;s\YuH4UnmpL¦T乸^^Bw˟Vy+xyOdn%:JB !`U5]U*wX(ߤ*4iJRS*ruTl,UA0- )i*j4rsrw[ 3X$zmm|odTQ <:PsfJ4I(QnWxaȎfZYzkÞݥO1;, I)LwCP|KR$2iѡ~|`c*d n\ gNy4X,@|CI n)XBDaB^D?o,j47䷘Lwj/n-DIPyd=LXq($M(QiSIlVNAo vOp_KFЊ3|/vog:Zg7tg8}Wd/_C$[^Ӷl_ =2_&pwvqտUTT)Cꊮ"NP4+f|+.l,iHFݲ-nve9:6;{mօ< y}J衕S(Ӄ{=03=[^=?N):Mz*>5MLVd=E/V"XECdH X# S#|s؊X"MqOb0r $AP8#06Bq@t Lԅ/dGNl8=:z.ght}do|Ob躽 -W;QtA-y†L:Aj$V֢f q9$KCd!.,iv䴥Sjv'-J9)Y ڤ;\ALMkk >"n4Qk[Kq@׷(it>G,*g @x՗Oտ3[6\>ֲ"RGs/åCXSwneU4cxB8&2ݰe<dp%ØWT)-M'&;,E ! xʀH3kE;a?J&Ĵ9kУP &+q:!aNۍ.q'Y1 =_KvY\Ɇ*׮*j>.x4HjDAQHCcs&4p~&a"12y꾔O䯖E;vf9+t-ϟ[ec|6lmcTenm*̚DI[fģ"-QGGҫF< M AiOMf1R'Oܳ"vaϥAP5kɗfy<r֥>w~Q͉zE6wq}Gb; _o|`ǭ=`=< [A)cGP =|۶-Z^3.aN(c?|n$ N C,+ 4DU9#ޠa n2P&̵fi2?/HW;.ekY7땪nǑLm.ai PMhdJ2 ߰DPLٮOl$nzbfCexU&&diz5ONN6BJBq EDHbt1V:(sPLVrjQho*~ s3Y[_BŢ?PO\+s1R=hbriO TK_oU{;:''"AQ#N"$`&L!("Uy8 Wv4QYv$T(y20K$>LˋԊK %MTj;* VHWb s*ԱY!A$Ζrxpqrb\O[(4ԦEA6-8>IS;Pnn:gAS':m=3df$VvCtKErѮ(<.jZ98~FQD7ݽۻ^JjQh\[P(R =jR+&F JTТԄXc!AhDb/AS k}'_47w{Q6Y8UBe ɜ26&oUDiњK5ձ(zrf82Q<25h.Sn.Q$6^/[Tw?}`_B}]{ 2QЁKx>":/Hwh_ȱmEslZ~0 endstream endobj 249 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 0 /Descent -216 /Flags 98 /FontBBox [ -498 -307 1120 1023 ] /FontName /DCDFPJ+TimesNewRoman,Italic /ItalicAngle -15 /StemV 83.31799 /XHeight 0 /FontFile2 250 0 R >> endobj 250 0 obj << /Filter /FlateDecode /Length 11622 /Length1 20800 >> stream H\V TU>/"FT> EI"Aq 8\(#|4*bjkJ3%\|̌3jͪ9hPH3oTvr: ^V_)roς4 %sش w է6oOw@ 92/ C *ve P| t[;-GODpN/r{TJQl3E%Wcx#Z6!xO \%^D,7[_hlN]_ޟ*B#Q3c(b z#-#'FR|>.;/[|MXs`f` W51K^Vc >@H{hqXOa4 T04Suhtd { &RQCgUx@=G zl*xq>g&#a#SU𗘜8@G=~")$HW;q'(ҹ2?r߄7f 2T,'MRWPGtq`.n;IN;kQ-ܧWu< 0\j"du܏/*o_C/keyV)a9٨ :܁\ߩrUe|bTLZj 8oq?J& j3\gfѼfxIC~"$`R/qRz PWB2ZAh=U)*GʥdDfyՒfu+ݛuD+=Px'9.N-y܍8$I+0Kj]$.\HuWvS-7*qќoVCݙZy)l&3D~<2_BPJ ko Iً:QDR LN*2z"e5fGQnC^fH)V,-lvZZ\vswrtss+zޮunhT%.Ddɂf 'W` .1^Ɲ׸+"7pS*{-hN/4)i~,Q3tO}9c3F'YK@)O,AFtěz_:*Wo^XH =ϳ[YC ›r8\w!R˘G6ؤKatSf8=P+/NDc*s? t |˩ΰ6#)QV9dBhU=0+ NKISB:(E:FGɏi7_lA?ypExuw<ڂ3auHQ`ЧJQX:zdg.#*pN?UEf~.3p[X.A}]$2T2oij4t{Z#X"IQ|!MxU25S3pDؾ@f/=S@crKmfa}c*ɿT|"ihϚ6t߯oȈ޽z{ޭk~}O2 roSZrdiom\63o-2,%baK\'v[=ML =Ӧk[b@,[k܂YZ/{Bgdj=Do/%8jS?qh-ãru;]V.JMHtt ʌpQB=W6$qY\67ѠVѰbeMOqN/UUq{n'@6n~B$B6GE0INK J+B"g mOR8 6rQJTl-s ;Ν; YE8pEsfYGrM&G8 ہ 8HM6习~Ž0G V=OJS]P):酡JjɓZ},9hKz䳤Җ}nn⏀_ݼZ"O}=uzqY{;5/geV+mBHЭ X̝؀?Ag BQq4Wn 7)[!)k)rGr3 ^_a#EWw"nWnW_'^O4(L*"ii\*KŔm1⌬1Z-F5.J| 8ic4j{NҫeA\I^o$h xKY |6^1YB r+p, (}8a6rY dZ@wm"ZYFXm^GtVGYɫz6zÀb5hGݶN!4w8` My1vAFILj;FulI..D"Kq>Y`8Ҫ7ѻF:-0m;d8ܫ@18 qFϘ CU7Y5Z$}8blߧ|bi8S0[m';i}{\Micj*c}8ZA[ tE}uU / 1D/Z󜎠9"a6WWM -*1h:@,6`|&+Z(6߁?}Wk@|sTK?ʁWDoZxc}e?--[36b^w3ʢc+Lq8;t#Meaڨ8۔0qNKT`Z{q$LW=U8z6#@=usçh cc#/˔[M g6b?& h75ӬYi{c2"e_@ȬFlhX+9/kM*1ڦ2-̧8 PAqFw_߭D⨡"ws2]PeX1wmE >pfU!߉8#U>.#y\[8sm316zׯ#rHsӰ~vaMTlk=haڭa9=("1Rh4OK g*>ٵTf6\G|&|sHTuܮu3S+W1Wrn\ p1|q %3)JV,h].=gVXPs#bo\P1B|g,3alj0zJXKڃlPd3OdVudQ^o tF>)7B/S>`8#ȥ%9ΕNik[8v24F!A-hm^@QfHJ x7-FqL^Gy#Mmb'eI,q8$xwˑ85K:ġŅ*Bhѣ\ \CQݝA[hOz8G!RJ]UCN=t\UJ{P8Tt'fvf' i53(Qd# ^6ȟ@  w^"3x>I^SOJBÕύ^\sܲǪTf\nyT9e;3, 5TUX&75; u&cZWhC Ga*0ތa`HkqXL/Mۭz6Y*ŵE4ŀp|Z+feӘrԮ^+x"z vD_ Fv=ԯT$v}&i[_GVՒYLօqڐ.!'@yYՑNf¦jFe }Fb0_ 0EIo˟ᦸܛpo~`OY~= xπ £$,~6= hȈ8Nt3<e݁B,Β.:9sHB"6JN{Pɸ.[%iմbZ~*-<-?sn%#kgLodz$.9.__OѸs2^zd:J5~pa}]z$ӰaDpH *YBr-kIayDj(E}Q6I4g.'AP El:J1~= aߤo@9\6:t9 5}PB F]ʱ*VPCtx"_а{6Zt-کFwP|%(\wBq7S;/>:4GjQDM@i0ی'IIƙ8:'n3mOkfD(L L'TȭGkGn>[s_ޡ>\8<-5\8[/6ר,~ϭP61=89w`A( %r`)kEnE`;S"07,"ǞPĂ+F(c徼۾ @/H~o?-\ccPݴmx0nˮ?_ n;ei(]Lg^j6FEC;yhÒViG ӾOmJ.C4r2MYA9<+ yyEa뙇s'^7{-x*zlYe*թ4*:(xq~A0*vy!Kq> ^TMNjYmPkǴWSxGhDb|4nU?ݭIÿP~2dp**,C[mɨiɤ )nҵ`(iaEU`]U SX4Rniᰦ!EQҵ&h[xQT)PBa'L:\.jT5.zQ Z sd2ă~ìEME6~;7[y4HcyUcpwZ'*y:_aJG?t"(pjG@ =`tļ7j^.lW{lS^;7}#׏m$v|.]!0 ljۿk7/M-VSR _[jÊUd[Տus2=<6:-{9{dK>ުA3!e[zw׃)qݐ:qHs(Tfb.bry '/^/ycd>E<+ˁC8_b31DZݜI"61!K/&\La a!&`50pPDn/dN]FdD,~1v"Y/R%<'׌kب#l̾#!d&04.@Nxh:Ot$0Bv_KV%WIjdjo5 8?e7hM1)Gyp?|Q ӟA!8y(|<߀Ad)ܾDVV\lo =-6^8|cPح1\SΊ=l$r[Qz$^ L|l93n ϙ-1[?Rc8H T-U39ʺ݊ $< uJ.bS'/Pغ_0r誷WoyO\ ~r9O\ӁOV9f$0 ',A$G|H[Kj&lY]S&a#QSh()pHw F//)aΠ :pJ;ۗ2(З:j§ċqn3 32[ևUWz|< 9V(@-W!˺=%!Zt&_X˜~wb+Xd^D~Qѡ=:XSIi>poݚ y(vw쾺kG㛆[sh$N5)v%>,9`1:,L_F@ L޸Zb6fS% X[~u8?dz4a:s:@F 1ZUƊffJ\P3*B4沘PB 4%l RX걒FّݗHɗb7tNyjr%Y,@aa*hZ:Pz =`Jlؒ!ۖ=+L~|pmл-wgovd+ZUyze]K39bkG>|8+J4.5OȘ\$5K&_T:./LH3.MM32 f!Qia8K S;XbI\DVOu[h D9b' C+GЬh' VZص??qHwK(WhRz`> BkW~‘X(لmROWڂڕcW{bTs&vKv 6[׷biCmюOX>vݤ.&;?ϝ_|>8/q?!Ȁd MHJ+h*CЭPAƘҭ(JiBPZ i,[:ME^MӴm_d=Ib ;N}Qg`Hps H%| dX(Mz,H^P -1O;%/x/$#i)Y{?:$UT$ v3کKO.1 IK$B'MG YQYS(Vt?(7833KU=1Ox^qJũg5c&,~{˞T ji=\+Li<睌悛7b6=ZZb\M6IJ}M{z48{ɫ9ݖl LXgѪ:XA{QJjB#a8 $CҐL00 5^ ;0 p+ zAHQF0ΦPTI7LB`N(bSeM _2L&SɄmJIঅ04k"pE 15,H$H\6YP;rځHcXex!0 |%90 WԑYZ\|XzT?_q]{U`M;!֡n<$YJ4FQ[*6xhZijkt X )l 'JY]ߨvu6`Ng#pezwɻS;1 !4yH3V*L%C8e6rwTۙN~gՀ eXCf빐Ntes4EP`Ï#/@EuPURA`orNVrz0Ǣhqi([7 j oN'S>tFW<^pȞҮ=J]PUuu--x9˶•;) 4T@BkfY͙v"|WE`0b]<xD m>At [v QQskBU(XhJ)(R۩C uSsd ZM`Q ڠxU݈C1e "1ǾpKe&,[2%@Ӱ2 +Ӱv#2+gu,R^ 2u ']w>|6;Y.c,L#B˜OfdFns=9gVʲRi+kjt뎓}NQ"LWԧ{2'K(>GNOP\@[:"9h$`+wC,BjiJQ|R3y7g$*"[+<.廲NʅI~ޭSFqi23n9hyn#dHFFQG5F/.#Z=10#ǖsF b@PKh|f5=NoɭWOu910@AL5 Î9~10> endobj 252 0 obj << /Filter /FlateDecode /Length 9420 /Length1 17916 >> stream H\U tg3IQyx(Bf&īQ 2H4uht5Z6PВx,&ۍ;|s}~1Rw98;י_y'`d @ g/+Gߗdn~v^>"{.~ *ݝ3Ǚuq;.sYޗ}HNnAad]} f;m \gaW% E95GɀT9Nn`i_2'әb n>dn˺k `u7n(A_(0_#8'8x{@Sj\D j5tUTlcqQnR x_L1F@J{J PZh0Lڨ>_N]s$'Јgԅ)h^:ը&@ 9a(ț>Td,1J_.DfePdKLtiZFx:ӑ" c'΢7DfEa@醚v=NoB#Ѹ+H"UlK|KApp\$L(I+i :@?p$9j*SZKOXAAґpW:I$]pWłJb46S#8SRoT7jڮ }^kv3NF?cф@qDgT!VJ.7bX?(ylm@2ċB)%~QQ gFCXg<3x#fIHTW1uAkKcdݩ*LpsW_n;۽ɽ1F3 i^9a͸f<ԄLA҅qx3I̿ocDyଓJ8 oHyQ(5.SC_IW_[kX Zk7N8}S?ӟLij=bN1`lx O~~LլSV&iY\I GTʅCͲ&uN*uvJTX"ZOsfRj5NaEN#Mߤ!3)Sն yQl<ڪ-'|ۀ~OEKĿ*LKS3- TT+7)T6._s Sa>e o"i (ro JJ/A(3NwQK%X^B5bjnX>!ܬ*.ԻoKjh* uZ:]>q:{,-U[+eWKJMVRo6IgPVJ~-U(=-ײ0eH`#1Q4yIN{@#c]SE<)v.4S:}tI:dmTA5!H&%f7eN|4qZb}6$&&&ď7l萗c п_Ȉ>/X{[zԽ[.;u׾][6>^f)&D9IWXK ݺ: E~)dx,Lɹ'dOgQ:cZiTߴ[Y\M~ ,,E+iYZG]UYms|PF6¹UOê^m)W7jzRg+eR=8xZtl.XGGzD`ql.nje^+TEծ]_̌H,ksFK9)vہۊ^5@UWܻD`$> & XT0FkyBOjhgR@eQcf2Ěı1-~gOt*||{={9k$(O[Q5~|8|< ay+֦j7lh6?k,y\oQIe0Pe,gs"2:\$vKOJH\̝A#.!w BQh0j_J]*RtK630eހF^TVvTd'8(Á*DCIA}6\"$wJ7wvc+U7rɴXDW#}ʥE:އz#=7_3~N:*4@=WVb|5p?QwuWl`tJ׈kaΗ|zw@dcww\zkΆM]#*h287k(:znh܋D1 za|4d~ r!-KIZI9mAlm X7x7ͬY*Q@`g)n8#dިmՔn'hjH^6^u@zkUY Iҡbt,]MeЗj ʷs{b~g߸Y8 v}9g>/x|&6?k o6A +@83N8z>@+ 6DA\"W}_868`Dڃ|[Y%|p1M-9 4I1a{=rض9ȴXU"nl|'vqEm_\pVtNSftܩfܸuM3ܾ™- &Z_NܝNڂv?q-@0&&SR Q xghAF ohxQX٘$kxq5qac9__tЛ :l j5ZxV.JH66E|6s{&'b`Ll!&W'vg8q`{A6$bSʒ}@a/.qҗT!)U)/\Eз|9?̙7 yaqў( ۡB <Ҁ6SF0e !f[b Ȯ߳;ņ[LX&d1# -ww٬fy[@TL4-:*(Fz>TxNt k6:@G0AΓYr9*R:k0]u2m#ݥ+Q[OPC`-ߪ`kddPPA=o}V7I tHjAIG2~dtqCSG2iF~p 7 ,T]~___˛r\V"/ 8~lwUW~yya+{ )BQНRӨ\=Q9C/ڒ67CQ뚇eH1Ui֖K䑥C끫 LFpayCV@%R2Wյ@Z]3LxJgCD|""<D>pZ@z `9~Ӻ?ՕfOt%l?r O| ͈| QssÃy/𾅌ܾ'7'[w!ŰD1WZy'Թ.p V=J-d×%6sd04ȥ++zsHҵ ~ 4xV ٣R7>f`N :]e4ٗ %1A>V'٭i8up_cz}]:H\|gNe+YQs؇0!&2VZ(~ f#\y}G 7` :Vq=6f#@a?Ñ%*qM.Ij:[>ȯŲKf/%`5qjzz#,U4GWW$/3IqLl%b")٥*Y$I2KD$$eK4Ef;'Q0v, M|FCPhHjmG:JetK(7Nb>J_ۉR;O2i)Zq]7yeigg4VG)~> QɁ;\h]/}C//w?.wȓqjmo/+T:|s49FƺdS_4c]=܎>2$Wv~.=Ðu28EAø!#[2o2x9]IH:Ҫ+9fj$UV.@qɤ3ɘp7t+9wwm[_s-9p/kkPG_-3v+Gˮ2%-tץ:-|i3HE(x=|ԁ˰?*]SWDFq/CTeVkTBs\zSʨB 8W>-??p眭[`bq p:[[P+k9QnP`E͢UD#p05ьD.{yŲ%{nMШjy~)֔Wb_`r@ SuH8gr)}o+/#:jzVǜcJRMuMjaqRmEM`UUUq"ǃ\Ujweݖʪ*r"kRW Q$jr5 ͬzy9痻};}}g;!~wH /0J1mZGZEQS6DR":އ)HTZi"$E 2USF8{;?|}xgVEPhl8 ,=7Y2NZg2L Vֺv[`EЁ2x碉)Kp5KYZyKgiZ k2 NG\ݑ@w\◇^T~K.Ou;ȿtmSlӏ;vխ: uJzvEh?Q¯o񅙅7\=:B"TDˁz,d lT0 ZA,=6i?$R^j=Wi<Ζ|N}c-E@cY0PZ)Z5"Ke pPdY~N$0LY"+a8;"_x1h:^8K&‚j.y³)2lT#I'2ki-IVϒRotpofLnC6%ժgԕ`ءѕI2{Fe؃ ]2^[%h+ܰ,"]Dfrp>fs*K+/grWY9: Ϊ]!DV6c(oB|!Me 58Zra3oSԳghkqќ{[%>z`őN?ߐYyoM6Ekw5v!]EgPWo. LTZ&h୼.>U47̳͟@|0E!+-YZFyQ9^觙(J kMqӜ+jF+\ŻBu0ݢ v} u>l;f2(Vˤxxv`>78_1a"߱3#6k@3 U Zĭʃ8P58hS4Pc!IPT9;)c342۩?-|J&|QҤ2 +J; B('I9|nOưdTEe41k)N p?k'N}BgnD*{ J}+;aqjeG]7?? zPniT@|G#}2}D1@(j4i6T݃-)Ƹj'&i33@&G2<-5sIH'&񵎃Eo?iöNǸ5Joq BLjkvt7TҌֻf/Y?m|V=~ ÛVδ/ __`_ ',^tZ~kvԪ#q g<@Oe8[.L{na.͍x\noSVqAM:LT}ٕ]t$L'Hd:'`$IB&Og|:anrj+a(AQfIm0 A#pҘ躚 ǥCt.74hcG:mo)PHtyЛؾwa,s#fPtczN-䉝׷t2R7͎KbI/= oX Nu.U: d~/.Q8>>}_:=#N_ߕ9,+ \ ̰4JgWjo9;iI6=+i/I;C6F5@-cX=|0j8fYQȁXإ(wa?7R*"(g{ |Q͌@\E|݁T-+]ƾZ6NC0=J"9@@TtR /BR7$昧0|bab`Ǝ>003 @ ă iLB@pD쁀*8@Ɯpb.L!VX~{8q86 )F endstream endobj 253 0 obj << /S /D >> endobj 254 0 obj << /Nums [ 0 253 0 R ] >> endobj 255 0 obj << /Type /Pages /Kids [ 269 0 R 1 0 R 4 0 R 7 0 R 10 0 R 13 0 R 16 0 R 19 0 R 22 0 R 25 0 R ] /Count 10 /Parent 256 0 R >> endobj 256 0 obj << /Type /Pages /Kids [ 255 0 R 257 0 R 258 0 R 259 0 R 260 0 R 261 0 R 262 0 R 263 0 R 264 0 R ] /Count 82 >> endobj 257 0 obj << /Type /Pages /Kids [ 28 0 R 31 0 R 34 0 R 37 0 R 40 0 R 43 0 R 46 0 R 49 0 R 52 0 R 55 0 R ] /Count 10 /Parent 256 0 R >> endobj 258 0 obj << /Type /Pages /Kids [ 58 0 R 61 0 R 64 0 R 67 0 R 70 0 R 73 0 R 76 0 R 79 0 R 82 0 R 85 0 R ] /Count 10 /Parent 256 0 R >> endobj 259 0 obj << /Type /Pages /Kids [ 88 0 R 91 0 R 94 0 R 97 0 R 100 0 R 103 0 R 106 0 R 109 0 R 112 0 R 115 0 R ] /Count 10 /Parent 256 0 R >> endobj 260 0 obj << /Type /Pages /Kids [ 118 0 R 121 0 R 124 0 R 127 0 R 130 0 R 133 0 R 136 0 R 139 0 R 142 0 R 145 0 R ] /Count 10 /Parent 256 0 R >> endobj 261 0 obj << /Type /Pages /Kids [ 148 0 R 151 0 R 154 0 R 157 0 R 160 0 R 163 0 R 166 0 R 169 0 R 172 0 R 175 0 R ] /Count 10 /Parent 256 0 R >> endobj 262 0 obj << /Type /Pages /Kids [ 178 0 R 181 0 R 184 0 R 187 0 R 190 0 R 193 0 R 196 0 R 199 0 R 202 0 R 205 0 R ] /Count 10 /Parent 256 0 R >> endobj 263 0 obj << /Type /Pages /Kids [ 208 0 R 211 0 R 214 0 R 217 0 R 220 0 R 223 0 R 226 0 R 229 0 R 232 0 R 235 0 R ] /Count 10 /Parent 256 0 R >> endobj 264 0 obj << /Type /Pages /Kids [ 238 0 R 241 0 R ] /Count 2 /Parent 256 0 R >> endobj 265 0 obj << /CreationDate (D:20010914085240-05'00') /ModDate (D:20010914085240-05'00') /Producer (Acrobat Distiller 5.0 \(Windows\)) /Author (figueira) /Creator (ADOBEPS4.DRV Version 4.50) /Title (Microsoft Word - 2258357rev3.doc) >> endobj 266 0 obj << /Type /Metadata /Subtype /XML /Length 1084 >> stream Microsoft Word - 2258357rev3.doc endstream endobj xref 0 267 0000000000 65535 f 0000156164 00000 n 0000156316 00000 n 0000156507 00000 n 0000157363 00000 n 0000157515 00000 n 0000157705 00000 n 0000160078 00000 n 0000160230 00000 n 0000160420 00000 n 0000163269 00000 n 0000163424 00000 n 0000163615 00000 n 0000166592 00000 n 0000166747 00000 n 0000166952 00000 n 0000169018 00000 n 0000169173 00000 n 0000169365 00000 n 0000170491 00000 n 0000170646 00000 n 0000170824 00000 n 0000175188 00000 n 0000175343 00000 n 0000175549 00000 n 0000177673 00000 n 0000177828 00000 n 0000178006 00000 n 0000178480 00000 n 0000178635 00000 n 0000178827 00000 n 0000180905 00000 n 0000181060 00000 n 0000181251 00000 n 0000184566 00000 n 0000184721 00000 n 0000184913 00000 n 0000185957 00000 n 0000186112 00000 n 0000186304 00000 n 0000187713 00000 n 0000187868 00000 n 0000188060 00000 n 0000199622 00000 n 0000199777 00000 n 0000199983 00000 n 0000202824 00000 n 0000202979 00000 n 0000203185 00000 n 0000206168 00000 n 0000206323 00000 n 0000206501 00000 n 0000209028 00000 n 0000209183 00000 n 0000209361 00000 n 0000211450 00000 n 0000211605 00000 n 0000211783 00000 n 0000215096 00000 n 0000215251 00000 n 0000215443 00000 n 0000217709 00000 n 0000217864 00000 n 0000218056 00000 n 0000220964 00000 n 0000221119 00000 n 0000221297 00000 n 0000224201 00000 n 0000224356 00000 n 0000224547 00000 n 0000229248 00000 n 0000229403 00000 n 0000229595 00000 n 0000232409 00000 n 0000232564 00000 n 0000232756 00000 n 0000235782 00000 n 0000235937 00000 n 0000236115 00000 n 0000237365 00000 n 0000237520 00000 n 0000237712 00000 n 0000241506 00000 n 0000241661 00000 n 0000241839 00000 n 0000244099 00000 n 0000244254 00000 n 0000244446 00000 n 0000246934 00000 n 0000247089 00000 n 0000247280 00000 n 0000251186 00000 n 0000251341 00000 n 0000251547 00000 n 0000256322 00000 n 0000256477 00000 n 0000256669 00000 n 0000259293 00000 n 0000259448 00000 n 0000259654 00000 n 0000262221 00000 n 0000262379 00000 n 0000262558 00000 n 0000264147 00000 n 0000264305 00000 n 0000264484 00000 n 0000267550 00000 n 0000267708 00000 n 0000267929 00000 n 0000270774 00000 n 0000270932 00000 n 0000271111 00000 n 0000274977 00000 n 0000275135 00000 n 0000275327 00000 n 0000277294 00000 n 0000277452 00000 n 0000277631 00000 n 0000281146 00000 n 0000281304 00000 n 0000281483 00000 n 0000283403 00000 n 0000283561 00000 n 0000283740 00000 n 0000286308 00000 n 0000286466 00000 n 0000286645 00000 n 0000289773 00000 n 0000289931 00000 n 0000290110 00000 n 0000292149 00000 n 0000292307 00000 n 0000292500 00000 n 0000295565 00000 n 0000295723 00000 n 0000295902 00000 n 0000298571 00000 n 0000298729 00000 n 0000298922 00000 n 0000302743 00000 n 0000302901 00000 n 0000303080 00000 n 0000305424 00000 n 0000305582 00000 n 0000305761 00000 n 0000308394 00000 n 0000308552 00000 n 0000308745 00000 n 0000311947 00000 n 0000312105 00000 n 0000312284 00000 n 0000313617 00000 n 0000313775 00000 n 0000313967 00000 n 0000316055 00000 n 0000316213 00000 n 0000316405 00000 n 0000318863 00000 n 0000319021 00000 n 0000319213 00000 n 0000321547 00000 n 0000321705 00000 n 0000321897 00000 n 0000325340 00000 n 0000325498 00000 n 0000325690 00000 n 0000329269 00000 n 0000329427 00000 n 0000329619 00000 n 0000333059 00000 n 0000333217 00000 n 0000333409 00000 n 0000335569 00000 n 0000335727 00000 n 0000335934 00000 n 0000337034 00000 n 0000337192 00000 n 0000337384 00000 n 0000342072 00000 n 0000342230 00000 n 0000342422 00000 n 0000347755 00000 n 0000347913 00000 n 0000348105 00000 n 0000353010 00000 n 0000353168 00000 n 0000353360 00000 n 0000357710 00000 n 0000357868 00000 n 0000358060 00000 n 0000361703 00000 n 0000361861 00000 n 0000362053 00000 n 0000365490 00000 n 0000365648 00000 n 0000365827 00000 n 0000370332 00000 n 0000370490 00000 n 0000370682 00000 n 0000374342 00000 n 0000374500 00000 n 0000374692 00000 n 0000378683 00000 n 0000378841 00000 n 0000379033 00000 n 0000382733 00000 n 0000382891 00000 n 0000383083 00000 n 0000384313 00000 n 0000384471 00000 n 0000384663 00000 n 0000389040 00000 n 0000389198 00000 n 0000389390 00000 n 0000394877 00000 n 0000395035 00000 n 0000395227 00000 n 0000400212 00000 n 0000400370 00000 n 0000400562 00000 n 0000405906 00000 n 0000406064 00000 n 0000406256 00000 n 0000411552 00000 n 0000411710 00000 n 0000411902 00000 n 0000417121 00000 n 0000417279 00000 n 0000417458 00000 n 0000422440 00000 n 0000422598 00000 n 0000422790 00000 n 0000427818 00000 n 0000427976 00000 n 0000428168 00000 n 0000433271 00000 n 0000433429 00000 n 0000433621 00000 n 0000438570 00000 n 0000438728 00000 n 0000438920 00000 n 0000442980 00000 n 0000443138 00000 n 0000443330 00000 n 0000448323 00000 n 0000448890 00000 n 0000449295 00000 n 0000449692 00000 n 0000449929 00000 n 0000469621 00000 n 0000469864 00000 n 0000481578 00000 n 0000481822 00000 n 0000491333 00000 n 0000491365 00000 n 0000491411 00000 n 0000491558 00000 n 0000491692 00000 n 0000491841 00000 n 0000491990 00000 n 0000492145 00000 n 0000492304 00000 n 0000492463 00000 n 0000492622 00000 n 0000492781 00000 n 0000492874 00000 n 0000493118 00000 n trailer << /Size 267 /ID[<134609d62d82bb98ce90bd9224cf4b58>] >> startxref 173 %%EOF minc-tools-2.3.00+dfsg/conversion/dcm2mnc/doc/philips_intera.pdf0000644000175000000620000203253412574624760023615 0ustar stevestaff%PDF-1.3 % 536 0 obj << /Linearized 1 /O 539 /H [ 1433 1565 ] /L 537948 /E 108011 /N 71 /T 527109 >> endobj xref 536 38 0000000016 00000 n 0000001129 00000 n 0000001291 00000 n 0000002998 00000 n 0000003232 00000 n 0000003410 00000 n 0000003917 00000 n 0000004510 00000 n 0000004732 00000 n 0000004946 00000 n 0000005511 00000 n 0000005718 00000 n 0000005759 00000 n 0000005782 00000 n 0000006219 00000 n 0000006435 00000 n 0000007554 00000 n 0000007576 00000 n 0000008375 00000 n 0000008397 00000 n 0000009364 00000 n 0000009386 00000 n 0000010313 00000 n 0000010335 00000 n 0000011241 00000 n 0000011263 00000 n 0000012218 00000 n 0000012240 00000 n 0000013169 00000 n 0000013191 00000 n 0000042362 00000 n 0000065788 00000 n 0000065867 00000 n 0000068543 00000 n 0000081783 00000 n 0000106918 00000 n 0000001433 00000 n 0000002975 00000 n trailer << /Size 574 /Info 535 0 R /Encrypt 538 0 R /Root 537 0 R /Prev 527098 /ID[<4ef7e63bfd6b8619637083eb9af585c7><4ef7e63bfd6b8619637083eb9af585c7>] >> startxref 0 %%EOF 537 0 obj << /Type /Catalog /Pages 527 0 R /Outlines 314 0 R /OpenAction [ 539 0 R /XYZ null null null ] /PageMode /UseNone /PageLabels 525 0 R >> endobj 538 0 obj << /Filter /Standard /R 2 /O (r幛4v쭥=4y WҵF) /U (L$\)h՟ݰW\\T) /P -60 /V 1 >> endobj 572 0 obj << /S 2412 /T 2545 /O 2676 /L 2692 /Filter /FlateDecode /Length 573 0 R >> stream ~uX dtonrJ& qt3y$ovi إVb2#*N]wsxa14$jlB|-N?<:q5 _ɯ˻J Ƴ;B]LE~Y"w )-\l}[IFD$ںY4~*J9,[A7렵 0p[xHLQLt$ R6{OtاDD[J'3T^L^׾kb!:^D'? *B } & &Ρt[qMeՃsB(40Uc5Aˋ* 1Qe ,!Cpx 糬[C~~cbOu4K;2Z4xI$P]dTpۚwmmdnwZSZPuɅН"CڷFU&1$r׷}k:QӞ@c?3Pj@9nt)(6mxLw"q 59F֎u"aUE•YU2s OxǍ~ƾ i; K]w騎q.5JˊՓs{j"J&J V f@bzJcQ,qg9݆<)FDzibKw0ՙ#tfXJpfua!{ׅ!s?.}/)VGc46͢*ZH'yfu_ɵ_*s8GNNm/O3sJ<3蠾Mwr\t-^pƸT?B7.ݸk,4<{gaEH@U|̗裱ŶrŹq i~oA8\q,ZMKĮ*b.F%zN?, S2Z2J'b'2Mq&>OE9Q\,3Չ7 =6 0qeÐH\dUe:}i{]@E&X$k-E譢[]S;t ڿpzmEX2 A'nTa,F0EΞ}CնMB) Ε[!QYL+dೞt#vo]|4qGH.a/( ON nbsC_]i[z5 ]:o7O>"[ۈǝ% endstream endobj 573 0 obj 1430 endobj 539 0 obj << /Type /Page /Parent 526 0 R /Resources 540 0 R /Contents [ 551 0 R 553 0 R 555 0 R 557 0 R 559 0 R 561 0 R 563 0 R 571 0 R ] /Thumb 379 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 540 0 obj << /ProcSet [ /PDF /Text ] /Font << /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 541 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 121 /Widths [ 250 0 0 0 0 0 0 278 333 333 0 0 250 333 250 278 0 500 500 500 500 500 500 500 500 0 0 0 0 0 0 0 0 722 667 722 722 667 611 778 778 389 0 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 0 667 0 0 0 0 0 0 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /PNILCM+TimesNewRoman,Bold /FontDescriptor 543 0 R >> endobj 542 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 149 /Widths [ 278 0 355 0 0 0 667 191 333 333 0 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 0 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 0 556 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 222 333 333 350 ] /Encoding /WinAnsiEncoding /BaseFont /PNILHO+Arial /FontDescriptor 546 0 R >> endobj 543 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 0 /Descent -216 /Flags 34 /FontBBox [ -558 -307 2034 1026 ] /FontName /PNILCM+TimesNewRoman,Bold /ItalicAngle 0 /StemV 133 /FontFile2 565 0 R >> endobj 544 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 0 /Descent -211 /Flags 32 /FontBBox [ -628 -376 2034 1048 ] /FontName /PNILAC+Arial,Bold /ItalicAngle 0 /StemV 133 /FontFile2 566 0 R >> endobj 545 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 148 /Widths [ 278 0 0 0 0 0 0 0 333 333 0 0 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 0 0 0 0 0 0 722 722 722 722 667 611 778 722 278 0 722 611 833 722 778 667 778 722 667 611 722 667 944 667 0 611 333 0 333 0 0 0 556 611 556 611 556 333 611 611 278 0 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /PNILAC+Arial,Bold /FontDescriptor 544 0 R >> endobj 546 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 0 /Descent -211 /Flags 32 /FontBBox [ -665 -325 2028 1037 ] /FontName /PNILHO+Arial /ItalicAngle 0 /StemV 0 /FontFile2 570 0 R >> endobj 547 0 obj [ /ICCBased 568 0 R ] endobj 548 0 obj 1039 endobj 549 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 121 /Widths [ 278 0 0 0 0 0 0 0 0 0 0 0 0 0 278 0 556 0 556 556 0 0 0 0 0 0 0 0 0 0 0 0 0 667 667 722 722 0 0 0 0 278 0 0 0 833 722 778 667 0 0 667 0 0 667 0 0 0 0 0 0 0 0 0 0 556 0 500 556 556 278 556 556 222 0 0 222 833 556 556 556 0 333 500 278 0 500 0 0 500 ] /Encoding /WinAnsiEncoding /BaseFont /PNILKJ+Arial,Italic /FontDescriptor 550 0 R >> endobj 550 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 0 /Descent -211 /Flags 96 /FontBBox [ -517 -325 1082 1025 ] /FontName /PNILKJ+Arial,Italic /ItalicAngle -15 /StemV 0 /FontFile2 569 0 R >> endobj 551 0 obj << /Filter /FlateDecode /Length 548 0 R >> stream z"/!SlI/%G"aCtFB^%hhA5KQw 1F4 ETmX`Lqa g]H8eOGa58ߋyLi{t$b@uxur2@e|1L3N{ 7 ,"pan BKBnC=w Ektog&QABDa#I7wazQ[TѪ-&O!)a;^l uso=ܮ{ҳΆ\$M{D8>[D?IP9+)3DdPр\tR%ʸBF2c^{lQ}d0& 0B m"[⏔LmݛÁJg5k;)W8M8^M6݁(;O7=8cKkC+rT x5+|'4~𲼛Q9(7@x-wf:үe˱Lh`H\> stream ڒLn!yt%M/k>A۱Cnrn[gR*0xz&%b֜`oW'[4R,zbJɎex7]$"sMùs9h}Iъ0, aUCX?O2iA$ӔIWtB6ض_;s;?[H3#B`yMIE@nߠm_ՉtQLPB Zdp-v{_i>7)V*#k$Bu6иte؉2#<T_5ԍC1C?W 37L y qPV`{ 7&O؎l-t\9e!ZLϽ_-&!Ep`Eޣ,}sFJU 'yawv!"A endstream endobj 554 0 obj 887 endobj 555 0 obj << /Filter /FlateDecode /Length 554 0 R >> stream /v p8rU/lwM)]q| q(}?_֊WF\'~2LP}26kr!3Th*}:9l2m}L8NcgQ,k_B"p݊Қ͆~8i0PVÝAf)X3}3r5Uj4Ԃ\o@-udM@[A*"0q endstream endobj 556 0 obj 847 endobj 557 0 obj << /Filter /FlateDecode /Length 556 0 R >> stream :Dd)U,WLhg,Ԍ⠒ff} ݡm:=,{ 0s"Sc2zDVp+ɠFQUk^]3A'QdW"`8ιPSc5LDy{R4%nNy=smf ;FS zAtq8\9Qz٘~mͅ*/2IG^/xdFa$;A6TJFg1AvSVF$~F'3l D+/a8O}5DUUܫ' Bf1Xa17y ݘE$G|oH?iO@P^F%`|x"d+څl]y~WWsPBFf=!~: o*۹2M*Z BWbohy6&uFI$'-['-Rf [" }T| }50̷pIɍܡ]J%P/[-uEuPc#fuߞ7cjMq}7=OOTP>9* BgGq / 5>O$y\|rii,5tL@S_VcKpʚO~NĐ`)>S!/}YF!HA-NG3Kn#Q9:0ʤrJ)x)O{-fD#=2"WzG( LaLd~NsPܦv h)4 endstream endobj 558 0 obj 826 endobj 559 0 obj << /Filter /FlateDecode /Length 558 0 R >> stream " [ѠF\z*vLa/l]r&8jXŁoɴ4?A;hR6vƘ"q^AG9OC,1DtmuP8%ѷom ~ = :~z Ⱝ +ScqލtM٪Ч3uw'uR},i6.Pr9i쵏cʑ"Ie% *o dU PrZSOV[4Mf9F/A2iiB.. 7aU 8V Sw9miȗ\ 3;&iG朷脆s^ؘa_U%t(As{k;LP4wCFƥvd/`",~s2x\qj♦q<ǹR s;y\ lW%;yox9^~Q$z, xjWXo _Do HXY)&,ιt~Zme:!hAgsI/fLDv5vV@Y/<Ⱥ|t(̧*kݭ`yl53],ʊ=i3AF֙MYm,gp.q3[9jVn< X&zEwZA&Shk ӿtiEdil%ySLz^E# BDb)=i_NI` \|"EMkW& endstream endobj 560 0 obj 875 endobj 561 0 obj << /Filter /FlateDecode /Length 560 0 R >> stream '"cwR]p3ţG)$tkC3 s}|FnLQ>7mdwYZqrc&ȃ5.sE&$36~y.cRZo>s )fllMd"Ճ-7JiCu лew@x=hi:J_1':1à H&Z 2t̖S()ǿ92z:7>Wݎ ıFkSA6Q\ָu4 h՛ ,>XvuԶ+!)cPeJ4n*M(Yw#1ã\yqZ ۝'?T1 wzfwJηPgJODJ[7f֕EP7Q&}veLD"!{KVd5ˁg/ZT441%&4\hhDCZQĂL&s|&fre&ZQgqhՔwLLrETp {SifL|)S˳){{Z~l6z?xRvw`HC1oO11s @]$"G2w,/kari4*%g?ăX> stream 73(*Gp4ς[ lf tc8@MwG]W?WHan61ULY t_007kQ#^Iך [ŸJO 8jj#!}111 LiTF=@˂rM00dķ Zxdz";S3{;of.Hi ƚܽq+0Hp5 XqzB,ldQT".m)C4_߷$y)* + =vV̆wMo,b"/񬤹]]-@Y燲w,thRo_6#owD_ۦ M s>KRcSg8cKf*9* id'! =N3d%c^{xHB1%l^I非 endstream endobj 564 0 obj 845 endobj 565 0 obj << /Filter /FlateDecode /Length 29078 /Length1 46680 >> stream &$GR~YT+4u :@WWBx#/UEQ3j.ׯO>qcZ}q,Xk~bgѢ&*<ʇ)t5J>?2ѶkCBQx E#2- Ag(?ay# b`󷭙(\jrr(ODuxOgbxAזJ^ fy`juLJ* {qV^:[z voYN$s#EcΌ{ճ(Z &o+ a9Noxא]|v;hM$$9_;;F$* H8r20' lMLHA%r lDJ5UQzsV Q~9c*J6ky%TCuxN1Xv9 %^N~ `x,-~~ܸR,@(D#ͰUۄP)%NMҪ%7Se0X^-HW^~łɃNR,vL(/Xp쵭_X%8Y.8A9ek*+ͨ>vZ!]޿>д`B?"6 :X[t'mK>PBmךkǤ _P/Yxusy*1hHCk -W^#>K/±d>s2gZ?D <ÞJ4y=blܖTk9!!&/ _% ߖb!cߘdnzzXh+EZHwhǫԍwx$쭋7KU얷24?#EIyv՚7=Ӈ4dXx/Rg6库Î"ۅ",kHnҝ8?le՜63rTnǔdy~egl `3,U}7f~ )&[EKşß، ɣf#[ 5ߑ;JmwrmH֥5~9g#WwOE(x^GbtMR}Lxbd+n‰Uv;XmYMvupp9{8.H4w KqB$gyq& >lRF+G?!bS=%{x\a|J.IDAK}q &WjrSjBi?ςRG[p@~nLTPj}-AvAZ))lդ`7 W8f+ `6yut:Cz.;fn{Յ($}R,aP'4oZy\IxD A*#]0ZéX/n?V~M\GLzP<+֢Wڕp]_PZ* 'Ak1N8P7$uW}]~9ּ @ N өA2fw!?1! >Q~CnnIzCf$D@{T+pI(z|1.%@ Xҳ]y!:=8 eDPY9?pHAcFkU"ңL S`6:{`8opJ`Wt>4Lx :zRqHeѬT[k=*'@zcO;`2u=~0)XH}Ain (rhC 6~2*qcB:Y";u>/oLlbas-Լ+I3ȃ4ҒppLy:4fGA Rq|M> HhMْշSyYi+֞BU0̒1r|-W,c8@VP"iNU[X#2&6:Y);y_߆=d+Q5hА_X-0Ě4bMgD<Y"PCt^2qq0WkH^p6ToUPmKFK X2 yNp^|DcG!zpe{<Ԯ]DnuGwUy؆Czz2*p]Б7e ?蘣b-| U`Ӑt*L\+ c"S! $<)^߄}p>*p^c⯈ho6/dVƶCm0 [}dT1C!$0E-lNj 4><>늬qs$< @st ߆|j 8H=ٮ"5:E,tѧuΰ]xoc 43=l"7ժ\Lrd\̕BjۤL0٣u}r*G"`] DüS:KfxJ ֤*M4*[;;y!-͟ryǀ@k"*k@\,AWF#dc` K ܓ" %YOSh\ȿh$~g(͍a$;~h>-U ώLN7HD}^L77Mv*s<M:c:]f@h8M, om϶[*ӀO25.݊>XR=Kswk,vOuyCqSWr!&;% uS&" f"vҠa±T ƭhMYz""[2:Gh\[u^\4.ԵiY>6!IDLhNJØD:8R 9iSg.oA y*pBzYϊ=#>5깚P @-ޏ7J>f*ߋrh-n7PG*-d`F⻓,  cahwZ[<ĉ âM"kU!ݻ.cv(M!J|] ˎ'1ez~=]ww[~*oQmn5 S`^*cu}Y&dWk\[`br!9tP{' Jy~>f)4bT-Wɢ}r+*9M[[goCNO;4ijT,TCE^J"@>BXP9^++W1 9F%&ravv~I1=ye]A/7 y>#ͱ,-& D>>d5IFm7;Ip%)EXq=tϽ$F9l(`KևW= nlDp%HU4JH+g$ظׂI#?Y]aT3( 86Qʷ3AsţZlz5b,Ӌ֝?:JZKK_E8*j_>(Y> JfLHZEzĩ, Q?='|oh`v~+]KS֘^  XBimn>~kB׆ +ĥk a}E7Q#xK5CTh; }LC9-[M0RGDjSR"K3h MOdlIB85M;E;nb^d,OUY:O  5RQOl '#.fЊB7iOg&fa20\"^Bs?\5-1qT)g*}h``Űh# T]$L[k/KdOKtQ'!wy_RrZܢ}(1Z6ܙ7 PA:F߾L>?1ge=V7n FL}㏟ 闄~b0W9BF4n_bnUof_by&Uq_ @nځ,⺀vX~AzmIX֖%>;j%0f+#Ӟ{ܗcm2`)2b;,B#p@[a8A1V#|(&Or'VѡJf<[ڿ D{@c!&W@Vim>9+߸ժܩçLpg]*"ij 誧nzn)LIzIqdD/hCW;MFK(6MtX >9May")O7F* *OrJ#r ZFg7;/A"SB$r @Z 0 ?>w. -C ]} ɷtRRsvvЁ*|rs [5ŵ:  [۩ 7 q*Qeb1)l!bc3f[U 90OCLE,)Q8;C.pWIm[* ֺjz 9=sۀҋBsBlV!)uWX3[ےNbΖE4x0e'gntUWW5*8*4ZȌ@ 1 zgr?Ȧy49_ޅ> *,NeHs*Im=ccgpq7l_0Xĺ2=!HrݪW<˘uW ˌ I]h%4I/Mb.ff}R;\s[:*f& 9ɊRVeBCA?VMե/T |/0j$l N8Ɨ{^V[z/dMjT'->9Ѹe=[+%VL<3WX0HvXNֶٝ_3 #_)PDԪ 3sk@xAb8oJ+{nݖz*P#_/t7rT ѿΠG;.H =щ,7e >֙٩#HΆ$. L5=k 0@gՈOMC|Wz*_TRk5Ld?_Z:)*zcAڼ,rGc 0$l<{z= 7ֺqQX yN=&|*u"SlEJz h6[F0`11Çs^jt Yԭ} ='Bg\Y-8KQ}&AUϝCcv46Do=^3,)L7I)$9:KXA|ե *\s:4fN^h,eYאˬP!8Q[=F_jJ+&a "zAķw!!OߖAhH OYjWy+Sխɯ`3'OAu9l?~b֍t{ʏt;%!D>973{&sUb圶b8ʪkV+7WJ綛8ޱ: 6 G|H0ymjhV,bn |[wTERY[g %苑700."!/ VMNa#}\sJ!Ck| 69gMDCB7(6jai3v#|tIa>]޸$DJ&&"==N1vsǡm?NR v5aj En4fݺL7\xl9d=Fmh7AqOc [~5oP ,.?;u;YpiTlmѨE>>Qqn&TjNU ܉ȕ]Jɂ(Fͱj؀=!.]WƍBo⇥oܾf5$kWX^FsT A|ׯ$@D~޴zWAvmWʄrR>1J+b1]gD~{NHێ axA(%Wr1ިt|fZC>P/qγOjAw 8LX'p|󓣗'֮l+}sp.;H_׻Jyh^N(ݶnIJǖ<#θ&IK!灢Uj N{̢K¥:0_K9ɻ^@DQ,yE'iew8%[{$f'#A1T$ ;bܙ/ULc?,4o.ԧċq#}3 r( .{aR]GmaR`kz}ِ~PiGkcT%B$ .|_Pl0W"RY7kUm8;]GiOi4ƛn5P. g`ʐf:EʻUH_)!)9[D8K)]=+s<]W,WOY>s]^L jYTAwuv#PA@Ov C핦Oo:PTa:ja@Sc=c^0LԧZ_ de%gՕz.{E:߅r8mRƵmgXՋ3ƚ(7 6=:O DD~%=u:%8^p4ƮhuW64~[5o4L8_]~hoayݰZilW v;2A{ֆ2,00w6Y,g m2enۘEɬRw =H3!K@!ú%֍%}ͻ v^[H25.k4 d9] {mEt wgwFQ|Ulց+R&uO ]U؃wl_WK $aO+ U xsByFv9ō:tjSrPsOu-r+e},O8m!>bGr97*nįԏ!3>arxmlߌeQ w6% uxPT}«$1Ew^gQ V-tT8a7'R3%fɊ\F_MP#҆C8jOہ0C^|E7o*MZ7=@^>\9,!Ys{D0QE/*]a7-a32C Eߘǣ>_:r J"">:sJmH;aFЖ /HY1)s [v<E<-kG ,hR gW܍f dYot ,o4{ؚ+Ze $K8A<8:6Y\0li}\YRid-6۟nI 2 z f1hܭ"it..]iҔ$ՂBQNX%u0C#1Ȱ)]c[vh_RtipagL-?S\hVv1o8 SI_ W 0#{R= Ie-:T2"E2\TP"'j>7I)` 1C;A*s@<6yчY\r}ظYJhN_*%Ϗ C+ h+!h9i ם.NV -%n.8֤&>F.m&r,Gxh Bۏ!Xm\3ۖw B#e:V(&tR:f;u^A\kn$)a?2QQD/b#ZaMXߏ;[OFs!ԍ% os ވ=e/`|;åPiW`,QeFx xI3ŝu0/PT7Jc>\ ٹmTHMU~xBuʼs# *:>kb9brkAR,DuqD={{=W` e\wԄ!15h6E]~9W9%|ģl״$l^vr95Xd,ջyc:2=L{ܷ4aj P$2պlaBz~tl7E$tnl VCm\"QN@}=Q(R@ab!l(C[poi}~o-",~ՕZ,h=jľ!biM߶aPuF-S3]=>bR<$O`TYտ4dwIGk|GuNO{g$Gą\}r"k 22ЁpT$n۲xH؊SIASku^0s;ZFD^F)pg܊)mY'F{o"03\u>B̂S+u5^ιx 1e|C!T?_Rj6А ?݋@}e/-m)%24;JHe{Ik`ϾcKmf.=`~]ƗE;EA6?PVvt(cb`S &BtQ5?P4FCvt'7=;9$?1!_Δ$E>@k3`%-:Vp1/E,ibA٠B 0& ~RL$iX*Y4dʄlA71:ּjLpDD*ީA@_XI?_F 3k[5p\k8>M0LONLQ a}.ؾQ̖s_WLiOܬ^!KYC (=RYiZ|ٹtWP@dt_#⹳ \X^Oq4| w8f"FR _֬']e=U3xmg J.yABW$?]|Ul9i &4΢%Irـrn+Y#IOyNF6CR9M0ڝUZpC}I3G%6"R-$̓,?CqdfP6:]oG{y-+?\AC!07Ntf=m~No=hQ:; WxN(j1^;½cτWp ׻l5MU.ĉ%"/=FT㧚S۶bmkt-Зp5Kc''#~?$pힼ G3Өm wTǗ C Fdqu@nAWV* `dߛ1{T 9r?$=C3-`lf/q@s *gjtҒ =nJMq&\FodG]I4.!^b}l Jg+rܸ-TCٮVxYyp\ߢn{Y{T¸QИsf[ ekZW2I֍PB<4+"t J!a4Zc vNGA&bk k2kgS,rF~ekՎHD~1(i}5hFB)33veڿ[Q1\9qg*3/]3VnН}|j%nYJpЎ*_|뚞0,#9Pa8J؜2K/Jw:ܢ'&BF+ zp—^sI Mb[e]]aqZʫDb@ |n1s ɁzU]kr{c0C(4Gv|婢ϥ7k g^L/4Ej#Pt_Ku-K`IyzF+0ˌ `~-/>tpeW'bw1~VᔏO[y 9џշŤ3v-48l61<7H$6 Ko)`7Tsd4y`)L8aNqw ^v=#i|JQ/AUdI\\PΒ:Pa||qf DѶKM)L( l&(o!г$Jo$!x0b 3_|88}ԅu,FcM1l3 =651n`)mssieϝ֕Te((Ujzǩc}@Sg2Ň0H4&#!H|aҍ3Q$] V^W?Bg~o=uO8yԎ'z"#;J͍akK $vq"W2;nGWX}~s*JZ/0`qH yyQ_];Wr.rT8J ziꡎN}1kOs> ;K+M%Y$kcϰ8,貲FU,Ƅe+mgDtH=sALf*`Y*u E] W1L =Ao{,X%IũoAO4!l!~6dz`zs-6;8ڼGP? QJg_zKWyj&'h&Y9}(P2RIOFe&{Ʀrf<ޘl;5skˑO/NwLȚM='{ڸ "<M!b+I] اGhKZ'\-<sx60NP׹ fCDEj(^ޥ%=~(Qغz7juMQ\L> /-_W!{.f ޲ŋ'{;c?Ѝ)V?X\{ڐL#T Ո;[ ,QƶtF5K23ҙB` Ym:0Cpz_SgEK΀V7 ANN'ZZF3rޱ:gWTvL'אjTM3;k[H|@|uE[BPO#7M·x?=h8\wWcqܫeu9.Q( QLA&±θd(y+$ݦ~|,T㊇a d},X;vLվEZ ܲ;olOthτjj7m%٫/38u*eY] Yr`yZ KrY2o=Ms_lp~C8o l' ;\H9jS]*sx%{ H,3MS>Tl҉KV~hu2aPv_8Z\xNi>, ҹ'OzJE㨵ߗc'kߨqph=[H)1X|ހ2*=^GHVL/B "TLUcc$kG ۔*S̹ʅm 儼?v-dǛiMhR SP@ߠn:?-N"wyL^B#tfTf[yʍk=zP[u}{hՁ*B_Qn8iIgCr+36SfGd:የ4q?'jzUc}^;L"UfcҖr7Fj}(BRvGC--ӗyխoT64(FlVԙZO<|y;q.@lRSw?Oα3qpdy|[X#E`jZ2#KB'HW)iiz2 mJZ ,8~y6]A5=1k,7!jS}:5M.&n[Ա֗/l(CfХG䞘".C6Ȳ ךT^KŘ |mI&u]nna+J_{ל8t&e,T 9#yqbiO!Qz2Y8ttzh9(&25xCmx[_@eE" ]3Z|:5C_h=C76]7?@-"c@`dͳ>Y9I(6=F'ha:+lwVmmF$!`L 6Hl}_f 1@h<Ѧ_^pv,cHvMti i4cb쮨!%1w }Eq43Z3]ٛaĊ1"|b(JMp!?Aw8+"(ѣ@$fXS#x g=3(;ӽ=0o*2nrw7A;yD%c5mP83(WJNs6H:"v AVILF `曤Dl KF 0`{K@)ΈW]19Ɨfk?+RZ\L2ǛE0Q)1L{xeT"+B׏;asJ-mhBwX1A?ҺyB,nO&6)fvPN˱ͪ{[yk#qc2= :2Z=CCHA<ЩUe$C{g@ @q~X*k> sW v1eh r?Q ,f"}lA(nLk.+#8lڅte?6륶eⵓfktO^$Y-Ѵ^dH}~{hkbpS,c"X-2ɯ~'ԜA,@>7S䄓=BQ6Tcr;2w'5F$%ݞ5@Kg1,WmiXHa%ڐX6hR2ضwxco_9rb͂Os rV_܂4 sh;O{'5"OifTpAi"/R1ųIaF7_cW75EF$d9eӟ*J\I8[1D=V_=5-P]r^Ȫ u+pK]by>i/h%9WSbƷ:e0nyw=>CkhCܫT;"o0Xg,fz_;L1ZW|yꍙ\hEAMzT}VXI_@Mf3܋ f6*+ B׻rN`; ?F@+?DT 2*OQ[嬝|&?fd9>+4<ھmrb  /gGGTؚ&O9Z [kjo Yߪpj`tgTABU./$EIsG`ƝVx8(~ˏ6gV'R}5Q-sJgs1ߑ69LltHXFM!Ew\lMYuX@ؐ,oi`A>%SGbgCCkYăB|14V}}m#d}N!-ÏQueKjw.ь-\i16LBu=8jL{z߾u h3ȅE劥9`FFƑ>tc7TsW;"[s5zt_bs w6l#{ =#pq^چ͒; A,V7SE*?;Slbk埿 M4 Qs6Xthm$Hnm-.\DF$R5[7 ,>3k A9jw~-Z'ѩ>YmPr(VOqC[gq&.4X_ӭAPϠjWWr|}ڄ8x?3/fw=}7rρj|m IHy\,)ߡ땏ݜh[xL 2 nŸB( l,[kHBO4_=\/:.(r UG 8tcE6}c.yvHC]D&9 {,8tظ[!Q o9C*ϑ r# "2B8C"ۓq/إL3-1 >x]tJ4B݈e{'q.LޥtgZ)ƒZT6B-JJbH`p\esKqc)A .ء oi,n)vjmË-YJ?J=~7N3D$lُ:)!CTVr_,kT֍'qC,G7xSzLH*Ѯ}ohdn.AeX~)ւzd . ԏG_\FhgwujA8\g0EsuY9od|E\2 8'b/34$Ҩ] V\qm6@F:m-'d0kRܺm9H=N}ScB̎pmJ F$D&I ( ϏdvW+ "jчEf{z(xRQ6f^ l_^UDI}=! G+s8_{Pb h5DkJy ԀVPR7e{ޕ tIH8-6;_%Z̤'O5hZfɻOPQ2KBGj oȿO6n1V+u;N*|ٱi6tæLH &`ڸ\x)UO7F%$y|b>*?9>*zDF3bu؛!e <>`浖#Y! }pm_+7!uq`@LoрrJ ){L3r@*ڌv TPNd mB? Fnꏬ]{#&m\;7py*$Vz&iF@#"AW2>rUov;OB?&t#U iOsCH@ -&ěOTWUky2k$Cs?|̤&ӌ>15d2VжCųkQU#_%?H]룲e卫DgJ0͍k=LYEiR0]ߢWh,|U.$dQvd[@INRi"1(ABSg0=u*]G@gaOMgy)*fTWHoS.κ$f]d+{G}Zd@{}ne*5许+3ZL$Dxrm{cZfVǏvߋ}P[]o_ "?IZH7 Y6G=Mٌ¼x6Nŏ ȳ^pŒ?PP_t-T;/_vYF@f$`Xyɧ@ZMPkj&d^T]C9DnkҨ;sVm)G3Şk>ѩ_&DUϘ,eꄾ,ImCnك`&uFύt)?DCp0MэrH~X'8T)4Y uݝcRto~ ! G τ|J6E,e|5T6c6 _f32 cѹ-5b j ՗EΝg2NG΅KeQM!I:~?RVQNL!ÞB5c鼈eQPQ8 4yo{XD٭hhEonAjhh]S9smWtp-D!WUXy`TWF1o]WO6fK("[X| i'l9n,Ƀ^=6l\OŃ(D`S 8t[6sd ,SITQ^de- ~YRb並,98 ;)5;E8/݅#1vS?]~d]%u)W4^aX =P@{ͩC>=ab`-¬tȯ_1Y|>lq(~ʽf?>ֹw#K=`!Ö*Y2}TYw?N5){͛̚VI´4,q|/X‹>F0>4ᔯ0*wC/ dF]#,0t=ՖT\AFX翔}7 [I bB]G*+M&J uC _(?sqxFyϯB{DoёtPFwXOĞ*K}YN[}W4򞡮xNfRUD; _AhAmfa},]V0` [:GoZ¨Vɵ:Я6i @uV~KsJ608#n8ļ>(~^£Mc%mF_,sAR6t7x/lmB +cYpqWYO! WcS \U^"U;e食YE{4bHR bM&:QhiLA^4(B".@ne-:VRl1$ITrХ:~u_MT7^ m\"cU\MCj(yv1$칳 [U$i<Ͳ]y#esCCt|PDvLsM bX=صK_F!aC1 7"b>glad:#pfcaix?Vm5 =Csvs5}yEtD7b%ה*F|@z|lߴmrLrq|~a(&]2+Gc)USygc P9"Q$c0 |XN(9!Z!t*~Ol Xnټ/RBs8#i3rYN!@nMrߑd2\CѶ~ͺ?wqd2jttǰR( o>#̩KGpt[bƏ˶+ሟؐ *TtoFs]D*cK)|"Q .ʲd'G4ܦQ*ᨬsj^rEB򲌖وt p+8f¾QM]C&Cw3bKvOvqGpH-c:^ Vn@(@%D$#ףʖpm8th'\8{0C~5ZjʃRRHlPӊ;`s4V~qqVz~I ׍'#Nfu/L.YvihQޱ8uZS)O = im%ɽqr7prIRju@1YY"Gk+rzl[To?\ďI>nTElZ/_#}uWc2|ISP>Q&"!O GDo@zlG@cDdx@rOÈIЙw+T_J +rDžSK2n"?9BPfN2 Ow|X* ԼhwAPPYXˠRYݱ2,2I΂f($9g*FoW#!1WfEZRPߴZge.N3܍I~-?Omyhs/!)UT)#En PӣGvp#) C)&0#5Q6Ƈ}4 j%ԝA-@[-]d빉fՌ~tt<}4%DG9 fc* 8Ce6Pvo&Mol۷\:wtQmF38ҢT |k٥* 8,JS+`gt;[`Jȼ6wQ s,AN]|΍XrBS{1e ~m?ᾮNkYe t՝$ Z`Ky>>,6Dr+-AáijUu皵_uϑ'R dNY +z/Lyd7Lb0ЯEX8d8x(!`'B R?.h0g~B/g$dktjr/| Բ ̋_-^Cȕ6j?n®9=5:1kzڌ̙* 3GGǒPj@嬱hSU8TmbcB8.x?Xk/Y*[m |h2G* jZPAI<tdx.$#T,SbpQ_|sN>5h?7wΥޓ($XmV62xXg>}S׋s 3\Dž҆rG7Ó -_ tcwd?Uڃ!㪝_*0/cH_1yz< P~#bz &C3sºTZ#"&n@fz lenlJ·ov>N^5[/s5^И&q`~hHyr\ Fva} 8nc28l:郰Yl|~]1`)'+nsV=w2:5͙̚DĢAG#gkd++kV-4 H ܵ=A?Aq۬1Xd֋lk|+Yp'jg$%| Ak Ucg-oL#^q endstream endobj 566 0 obj << /Filter /FlateDecode /Length 23333 /Length1 39052 >> stream n2ehv"7O TjX%a܌  c1+3H`F{mi4pɭ: Y"!љtHC ]o?,A\ P7Ij-1m;pLf>0{~5Wj;qFj*"CEL*&7)AY8jDzTͶ2Dɖ*r0XJHF pC` 6ӂvgS~:ݺmVM}?\P%¬ zz>S;.`"،ʕn黊ޒJ\IjeqB0S1Taa^ض)`7ԣ&L>]ԒmU8)&dYmK{'▿qY;LZTۻ|LU.1!+͓!+x8_z?+"^Dxb;*b&yUI($;=juQ$1oҳzRNL |+X)<%#q޵g@wƯBe08=ąV-/+pGƝ`^Df) c =Y JE;9?NcLUoZ\u⚙P%& D[R[ƥ0渚u.E:_M Va{ܟ @Q>`N'E(r>`04@BGVFxM?>Tc ]=*AflR*3w%jϐ Tbh% ܤHX58GЮTӛZF?_\P0Bz=>*FvPjW uutjW]t7,7æ@{`f\/o%46=!H.jީYzag1cMgy_+Dk'ύ:Ѱ* B]jg&]KycJي6N\Rsٝb5RćAҰ6]_wH-Q.\2MKKݿĎ9l(/m77>p03o\jbvuշPO'W+沎BՃvtsȈwdv,ұ0F[ kd3_%ό/ל~N;40<G" !p`2Qn{c5djZSx.~_!䃒PUhIcL .n!2 In&vͳ`GY;<|hF."rC pK{YL"RJ!ze54;Vyh;|}p`ڟÿ$LKG'B) G%H*ֿ+uC.EhlQC-Ys5'%xVZv$3o){4#àS 0c8ɶ7nR*+`09$UC[E5fN`~+٤!b*Bp[n#IkWK@Pᓠ$(w헴AydE.?uU&qoRT5d@ ~ `gf~;=tϪs 4eWuU>vr(UQqFL*NY+b>ʕɂ&!5 >R5,oEEYC1#! ֽDz+e[)S%T?Q=m~6f.L;-7+\_I_zZ҆qKWEɛhXow˭p}/5OT{*TBN}">~eh O3J.\Npnu! oΚ{&8*{i2ot4'M6]{\ o234i^f_~)#Of.`Fsϝs-.;P)ROM/ץlf.Q4GK8oX0'LkHS`eQFrM>vSA~QV{Σ@o~ۚIk=) ~YR ؘk̰MBeI/ zuJ -βe mR$6VN”8*bv)Y>h-jgs>N+"w/L|8-Ht.Т31ڬpݑPf >U3|v[Ͱq ᛋ2}#B>۠q!Eir mm9 K ;Y_ kSr@p `>v3nO@PRaS/j.GI9x￾4+yù@r7wwM)ʷDL2/mSdU5j͗,Ċ/G"O2WO !R6cob%6^yXwu_zS!]Y(U0:CH)*6G 'oBru)r)g%#w;^Dc-Ğ 667#g]Yt̙;E莰TB{pm.0DI?6M*X C4cPL:!4t+g[Zf˟cy@HFhu&i :_'h~轠zG:5(M#RԒB=Y%n0%mwEE IP4vPT [:e:0qj0}&h$LΌ#̈`ŋ6v͟,@"UeG^2RCmixOH-vkv-"$Ѭ5fFAJ߀s9uW-de( ^K \V]?wqRRL0A~T_zK=0yz7tyep;I?f̜z<2ҙlQ zi9矒UF.N=vA*pI3r~aЃ`k$~WJ #<ݧH3p^ԇ\ NX(UξdJHXJ{Bq޿#?y2)b@V-!v1+ `T;,W$tq;$G&R񰪝&Xծ k{c.cvI9?Q''ZgiMRoj~1GU !z\}Q/MyH-V 5sTT9h 34 DWM2XB?(51ӶN Z$QU^#dec@C %lZv!M#@(S2]ҩWTü8?7c)aSDW`',!-,h\f gc'H/3-]au}YKG퍼RL/CMӠϸ*D|;i~߿=zJi{a`kZ$Q)gUdT|+Ԩ! (Qh=~]Ҩ4M /Y C5w](_vGzwЅΘ԰-34٭H5zsUouaW(u{nr4'^1EP'$bȀ{U_<¶Q>XC-rpa=9_9O%̄` )z6[ITj %bAꭡ˶2RVF ,`Oɇ?lȂMWq4+T̿H:ץz}4V#h(=}vtmBe&;7cN_gbvRbF*:J "QD*BTG^&i|?WS" T&iLZE]bu2yAUjI2$IӔ&ڝX{%E҅VTf) H2W 13 J5ʎe$"W A֘NlJw FJ`NQf X#;v[L']hS7--wi >+ⓧC,hW<׷"@8ڃxvu$GN컪8 9ڎ:o-O H^p4T6)bߒ- V0|c)8g0Oʭc)#Z:pqi0~'$E$D6y`Ñx"ߐxc+o7Cv[+\wz; o$&`Vx\;4Ik}@'tB=Z'efZ8be_-R7,F=j- V1 i=\7 Rw DͤQCm¢DU*OUVۙ_Op[7zE ֲ2'Dy]qAT:-?h|#h*(u#5|z ms%hl6_ţT@w3Dn4LeE{S ]s3Mގ5"4 4GuA\ '嘾^f8f*GɺGpZ֌eԬP}(BQ߆VoBPo#1ݤslA}5 Lt! cSĎ. vƩz,5ĭhJEHT- _u喹9!~&97KDΘ-:OFjXͅ"pޑGWS!nljT||Zo.|i;}jsS_8HmLO r33HI` EA>3ss(/yAKfQ Z`&m҄WJKZkBP !zD d_anA[1ȋܜ!w}(D04q14>88y`eloEH_!AꥼPGfwz' Nj׷= Rv[(ئ[-~yl2 CRv)YXG z"? $jf&Uv*lt0T[@Bh,)Dv3rJ!Ļ)ous%//'X^h"EE%QkDղ/KV}KX0 (LuA3ELŌָZ wS`#.~T'oa_!dJt[Zqyu2|ѯxѬޙ'Z t5aRb tBÐJ%=C2QE a M}Z:rR/ =KxCSoHD2|XTguzNG2]$ 2(-^T] sy_movy5x|`Mzw+@V4vo`p*\`IfVpm58/";]6Ӡ޼8;̻I4sN paZ*-ʂK}̱v9WYu//i3T}YJ,ic+'zݳ߿X _uv( -p7*%׈`LWj`Zɝd0sjhUCq:6tw{Y 'g٘9fja'EA᪕iԹ %P<=Bp[Qm9;;-b ʆ6rL{9!G¶);DQ] zNYDz :\lܦx)$t<׃#KH "]HTq=BW45\х d;>zXaxY>*ҽzn+QКū txjo2ιpB;?ա!!jd%D j|m^ton~6DSr(Ϟ]nsuKvKd0*W!yU_%C: z }V n>7uNeIJA]w/?a06&_h7_#G(8A۴f=iMf6 IhJ;ull3a^Oܙ2Pq݃&_31AVo7*v,Uk"T"sӢ \خǒo*'uT]֯tix{JtgOXkϚww:U[dsZvE]=X HwPYp4ws&e`akfs` ؓK~Oɋ/;?c?B@IZ džP5!澄ՔH혈[E38 چ#ݐ{ʼn]H|k,X(3mjځ7;>8eYWt"|ܫO4zFF{;fi0R5{(C#0J !7zSݩqu̎3bLM Fa,qiQ"ƒmHp`}`ePcK>VH 4C^KhG97fez&_ gD38?H^O6Jƍ%eAD5$Z 4(K32w>^\@R@ Q6BI4&8'Z=dMC!L{ ԁ0(921^AjOϬKq6cD>ܞc5$\]A^w3 渊t'UfAkiY~5VT7f:Li؊y!_<g򻅻+pAW]^Zo~Rb-G<+8P@atxl,ٹq%(7,Z-կaL]/B\k!Ԉ GzT80Av j7*b̊oPL 襓;ͮl.@1R aF:=>Ei!ޘyH#ẁfr2[xp6{1T#l.%OxL8.ϗ>:q co$_X]!(PzloN#<`ܗ4R(# )JMp^ ݥ9ф[ֲߞ@v233ΓGy~㛘%LbKXu8+%N|N1U/MkZ@T3ǥY U6Jz5p_OHcᯧXo*Zm\s)c9#+l#nYX2q(DMMp 4xY6Ø+sgG׼18խ(L2?a=iĞw`ۋGTeg*fQ眈"mK,#2%Q2$I!E2m36*4 ?00 $51$2wwhxu=n Yۊ( \>hsyNHCo1|q 2H4 ҊsWזְzHQzSOfW[/nQ~ف*!Tu=έRtς_EvHzi1!y!P#̎,TTh|ڥRn}:ic;,U 9qdqadRk}Lc$% ꏴܣp(lδ2m6xn"X[ocBAۿUi &3*lʗ?gJ LW&iV[mkt_U5*x+ycT=^KRxd?ӑ+ʾ=`e$,q"]^edʵߕf{u nlčz<;(("O p ꩆbBy͌JC}TdM+OOu7ۗz7V9VliXnMc.=Pm)#!"̹ aהN+ -_/o_/j̓wnL2 ucbv d ֌[P[; ^pw!&kXj~Ⱥ> )Fl c>8i]6]fPPcʷ'W&4űAp{VoT-* j'Xy c_Ұ:Vi 5ėVh<10iB }bϥgbvQS{Y1?ԶpY@nCnẍ$EEt%#QmD:1SCfwIgTsu`$t}P^pOEK@Kɋ\~֞K{.gW8p?){n;N iF0Ty(BŷʛJ?'LYXJYթ tdK+6Y?3$OZs SIp4P@Zr`K;w_I2Nf_tu6Jin2 z Q`9>t͗_ 0U̸MYpalJOᡱ/vBb37[tg=A 炀AuSެ,E޿!-A1hz0K]T5?~& $䚻x_)=~Лz T+`cF'}'({9:"y.gcXcN'`}D JUmZd;1gXNYRAճmFY>UZꖄ f .@jkuw* Q)4{%Mg ߕ=mnc3u5( 뛕&-&:62 !G7HcۘHDG#:ٳXr?_r;gn^LHP FukϪA1lUθPb4'}Oɚ2:wNHǯI)=4bHTՅxBa5&7n~BW{í{ћkē,|+^̬KԹ=9](檍. v;xiVXA(_ih˿Vfel'@`.Xeυm2뿘Zd Q< o1Ij٬TO]rqLL_ u&J4pj>CIwEAR<Q;Bjo7[&}(m|'Yu'TdTwa,#nGqZK{q%H|zә|'QlCn3A%|7s$bL歉B>dX*VR.Zt_,>R|n#p{f66x\T0ӗ [xf3b}Pexg0 떑R2Z=mlHP30/$W#' aO@ZUCC jeݒ!$ p2 Z4 i1렓ufYW&x$؟g`'BR=<ŀ;,zϲ+ Mq;(cN魨6fosBw0CPcomBg1f\i Ԅ zј8M-۠]N+ph 'DR w6eە̓3 bPQ\sUi&l'S}ӳHx<#?()Uco'g#Yzq(Ɛgp}:3@c Ԏ};ZapF‚+1™̓Gna[kxJ^sKT:ʿ׉INi`[?R&tN$ZW"Z 5ܻ6W|inIM3,mtM~bޣ$a1NW ] [ϛ t9 ~]:H] jJ]J#/ (x͘1a ISoGWM$G)q'8ik 4̔>ױ Q*o5!>6:%=Qq YⰆKTߨ;{MnKX^ժm x[m859QeQX0楑bؔ9t?Qy|i:ߗmuOQbU?{$৵Vv 塚kmɶJ ;lUu+`0v`nxzl3YVY!+OBrK?됊="װ j,/M: =mć6@@'}ûRs3.ik@ϰ+y א497\*x^&701FTo(QM80l+Gy*X4.K_""MV  (_3P\UlP 1sb)uLxVCNGz=p;9OK ߝu8[޷-&5lOP! gmz2zO&s,KCHD "};F]uw:D~IN5bXk ɤoGV|QdH<&VT[=*CkFo˶rHׇwvxx$wXB' P# * VVໝ͓YWP:'+P~6؎ 781W(=-7vJrz{WA_`T|eΖjZ}W.wޛxEDM1M=A_=iOZTs1 KK?lH`-A1#ԅ]r. 1=LLXw<)5x;CH*AhqJ+ ~5HGB8=blr 0 CNHtQ#Bm4߽mvA.>ܫIϭOn)'yyt[fZ`4k?c*LМ9Ko؛̦6cQX ۓM o /o:(_G7,izJ < _dҶ1k1.,2A}d1w?10 "o\j́^{}Z. :>k6"xG==J&'K'SoDiOͨW;NP(y+*@.U3o1xMCr4҈oSKb vTJxa-%ܠ*ñ_QLK:~(Rz2fsYAJa}?OE=$$H*ƞT<>֪p=u#I$3*tv7sݥO )d)SXل* Z||^{v0Q~ka`Q>/G9C Oc*>r-be׬4G߆P7RtYP[r_M-]`U.)QeA47e~FJ⥿Sx h1Y;_ZDov\g"#7 ,$Woӽ4UG\9Uo(8n_(w?DmЌ~r_HυM3JM`q?pipyehPX`YrᵘfoW?J_g0*YKG)I0ۜ#!~v/G&9Z[kqWY~0dɑ +xjW3a>ฮWlT H4А3hݦ̭Epu6&bYȊ&@;@ B_Ug2t'5t !!ѓ)OR$^495^ $y(lۿg` rx}wA$qĨ qnN>sXZ©>|_h`ʃ-iCD79 }~/]COx*jCKa~^ 8e U>b|x8W<)Iф\\Sڌ~uljqXl#N 䏨Vc&#鯕MˢV9^pr.Myf*QJl? 3CR#W1SwT`$ @A{:zu eKv(ؔmj`G5v3f-7jV\Q0^/b6OcQ>/oò/×2#7Δ{ u? B WHkC? {/1۟ )yOwPaq5ҊO" mc azDChC|Ic8,dp~h.^\aoa ï?j^!=Ff*oe0=S|zQX^>}Ayl/2)Xkj1H]Z|וp@M eG}q%Mt> x߫]`7!o`2z% a{V^E`.~gK{l}VݴhMK PZ[{AFalݍ{OkRySi}|sœbd/@dpfdόG+ZAãw ?E*(D$q5 _''X@Lf糑MۢF 7KO=C"=0UD- 3^R8Ԧ M'"Фvړ菋sjƸ0|v? 4ޗ\َjjcJ߄ O n58I٨W,2ԧp22YvYA@"ֶdF߆T!I9Rt yܭZ!s3bj4G>jU} D@ubCo {ֲ?4F"?A{EKS ^b*r*J/FAMTﻭF>gD&|J}j uk6 -:m$ fE: j7| b#G 'vׄs*Ɩlf3UUIZ:Ui*f:\! ׏7>L/Kʸ=NzIei$+8<Q{sҵ 4B Qs/.NN$W3*6L DtWXnM/eNǙ|~?x>ﰐz/a͇.ޚ(!(ݞ=M{ֻOS6s aJ]>lxTvTWy fJ~JܭF,QtQ<.INmϰ'ZJv%M;!`AeC p+٠ѺLxB)!ЯӚO5dÎ{[+:4)c J{ D~bHBeUc UNAM̓˄^ ˛"Bʧ?dCܔ[-l3M^&00؉6YmH6Oi6&6EJ*3,7t%<Tn+zGйhKq7lpyp퓼CX'jcO" 2 ^1r79&n4NSN.t"i:e5I50}7[syXϊPDyJn* =%=QtK[žsr}[)%)+Ȭ"(X(yɱBc.<C;'7_ȂҊvJMj-Wϟ› TEִz]溼vo=>|܈=j %K 0~U|7`g\m":Nf*n$w]XDq M[FvLPN08ߏ'28t91e_wL]¥')Ccu^Ȱ@ <;Fư3}.B]}#`v|j5 ٓ=KeЃ~ӹ4VL2}qGoF`0ޅkڴz{QK彊 te^_ь/Uo{cePZc7̂aLJy'6M,/imkJ }+ڹL9\2"~yVMnTpNĂ %Kf$[.xN[*mlmf{“qPO )l޵?~Pv6Kv{SScKHFN3p6I֞u%[* }!-xb Ԕ %fP@,)/`N*WGC{6˙CV 7sl FPI6>+C nU=`oؖE\)''mXfY!A1'jv ,.l%t4-L EݚѨxn|ܵ:BD^,\ԨmIlFFFjGX+,k{[ٳZ $Nmⅹ"NIPN{}p:2I+.g d.v\,EG⍒7lI<9I,l,2q(4God{N.$\^%qXEy [3f`+Ȟl~&IFP j(&2=D+~ʒ_2)^lrR]s%zǛZ,q-i{_K)x}BT:d+,]c>oB/APƼгjuFlߵCA$qi3^ ޳\1{hI(Pph^S.WHOe,K=elE}(F[N7_!J1-PA񂆄Єm6V5aBsŨevPt"97kp Ta[Q ChvExZ)cg0lIa{\pF:ɻI+lr{㔷Ki^?$6ZҼdXH(]\rqvzJC><3$5"RR0]sow{^:4#KU$/ a}aB%RlV~!9s?rXA5*dOS'P<ԁ7B gSAZsLAus`e9Y zl?F4>?t-yo`wc_3_,,J%@e]iI>c(0:c)X i֕.'5"xJ {jj"n\&^YL 7sй]oPRMyQPmR(Qӝ"|]kn,07`hݶ&́Μ:6IB cCK׏N.XFmDnؿo U\IY9sxUށ -lYä#QZG>}pp&>z?&G"e>R. g -rcc#l@9#?kʡ2d)ljl 8S j-;OW wRkfڲ:qSB6i0*1ܑXCJh̪O5 A|609k؜"K(9 ՜☖c˨`j1Un!e9ᲱF endstream endobj 567 0 obj << /Type /ExtGState /SA false /SM 0.02 /TR2 /Default >> endobj 568 0 obj << /N 3 /Alternate /DeviceRGB /Length 2572 /Filter /FlateDecode >> stream t{<˘#Sك1}eo_+K,nu1?lM\Ie]IdBDi鏎(6>Cq&&w\ ú՚SXA,9w\?C;]#nBߗ0)>߬e|fE?5#BkE$oRFa;YFGn)pYmv/Las¶kSE+Z5gs.>WsWլr$tDy}I+7Fk-'_÷xHgxZ8qy$ LBv{;%)/CMh[GɔdQ%ĶKW:{iG.[m/<)0H d-b1&ןD,k_-q"o#HIbAZ}%pBֈ*jc&*6 u62pJtR$T_z['l!ƹ0oG r+l:_jbuKf39KLK?Q] >*{76+^gj'uM BP^m ؾ,N J؂m-MHr Z#^U:bmRv4*Dm>@Q3i5ZoòE^Ꚅ  i12 h( k5q[Rrzdd<,hJ G,~6'lx7Pt v%6 {z/`'|9 wK|nyy7ʈ򃆢vZK^_ @s]"S_[>e_sXؕAo]76I;5S}z}E:sؖ6ԃX M;P_-x/_)Xs۫19֦/?Pc -b໑GtI1ݎn%O> ?$g`ŽjYӀwͿhj[/Ontn} 3ݍExz轗$7ַ 8 "jS]8ZdKSKzͶPa}>j~EƸ+ bqe5g(M{|<_3 [oKiK EU<j8 7RM~O-rY DFTyWʐBVV<\H%Fk%^&h-4tL2H[(uy^Xvu|Yyy63&i.hf Sk`,H$LYnnss@yLª5F2@ƪʢnpD=܋fRH# (zX;ڴڤd?OyG^ gxQ漏 aD疢EĨԦ.a'цͳFF+j %VwI9.%2X."š={ PN͑B bFx݌nHow}hAo:9xZmE |s,AK95iؠ]-^O/\WzEv!UR *Zke&I殫LXޥ{o󲛔ze;|jnΎbҲ0aLEK tWL [X|q+GeJ&| qQ j4}%ĩ&q-R"'eok a9i݊miVӈDe)ުc 2#e!CFA0 _"X1Z(!: K?;2EWDUv}7Mj|.f-׋ysLE"Gf7MPu}i|?l]v>uE`b˶@Ï£c6 ] S0nU@Z,[Tρp@bݾ7[ipI^k"P%s]]x{FU:娧7OKR/yX:J*T 6wU&r,Dj=ymΚRBoGdGg_, +8:&Z_RI+1uCՉ̛CGـ4*JyiTDTٗu,7A73Qw1=IaQ?u0?~A[FԵ* bTBP1|%&f$]ߘ򌭿Md<YHCv7%lsW8EAQAM5;Mpk5mnڰ8+I@Wv߶Ԥ_m&P'P]3.y]ӤlwCSC7`Y`G_2WT,ވ;kt!=H7[>Wp8:vu!zMUFINeClAs~ VUV\jeɺM&rRZSCU]rkQr~Ş{p9n馓( >Y5b`Ϻ @˛Y׬m/jܷi=Lm endstream endobj 569 0 obj << /Filter /FlateDecode /Length 13147 /Length1 21060 >> stream }ɽ<|\G{#PGF>FtIMG/И9CT{AU%^0 */ V8HMfzoSwaaKAʾo&)@F#a$iT]Li*r$Ouxgn/iAx~6{V0' hEw Pg ۖ$ΖKh[Gr [ƣcef0)l|BUŧkyRY1\ Bci\C>&my99CwZgܶmvJӲS-|nY6`*Do65SHDCZeպ/sIJv$ޏa^- #_ovG {pz *ĕ|5OΫmq,`v]R2j `-G=5yKǬߚ~gyM{^ic/N^ TkB𜍘SυZ~1w YM QeVjpnjjmL0ՒRkD,8??UD)viQ+~R@.l0 hh.1Ϋr_YlFHbҨ|7Ր*cU(ھZ٣-M+Bn̝eX4y`HnxW%KSzsg"FM 2Pb ۑ4ws}FL>5e^Fwl`%>бnWFM쵮CRt PO%>ɭ}(c&E/]9!#eAw\T-*5"gKmzЮ~2)^.N~|_$q{Fzj_Upp JˮMX.5oEVf:&7+Uo^Q%p d}@|c5ިdXRu j|U:W?$,1HeAalD?4iX'D t:p#$R `I)J/u]f?Xpo ւ`~NőF+vpP 's!ou^DSQy3twzt\`X n}*J˼+wؖ S/Uo3^1<ܤ¼dw7$`%Ae+ޭDLpjmt T3YfjBSے+\勉A馧/z{xv)A86wqS#; sj=r QDPѥ|r$ Ěϖ,y# Vfՠ6g'<xTwYw|rDɥ@՛ܚ6 4eX3\M6bQ/1s'ue Jwmq/̻}I(>Bv9H'i$ ;C.PF)s^fn_ F>F|rk74S :o3ϿA/<]e}V╗cAûGU~#Js &}$^W7+eӓ!9Gc̦2c,IQ$UHs<ݫߨĻ3ho4I1#Psٝie6T8^!; 1͙5 @NBNnQ+(-FYw6׮謥#G7 hĉ},E?J!5óIC倵n&]|QbZ!RhnU'/ {|NTIZ-]@ 뤦O@i{9ҟ_+fv)D:w~ HqG ߐC6e(ӻF1*_N gCsx CAy+Ր92ל-N&#?q ō씕z\zx@5ٿh߈,'$.q篋B';S@h1SKn"ز]˨S^] G@VֿRv19k Bqb=dYeJ-.jDJOHuȆt9vւ $ h6{5ǫ^* q46(7"،-7c>2(s3D..$$WaEI ){9Whxg9p$FËT\xhxFbC #Zu*Ȱ57(Pf6%^*FިVᙄ׼OT 2}.k%Z˵;N_79O-.`􄛸5׍6v?B+v T?ԫeywẦӴ~SJaL̾\:iz;8iMܣgTWgq[1PP%j} &Zdmæs_)W<4*#*,oeZJ+! 8߸Rnvkr9g.) ޼,;$2DtrAQ*vjaMj)5_g򒃭hB5H+/,V +r@(GּU=;"@P4-`Hɷ Щ3_Wb%(UY} 88u;# ܶ:Ƚܪ?_2T @N{#(9.ÎyjQ3rPp*> J;g:q9sI'7^k0nί ;ơ.AD +:@Xl^*c Z 9z_-]Y VC콜̥Y/P+g)4z{b8Y e'߬Vzڏ).[HOT3HX h6c/ˆ#@/1Alfd( 3M!.' syݔ@8n.l9 _.FJ ~.BKRF׽̯>3)kǵ8զka k5isyP;a+Ug{R>)(M`&XR lws jQ@Er'H}q_se2A/^Ou$)M]$a(J]lBv4iSTF)_+lS{ I /j˯YY.~CZ0wtƄwz蜼HF1K\#yOvei69jTCsY|cR8˓7 hHTF"52ߛPo0l ƄR [րCΪVJͫjl+Dד[kTȊe:[W9^nÄj|BeJÿcO*J'7WF|Ʊ{vئM?1vsuCWn.+{TeCBw+@1MM4N9~U%˔4 G@ 0Ip-՞F> !vѹFD@}}*7!'Ќ(]_be7ʩzeׅ]*i;z 2pruXr PlЃh B .O;e7w}i)hI4RjrP i& gR#g<*vm%ܩi7[ ?NW3,jef_K&\ꑋGDj hOkuYI#ݑ*Ox䦺U.&F?_߱}7t6(ΫxҮ=f+xViso^8)ú 4LZˢLA58pc8=7 cR4sS:gGo#:V> 5,&̩qir}/`vfqŻI=\ϜtBFo+ө`J>D!wu:=`&VG,W``dlK2T8@| .tV6bkU4D꣪#SRl^lE?MoHG2%#4N-D1(6Z  ȑt*P uI@_ :d9dDڋ\ P F33DMo0eNVMF)f(nto$%p/XΜ5mA8cBMwFsɵ}b (p&4s:E /Ԛ|Y5R)Vfb"oQ&SP7TqQ`9u16󹯞rj.ssrv D][OJ2/Ū:е GOn&7Ct=S{_ޖ5oqŊK<]aI3ͻ8q`׫0{<ɩar Xj%V!|^y s=DCPuYpЈ!vC^m>&' v>(-:y11E̳壛Z6,{s&c^۳Z>VO_";Frwf-cB| B[̎*q񄧬¥3Exԥ.&ZߙA-V$ߴA~ 1N߇ cWŝe%1i2] ɂG?%KVH}CR/+gޱ97@ Ur;"9Ԕٙ ``LeTA^Fn] J<ÝRj]'bel&vD5aG\R/?CbȰ9Yn !W SK-In#Ye,+T>>v kX|nFn|˜׹M&1r9.g1 u~yX=z0|-ɓj>Iznwg4Lkk B  f rIv<Ǵlm1p&>Jr#qA`3vJal/-X&W~&׵`FRC1H âiԗz|,'->HKA=Est1BY84OH+<{!U.w;(7&X"9 gBκFvyiݘ)?a@Igcd\ѓrGN3c3&t0|?7+݉G& jSl9O5hKe?czsZ-`/4"1%@bk8+ytw34zy=uѹK 4DjXNN?`>|tOHWn MIHc];ؔ T]WYSJdCYMȢqrBQT~DAfA+Y*m/5% 8ZFNb)ݡpˏ ׯG!~F7LB@d[&2$TH-o|šsR\N%WSK[Y 6?4JnG(oV}%[!M9z`z T2\b4èjAlWt"O S?p\,8C4EՉ$} #گ"էg,l 2,gRrU{&>ELccPh͓y Ѷˮ!Rv2}O:eHg3Db(T.~A?MFv1FN|p! gK-07zV^K\ @xᕚ vvw\Z[ 4dlP>OXZX:)t%cKn(Bu;Oʩ7H]gKG0FmoU'w%G&3śggt]fczR#' !౩*AT%#U}ߋs&\+H?v'Q@Tu%z/%b&NC2kΑ ln p)\t ve>b5){|g/dqלC0RX%QRF5 8ݲtf;أ.2`1uF)YJ9[ gFF߅䬓/tFnY\{Si"Z2E^g+O`$Q ({ap݅jzhњsqLQ%|&|e;NޗMWUF VPsVtGꈨ|E겙oV縛,zm܃&.cX @'P=U( DfjdX9,hY,]qF:BlPǥ-rSʩ;I|~!YÑ fo+0~kqI~EW&Ֆ$ҙ@|_?Ff:t ܅\8U 7 !I\nט;H#a'dRƷNKc)[-$S'd3~y{nQM6Y?Pr"==ޕ;MfN(F@ĭ}Y'+Ne_Z&-yb,$;_N9]u3;Gu3X⮎#G^L?I Aiw,02M@[(Ue.ov:-,бi",#ET^L4n񊦿tl=\ZNBt"XЗ[jZ6E%bRp$Kl:! EB>0e=<MPT҈3&g2-nr1N+z?jjә,w1loQ/#= ̭<'[|6QB*N+N3 _s:_0tf~)hqfc3 R?LY39k%C'P )G쯙k$:Ioj&ٱ4z 7B݉]]#VMfcthx1OJR\ u pt̮.mS5w+ˈگ6;~ƧċtqjwHCWQF/Y{aã؞Lmÿ\zQqEC+P;1IbJQܤec3 ŵ>U[-pƓ+\eD_l;U4#SYPA1x*i•"ܤWxJ"£]HȈù nVl@FHd t8f"O\&[,s^5ڿjoޔcVѽh@Ws,sl>ė՘h$+n=~m*8DٙL+i hRg̾X OgXw/S2ymE6t"z-zz G[Փ5ںSD+-;oDg D+~n C-M'1z Hv3sLM6Wqii;oF7ϭڼ>ØU-,B~)f[PA,v9]dBf`kS6'I>!*eg8 ]>|"XAmOukY@Q"字%AdPvNj~:/(Ӆ\/kFPj]90HD p3:bK9?D5?尮&ˁ8ik,hn+-oa*+ҟ@#62%bKp|FkYbPcJ1T1K"!/d*`Lx)StK-v֌^Jmn]%=6p.f4!dyYE ĮˀVa,)tV\8U 8]xWaވe]KWjS" FX9QKgeʝ+%I@䡧RE-c.%9 J1ћQ["E! 'cR56"1I7t;-TW9LhTӛVHU yle8Q )@+dA2n{޴mKͭ'W?~ E ]&1'ŅL/jצ#cY)Dz/6 H&t2-RÊi%iyB,_baZCZ=Va%;&./%͆9tjY@? \Lbmz 쩢0g-#ê+pdMM*ܨ$o(4` s[u, Ϡ(bHWlhAJHP#~rsm_xƎmhS =#' }y/?s~>q&8"B4u0@((ٔ^0񋣽Irgc9L"~m0ǔ:GY7-[Ǯ[J6@cf5W~ +#`B3-7X˥pyОJ.ҸE#uEK<3臏M!"6H]N\ӡ}p V*CtY! ݳǁ)W5 _|!Hv]<:՝?eӼa3A9 |F85mI,EjawLYD;c%Um[vB&(ida":=ݲ*$2و 5͏8^p$\!;i!c-͙b]k r_-SK7uO{ OY:,zo xj8nC9'y@>C2laBQV!xt<-ͣlyΏJSWNI" *cu>5MG!NbXԊ\NK@]cC|_* +%P>Ry,8rj JL"MC)^SCCyF3cy%О"}UOu ? >n`$6X^Eu%:'V=2t6ܵ)p%m)cr S}3p bA6&чm۬L:T=oD6n+w9Mٵ51Ԓ4@QJ#~ɓ臔}g<Jb:Ca3t4fz)yG`Ɍ f>W3I>kK~yB͞}_ɭ\dNLh8U',(fzy1@0ˆ]@RL[~#`ؓZ/Me}dCWԬ΀ ns[R7zy6m*Ljmb>p\rGꅡFY4={ 8{U/xA}%Q/V un1^]ʪؒa~Ѭk Os@0u 0nq gAm"غ^2M t(TefzeO/'ݒɞ/Lx'MhX;" ZTx'*Sd> stream !<5mqɵJO1sH|Q3&Q˯-ED559~f,X5ӛ $ml~5c\D eTM3gt#R`F9w.?&<ؘVD);? };Bs%F0zԸb-K1.8Bf~g_3>=Jchzo"=phM+?HIb5דN.ݔJ.7 X2Z.rtwd/ M`!D]Cu)O)TѬB~ʱcwdXՙ ]G=+Рޚ È~ߧMRJ- JTT汙V4`UǯyuwhBFE,s_q,|WPH1p_3[/*Z:#ı'c_&'A4&wgT;g "TdIC)JX2$ R.:9So.o/{Kqi`sWFH4B\lY'1CE,~15Is5q1B@t*̄)&2TMҺrWgG^o[sEI'~TΦ?;厺H* ]ϜW)֧GCfˍREsQߣR ZI3"%L[P9<i k%߾RG4H&Ҡ)x힙T8MSM2;{Q/qM*lrzq$Ei@3ԳRG"RD':T_u ` +պ UKw3jjfO0fdXTnG$kMTH( R5dY/!b4apjhvQ-E瘱# ec Z| V~ĜbuӼ2r7YBv|Gp"Hh&1o+5VJ#|ibvm'ANbL9}Űr? Y\C t0xŢAq{v]b\{%5lY>vl8UrS|wT`^RAt/&h 6֎S6Pʠe)ӳ_3y ѣKώ5He~l3lezDea:( CƋ([5@A٥r‰ǘo_(/FPɹϺaYd`DBJOAڧX(T eQ&IhS3'ZE*Z(wC3I0@3J);ZTdZHF &/IwYtWi{|eWbhk*G3lpw| R* ;S)T[UE/\>YG *kZHmw]3qiʎ|Vä/j|+G6o~pшe߲Kex>A׼*fj|^Z'$ ''8P6A뽆K\ !LV-#sBrr,"GX#碇n=2 sL7ө9PP=g8E?DWWLXjk2yگkRkɫlqu^wh\V,Zc-۸{z5%'K,j…ʨ:;SWiDi̥hl|qsт0BW\YE0zH$;wu0"utP M>f y).{VfUj&md^'T6ߚ߁%H{f %͝&L ݬncK@'Fm'H8JU~as8쭈u>yxG zǨ+jUB |M}D$yL5 xn&[*[]KhLaؼoѬd>5}%|B,v Sw&t ʦiml7[dv!+borcBƑҍTOCfJJ![SR&i BԌma{1d{J!VǏ]S.qz6#ZY,8!KoG:sSoSVfPԅ5AvݑF?/PDmW$u¸e~bx 7 R(u:τ7+-Λ̅u`@cc0 p|~{@mԀ@Nn~bl^d3;.}py{1@^;s0ZCe\ŠE?T[֙yCȘ=Pd4 Z7N7`h@`W?Nѽםds3™; $B5lAv? - hjQƦ࿩ űIl]|7s ź%IQ 2JRo-M0Hˤc8V5LOu>`"~@ǴS 2`sNgWTL%|bT%o ˻0w޺\R{<,2V',W WVǑZ4}0yhF%v 0g;_hzZK9'˦x!:l[/a}}ǔ; ;Q4TԆЋyLEsNB$xw \zdm.\o>pS,De4=7΃~͇ 7辞f~]a?` ›J.?O#.vǃ 9.^%1E8 PZ7=m1xcGܼuo_^g'SiT$2xix0Ǣ?ha."C@!󧟭+r~x 7}]{/I% 6iD@ D\C8·6P_.-:V⑦Ԑѫ6B^>s%">KXRT L )Sv茏1LVZ+SG;ZsBCa&[ e!Չauf̮j0ʖٽV@!9#EH0ٙG{<$o -9翉f'\Y*et;Ba{pVeFJ$uvjnkLUineR=6:3\;iraMy[A؛vH܀ 3- 1d7>Ǭ[ҡ:vÅ1zYpNjl_vt`\]Og%,S0p̠g,Rdnx`ZͿC:ʴP5ӗI_x(hRKVHګ+ Y+y]{o56W)/-GWs(ff X>a&kb'{wCXEϪN 3U.DW A]Kη)9Ρ`sWgx\-XßK Gnدidūz}>QXDyy*2'>gw;L~)׋*r&G@Ewv8Կ#! `>Dr-,_H'YOIW G0_.qt{S/% c~w<͆gQNaD[[ _>\[}F3.+5$U d6" ]Y7j8zfʭm$t1or[ox(i3Zg${*bfhQl9. , '#GhPwRޠ 3=! =-A)1K 6fNDL` {0#(x; +k~n6U X  _cl& yE6KY~<{T!݄Q),:e8tf\ޭ_b 2BW[zӧFV<1[;2;N-2.',UY/&c{j\Im Wm܉Ji̛W^1͒=,ޡXi>S'Q!`};>Y(\9Iz_!F~'@iޚo:;M5^19äme.U?`|E)SW v.6i.1Gs&Wx:K?974;24?b4؋]@8.qxpw/. ˼C1sE,?2|&_/xU&NCvHYFE"  /K[\A9]ms(<~Zf:J-FҲ _V+mh}'z*;s89qDK ~K`e. O))GlĄ<\wE&^$Ҭ=GB0@iGLD:UxwcYfP }ZWu9r|}8R[' ݮ/TrޓF[ 3,ұ<#nyװs,JY ;I$;Eneʉo!l4.a/3_TTMÖ(wI/ocyɏ1xmi,uVHN7T+mО}ؒnD fU)DNL."ėʐ Ù[bͤm1y ঺Ѣg<{nRdЁy "v-`Jr7`UL*C/,hPWz?{o.hql7}.ҡ5I2XahAEULtVWVPR s2"G9ʀJ&нUdBYY%{{lrZo&J$M;fb[! ]< 4ڞa{Sb> PV,Ԗ&fRLB t3pmo(R$;ƧOALWt_Ӓ~z@4ƘR\`]*IHVuG2_zVCCq jhx#pS82<[lR^_Kֲ!Yki9`gFs#A/GC$hU2c٥#ܧ,&FQm>Ze L|>yv"Lg1uʤMNiWpN}'(#${:s&{nze @yw+p _da:.H5oy1֐ZPAua&>v*3W_7LȄ8&c= !ŋ0Ae8n8Ғd&7Le[7}|"r^dH[k5ShSys}!XA ]?w'U@Y\&2qe=<{)c6^s"&h? m:P ,3W6,LWllK;WzI)RsTPİ?g5R`ύ'iIʑޏ 9ɇ+ҀYm&sa0A'Z#@a1DwccH]zS3hC6EXn=\&@Povo[g^\E* x1ǻz[gI 3f Ɣ"Ubbj}h4Z湘l Juuu-\p&i`('eY9p+7^c?_YD7hO Elg %#yB"g޷6(Ce)2^|Uaz(7M Umekf#Jh0K~wmDqA0>f(gm'-!rO-qLilN!LYzep:qG}/Gғ U]IJ[U nU)Jg}% vVQ$03uӜ`}Ñ }LEtEb& \31 ,b;d-7f`rx=<"-+ Svy&mޞP/bȵɨ V+'e|@scjՀ;H/{~퀳&1iC>d-$٠0h|U8s^%/uiSwϓ kG6:":lNJT6>lc FbzԄ[wXoi z,QGmw?LnM'jm.B}\ƌie185$#uc9 w,jC%}2A@ dpLWKTd*㴩3f Od#|I}ox:mnHF]9î?@ Q.>d̫';EW9MK5@r" gBt5-ȳSվGk f*[ET}XN) &#M{AaS/!}~…@pOcWg9JGCt <~u"07| .d"* u~@bĄ)ibgn;a$1 {smg,1~tGHWoQ|08@Ӧ~4wۨyw$]n"l^{ W|,!!8.@@,MU{N2'0K̀D\arG.A-.ri i'eh2[M9npB/[@y {c*@e^A} })q^W Rt|:N- ;MXdaA[;.t .,&PBnT}z76q EnҔO1_B +%:o eA 4_N s\)>*L? ŗ`CƠi;l`׍Ez#9Yd G6EVߏ0FDf۩cY!s^u5||:yI.5ueN2\(eՃaȽ4`'( ay:Dⶀ a䢃pf\c6u/NB%4 &%:NH0R&cѰbmЎwZf'S6p*<5a=k&x:O|(8:3k 2;PROyvO[=t ש`]SdyjdkߊE+AAQ 60^+G]AKN(9-Z0)>RŪ8= x\ͥk59C|s^ƃ)NFzT#uþ*Wyr'YY-E4HوF;oPDזHyQH0/OU%xMJܯ!) /~Μ]ݔ&o4=}(t {>BcJ8jZU+̛؜D \R#f(r #M'+.@t8o!MxG @F;y?zg;R-a ;0.9NtX3-5} Blu%םS@F/}K2#8a($[wyN%ef`?"mzmNcwub#A:m<Ύ)G៭)@_[4~Tn[3U3}jBȊw. ;c)v;\E\өѬQɩhS} [/< {yh<{dZ}KQ1&c+LwKBC:P@NN+0M߼}~~\ \NP̍$JK->dexc%Vvb~mt)J7-]nQ..+&`ּQk(%^梜v17Ɣ01TH~~|eey>~0x6]reH75rF8uQ1=B\÷(ԃD( F7"^&Q\SiͥjO}$麱a?ZT, Uf}<9^˲Wn@׮m\%Xnx]PSk %w63s:Čk8> ˕|SPM!+W[VJTF{,Yߟ P lф#,.δMV+H.[W OL~"ߝKk? zq$'-?IT4|Tz)8@ILC%3gq (uH45oBĘҺxQeQ]E >9$oʧRO 4ʞ07 .*,VEQYB/0$ l)kb1tLp1`(gz|ǯD`fh6sSv`=X *^VXjNz;pHWTX CTx](9ܩ ]Va3h)pTQۢj +W#'9]2|ȓ8U'bnQ9܌б fnh)>y^$TQ!ZB03IBaj E)്B ~bkC")h](FI/*8Q%-,֏g-着,uq%ğ֚{u{~|@4}N I;./"$M}.&Mj;+Ӓ.Dz3?z8MC_οS;01lj31?XkGͱ 1BDeٮ|Vr =Oi||lxv-ОDЀqzbmbjMyp\jo;JgUs^_EA_dC5{2ߜd02.Zq;BJ]B:GHC!b$^_Ӎ}|ʣ$wZ݌3@#+$of T2z2pƎNV6ɐpF00j:[$ Y}ŧ`lJz$π< COA~!3;cxg|J ]t4+z5="&5J m@m}3^Cr7- 6YLI&; u{vikKTx:1^ r˦Uk-dr"ղ5%B=Ae ł`}~id@-qbZ2WuiXFl#SgzR NioR"3eb`JH"{mm=-p+\7_11z WK#G,:+$A Em@(eۭbJ;)C {}4.\XWssY )ΆH m8$·X VEJgrZ;21xfͰh(R:ee$ jl _ t J]ŭkCQ!ׯȦ) *bzR*8j2*P~8"#ְj.Jf+i7"BuX!Q <>y"ނN A:$mb&Yyn~n q=+g<-muD_kflx?Ne6bZ7BW|7'e?\= Ahޙ*-uIuf1GL[^~%&L'@-@}@2t׌iV`ᖃEk!9*i*ZyZt;l;a1A.Y5E"9MyӿJ|r=5dŢTJ-4%X;RgX.weܪeݼ{t>G|&\/nT>R*?qb͌2R1/T DRauMɨyI\ jĺ7۟7st%舜kv~VG#@3@7x9fGit X Mc%l ̳Y + YH6fZV~ m\),dF[UYS64QHjȄHGHw G`bU>L!$V_s#C!أ%5T `o`'+2AG;r Cuz=ɢ tq[=)Vu?%h?3iD]1;LШ;pXmH> D# .x4?JfQxT}0U 7j|m8&FK[!E:0]; 08pk*f";q_4X@t8^xY̼G9& |u*ݥ%tK?!fN[lXd,k/-`SZ/$xcXUNsα]oLӼQXbAhT(d'9QOI^=aӤA: Pw^ٝQ?%{C B(X~41KVUiPj 25G F"0سi2eVBX}7b=.'fVfgO2l~2jQcօ=vZFّЭ94!lJ$n40>I evչӯX r9joJ^QC%ES5Tl !d@V[&51Ju osqKTDٲn^F*>l}7BL": av3P6{tE2`BF-mM5Е@ٹƐPQ *߾8ʓ> ^}p0⚵}&6'큅Y~ fj/ Y&'qߴl<zwK2T#`#ZǔSA7k ["쭫VlXwN R 'DH i?s cArH9 `qJFٺ{XYPطLmn>asWv~Fj6lT6/YauI# 2qRhv\u8b,6(WyxRrӜ+~v+Sڊp V IJcl~Pt(l&c7Nן9ԔM`-+K*P08Ui^v9!gb7Do#r2}>B68AXa!!-ͻ"`kCqvnj[YiS^ybB")OO0Efĥ' KIW8]Y=.+Zhr GǴM(I3Y+%;¾Su~L K"C "8}[`Hvp]ŦXXXFr_Sq+7E&olxD;4:P8_6|@#KYb\uq!GX<g%l|%w/"ނ`9`%kMUx5tn;x0Nyΐ8cwaf#x-{fwWX7b%΀›&+;]5fES*".1zǢv`];P)@/ŗ@?~9QW`F'5^_yf^o>e6艫j?`y{Y?45=TGBݬ0+FQs~9Kz" Xk`u@dMQ/|c b O3,1R%^42#\}gR0s|6u U {Až6sUrJ7O,`~r=ajq8{ ekZ\}-eYh&qDE >'"`bB^R% '{sT۟wըRQƯfBY s[V5Ż[35-S& yj(~kJ#F:D;0nDx* d?6[-?qyǡ2n؆OT|L9,Աo$ V o@ zTμ Hl#6([Q-mFPxȂd'cv4y=J xV¾q;vLy(|*Mفb|x ؏EMrx=ʰR9|twp3甤5`%@V[wQ9 2z2 PڤC.ԬDfe.Qf)/hƾ6?yxB9qJ>ǣ{Ĉ ?ع-*~ȬoykyiY;o@LUSȻG3O#o={@>oD }g1b ܇l/[um\}[P-8lci趦X.Dׁڴf^̺~m~\عs7j~9 *fjqLj^Qv8MllQOaK=>4JmM1Z+\ܹ7kkc gI,:޽(l+SŹHb GnUDE$cGʐG!Je&%@o ;)ix|e}6?yՖ6,zo;%^m|b }bR@ӜH$}^)I=14Q^.fn'}(c~3R@ERy̠ȋw911 FfQ<);Qj|2ԣ`JeH2pL $=Fy#6C1 ufi.!JMK:F*XE1\ 1;/q<O`ezhZxBR0AS[ .ޜ?ɱLk;"\Ց67Ŕ7']1>^ aicbF-v5"U*8k2lÆ:E9fr =6=74̺~&pp<罡Hiog`UPa=+#꽰Br%Y.Dpg]Pپ}ٹ/r__ItKL##XҮB3| no`.×i`@)lfpHZ+"R6>!2^0fF7ր?;1z$ȃ|רWCnm`e<j>ps>+ƤM(l+"F0X{6;]]EĀaA/ĮAk3^nOIC,h sl&:@xX()xQˠ+&J6D%Ǻ*gcN:,+bdT}uޭ(&:Vv@`9e|>v] {RpIdZ$TQsK'뢡KfakpCz!s@xSMJJ+wf<'Wݰ#sԸgNeWF>7)d > ˸}?]| ! (%LITz5pi堐:{{%2-\&C +G$&T ]2pe@tpB)SC|NILG;rXNG|^)Mxf;jF7RdW~ZvK !1^qO4ϢoCKW9ml15hνgtdz8u67f+$YxiIeh6)1^s63B&TQU?NFN":ė{..u$æ,Ah{qmf(@޿TqL+G/* dCUӱoob@CÏDW7nQ:G8J`24GGpF(h|W!vѯCw 6ez"@$O1ѸG}?Xl>HO+-8 kJ,2o*2 .&;MH_|9te n@XVr6CZg3hlPSW . h8]7]y&ELn0\r,g].&`Akw4i @q %"x!M_?m]_Y'ԾQG^V9/ Jz)0d4>W߿y:ջ׋gYAmrZN{b$(D0xo+'g63=V&}wtzJ?|kWB^]LS619_gQqQ:?[hey˛PjtQʍ!w ?,+ { &8K&_ HG*Sd:Hw*A:jF,rVƳE6]I1XѠA<Ҕ9t^:zRYUC| Ȥp̰^ҥxj/$;`34okI TP #"|nE ػ,r U6&(JyEk~R_׬5N(DOLA":1tnBf{lA MNϑkCkam)2w[}!wg:Ԁ T>oVlpUkٱlmrĘǵo#B`彅ʫI B8 %KOΪR+CĄD R36\ӼmFcؾkޞ Y2zL%(ڄ}YA-3/WJ<Qץ6}8|׋Okb\cN;Wn6;i4݂< 4< _۵Jĺ,i𰃮=M5% %`;_ endstream endobj 571 0 obj << /Filter /FlateDecode /Length 564 0 R >> stream Z»A:ۆ0]*s]^~V 8af)AD+f9rE-XZ UϝQ;+G^Kmᶄ'0 Ӓ*, lVF,O S,InI2ROs#hRݹ@D9p$ȴi:(Ap7)XӄʹP;t ,_kp@;6Wglg@(Tlvs w%!S JuKE̖K!/:+@t,dO1:;VkxA|?V(>|M'jT?S+C;c8dQQz=y^&BsRm.͘aa9%<9q<,m^ Ic,N;*d3~CT-6^2 1} 1wvlŲ+R4}NCH@"y>{vҋdZt/ҚՒm^+w ڣY]t( h]vq.nE$Xo'Vq|lUE"|xs+ iJfK i"?Gb?R:Ұ! s'>8T{cXj&ma|UKVZgVi4t(vKi׈A#wiZ+5Mcͪ}ădhH\Llt<٤jeh+ Zdr4+p0< +2'6,F$Ë1&O&]3ϊ30vu> endobj 2 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 3 0 obj << /Length 469 /Filter /FlateDecode >> stream N5nb,MÀ-9: is'oszI?G)Bq=Xvs~,/xMkM*x~Am5LtbAjz=78(j"ƹ! \iw8{3?)̐(41뺖ˣRlKS$mtg$i|vaWa@q!q]A vJ;2Nm8=?ӟn<*?^MBt٦^X1gXEb< \:5nKPjڴbs50Zx*\3uGaY.ơj ?䎣z!zwExIr mȺup+[`5AƠy"*ݓ1fWw8!:wzB3О  VZjV1MkC/Vm@DU>9B9 endstream endobj 4 0 obj << /Type /Page /Parent 526 0 R /Resources 59 0 R /Contents 60 0 R /Annots [ 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R ] /Thumb 383 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 5 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 88 677 541 689 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 6 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 665 541 677 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 7 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 654 541 666 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 8 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 642 541 654 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 9 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 631 541 643 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 10 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 620 541 632 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 11 0 obj << /Dest [ 73 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 608 541 620 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 12 0 obj << /Dest [ 76 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 597 541 609 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 13 0 obj << /Dest [ 79 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 585 541 597 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 14 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 574 541 586 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 15 0 obj << /Dest [ 85 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 562 541 574 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 16 0 obj << /Dest [ 91 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 551 541 563 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 17 0 obj << /Dest [ 91 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 540 541 552 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 18 0 obj << /Dest [ 91 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 528 541 540 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 19 0 obj << /Dest [ 91 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 516 541 528 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 20 0 obj << /Dest [ 91 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 505 541 517 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 21 0 obj << /Dest [ 94 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 494 541 506 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 22 0 obj << /Dest [ 94 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 483 541 495 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 23 0 obj << /Dest [ 94 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 471 541 483 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 24 0 obj << /Dest [ 100 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 459 541 471 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 25 0 obj << /Dest [ 100 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 447 541 459 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 26 0 obj << /Dest [ 106 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 436 541 448 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 27 0 obj << /Dest [ 106 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 425 541 437 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 28 0 obj << /Dest [ 106 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 414 541 426 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 29 0 obj << /Dest [ 106 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 402 541 414 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 30 0 obj << /Dest [ 106 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 390 541 402 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 31 0 obj << /Dest [ 109 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 379 541 391 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 32 0 obj << /Dest [ 109 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 368 541 380 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 33 0 obj << /Dest [ 114 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 357 541 369 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 34 0 obj << /Dest [ 128 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 345 541 357 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 35 0 obj << /Dest [ 151 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 209 333 541 345 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 36 0 obj << /Dest [ 151 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 322 541 334 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 37 0 obj << /Dest [ 154 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 310 541 322 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 38 0 obj << /Dest [ 166 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 299 541 311 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 39 0 obj << /Dest [ 172 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 288 541 300 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 40 0 obj << /Dest [ 172 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 276 541 288 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 41 0 obj << /Dest [ 177 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 265 541 277 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 42 0 obj << /Dest [ 177 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 253 541 265 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 43 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 242 541 254 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 44 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 231 541 243 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 45 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 219 541 231 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 46 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 208 541 220 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 47 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 196 541 208 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 48 0 obj << /Dest [ 182 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 185 541 197 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 49 0 obj << /Dest [ 185 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 174 541 186 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 50 0 obj << /Dest [ 185 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 162 541 174 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 51 0 obj << /Dest [ 196 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 151 541 163 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 52 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 139 541 151 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 53 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 128 541 140 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 54 0 obj << /Dest [ 205 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 117 541 129 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 55 0 obj << /Dest [ 205 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 105 541 117 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 56 0 obj << /Dest [ 205 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 94 541 106 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 57 0 obj << /Dest [ 213 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 82 541 94 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 58 0 obj << /Dest [ 213 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 71 541 83 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 59 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 60 0 obj << /Length 3119 /Filter /FlateDecode >> stream P)V1YAu5] /8&?+DS~Ub-V5 āb6n\*?ZFJc!fO]TV,سB&gs㏆YCxB&m1r..C|ӔĪDCܓ!A8 R@w:ivR;y 6$)dnMm}4gGװj*t˽`Ȫ>u㍷ƣDϔlSXPr,1Lur/9y)6-㈄xQ,xiy غܖ:s~n$֘L&1h3.lDtv 6&Ѵ]ӓ|'1Kf vޫ(I_sq$Qslo婃0p aHjx^PI!ۍ> uMH\jQԭ5\㣮]풤t4Cqu#!`۠OY0 X-ǟPr_f|:0գҕ wzzɋaZnfϧ7YOpF *>tryZtO$IF49DN?aY˞(A>^ glI|S*Ds0$4oߢ8>]E߃D߲3182UŧHD~#P" jCDs bZfl8+ױfrۻ#jý"8\yΚzu; A0UKЃ-y qא K'o\v4Ny)cB#Ouxn 'L8S{Oԡ++7)iKV+#2SM uKdt9roPycՐX3}ū@F6Ό,t,1++jlSp  5EG:R'HY5))΅KZSN[h^ְW=/./;g!lsan7ސUCj`Ip[hFstu|z{[avsV5ppX0 lobJ^L FrF""aH-4Zk!)'Qjz?2(Ip@vKy-C46 aֆMMVK_ !PBV6H!uט91ҖFH'mY9˙.eTq?䷃eT35nWC wV%JKu#.W6x`X^ h}h};Y/hu%cp{jܰ@Ou;LȤ٤h=#[y`<4{;BϼTC<3n2jApoidMEZ W4>RYh3:cJ. -[E>E%{Dbf&Zt, ~H53 a [j1t!>ൄȬ7_C47fmQb]4+ SEQ XR|{(ֺu-w& FmH:.7T e'c. k,@؇a/%ZZ zKt&ojl/% Kdug<\LX sq!QN\;)'dXyAHq`(h{(UOS d>' ~f&S7gTʩ#U))D^D6fL۲`CԖ ;N]& */..kwOl3Dz`Ó>6X_ n kAk >040#"cj8iCQQ6"SYh7Rk 3%pRõ JL> 5IҰ-Hb0 UO1H$H܉JCD TF},SX(z78G̼%K&}vSJC;2HdPF~-d@r - #~&h5>_vV}~z$m 2+Gjt7ic/m4g(e7hR:(S8j>\fF4P tJwg8 ?1'ȐrS2f[$ېS 0fjuƦdsaoyԃ>iGJ|m+a#ԜZw?CkQ niF'n],NZ󕾡Fa,L(_42Ays+')_u04;M-mv>f>!NYDn!񁈲j2Α:-LG4"WET^ncNxKK:xmx` I4Jx!k72fe&ӖbQw}8u8gW yv{.{ |K.d  j GᾈxLJYS8IujßSdhMUbf?u)l[Yںjg˦|&jqIB\Yiܚ,x7n8_ =\r;9e`45Zcek[muԠ)ܧyT dxcѡtKT3z`5=`F}5LXQ:su$9/!U, Y#Kin$# KRP_(B+<&]e[lt#Lꕗ_ȗ.{{ [_Lh2B3Xu'KS H]. _U>ʢ9Gw9\I#Kr4q #@yIɿ endstream endobj 61 0 obj << /Type /Page /Parent 526 0 R /Resources 71 0 R /Contents 72 0 R /Annots [ 62 0 R 63 0 R 64 0 R 65 0 R 66 0 R 67 0 R 68 0 R 69 0 R 70 0 R ] /Thumb 385 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 62 0 obj << /Dest [ 218 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 717 541 729 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 63 0 obj << /Dest [ 218 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 705 541 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 64 0 obj << /Dest [ 221 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 693 541 705 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 65 0 obj << /Dest [ 227 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 681 541 693 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 66 0 obj << /Dest [ 227 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 669 541 681 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 67 0 obj << /Dest [ 227 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 658 541 670 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 68 0 obj << /Dest [ 227 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 647 541 659 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 69 0 obj << /Dest [ 227 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 100 635 541 647 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 70 0 obj << /Dest [ 230 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 92 624 541 636 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 71 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 72 0 obj << /Length 1196 /Filter /FlateDecode >> stream 5PÏYW+#ۂywyiWsEl$^j _ D.Vmwc}gvpuC(\"l7^ 6$a"JzY2Unk(OuzwhڰLSvAS=/ҖEN4S>[b0wcI, x=`;ŵApqڥK `dz=',MkzZ>RΩjO&wvasZWEĹ3LYӚO\:T pٯ); 4* liSQ`#|^aH[SdCnA xu`T$?P{)j%"ę"K}4bڶ5R҂cGnKvl yy,?k?H0 O#o`VFPytѷaNǧbYkc7,@zr!l[.q }Fu ͷнcep~1dzX ^j%сfXzKftfcDV.#QxhtE*"$AJMp-ILu 1В 4!c=CbMkMCGW%q&!F2 h͇ɢΗQ:|CD7W.F 0ƒC1,eBͭU+&:l;%^ccmYjg/xLT!$aSq$$%7̔(#F'iW-$  ɓ'PR({$4Hʷ+Hs$0‰ڬZZy4N endstream endobj 73 0 obj << /Type /Page /Parent 526 0 R /Resources 74 0 R /Contents 75 0 R /Thumb 387 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 74 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F4 295 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 75 0 obj << /Length 3023 /Filter /FlateDecode >> stream #Py}*gZ&G>OᡒiewN8aKX.zhh_0vn?Mɇ2ddB-<9zx Ƶ O{\=1nW_ŽQRNؖ Ţ0%^' V@t<(G't?P ǾJ|&QS릈,%3xcaFe ] HݣpLXRث W]__ogR`h=/nO!!n!-wd6QxL2%Srq1f GϗU:d,LLFjnk4x ⽥n7)*mO~ƱэqHҖV˥ɡ*u2<Qj5M5gݍ[yuf:N8~I8GرMR6g6:˄ߊ2)[pQBm]EjH3:N'TSMZ GVDBgO/G%"4=ڱKv?UASH8Z>Yt뻓O`=ψcD>pB0~f Z{7=3فIU &Μ9ӬTb*׮@=}>;zbD('(:KF`ˇ>f4,ŧ(ȁ26&?G7V mHDujC!O0ֲfK]bbߒVߣ6J\|U Y 0\ጔ+bQP4"JJ@N4 jۚL * Ci~Sb_zi>īq8{㪳%= )TV?6lƭ:G(*R{ڹqO:V]=cB^KvL=,|'JVE[vXyN`s7OBv~[|}kCꓴXoQք\bYX{s~iJ#ֽ 0C/ Wro̬xKR͝6'j Ɠ4xHY8F)1Rmo:! xަL^LTPتMEU% yJ?>p7hGfBȚ7G=lةnk౜<t)v:8Suv"ųI~޻j/B|u:x*!)B;E'M |䁰ht T}2Zw D##{8W ^=87cv8Da4f5wʽ=jƨSwd3ZqrWI.ݕCQ^6t 0+h IezaS0'Uz?/Vɓl>C{ަ'77II"ƗAY9Zlu'swnL(E~+ whh=Ʉ_>>iR n3SG޼;`> {Yz; Ǻqd{5c.(}'j,J$޲ހY:Uǎ|nҔlWa5?f|`AcK z^ $Ϙj1ja2bLf_>ƶ!uT IgKi'h{RO'Lg1P̥bN.|O2*!Lfi?m>/D=80 endstream endobj 76 0 obj << /Type /Page /Parent 526 0 R /Resources 77 0 R /Contents 78 0 R /Thumb 389 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 77 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F4 295 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 78 0 obj << /Length 2775 /Filter /FlateDecode >> stream TRb}+ϒlz=;^r¬>|C /fO(ď5 7q?xi0"*偈=Q:q$]:z%"8x$t R0#R=κgcK6̄=ӝCa2:(xlYE0"Wf2[є Nr@KSdCV렆޸RDd_ wɌ臐\b`@0>1广`iW(d,M's1h096ekZC;.0 &7Cf&[-~ ?x.,ćyo"<N_]9]niu#kXDl!ev'sm ͈-7Ƀݚ>oMniRE;D 6X9~EfNLESP5 [ 14@lҁ%iWW} LfQ4M[˩kZ}umE]3ų:ٗ# pT< ݹ&S`S#(^kY&M} Cp2+]r{rDb ĊV-4wY$b8^+SvCSn[TIpב1g}fnl4ܜW tUےMk7?rYu  =^?X+CN{x'L lʰn)^1$:ɍmq8\G^R)uHϗ3ʲT ;Nd!\ 03gB6& { g0I1[h7x}]]y+OH '6`q}O["HO4=qE?^ݸ*,À y$BL+H ?dC&=]й5~1bb(#܎VwI@aY ^yՊjAT?D'WΏ0 X1ԶD[p"gz; ǔn{adD8-Gb>iVm{Ӹ8Sw_X&y3$F? 6?<[6mz;|%pvpu)|Z52"AmV:4zp@DZ -|8@bhЃa:2z)rd*fR@e,j"c=<ԥpn&(đەr*#ِGB'ِ /_Ea"͉DU{Y,P٨9/}^ KajO[O^gC`rZJMQ0/+!у-jzBy* Ǝ ; hzzLS2Ϸ9KႁM[[^O{hcm)Nc.sP@dA{ ,ChU~[#)4G(?nf t oZ@ p^zaV^-C i8(ӇԤCe AdAx8]>3Ѻe(%Gmg_E=97ԾVzRЄ SЬE_JͨvXY^r'cbјsRiV\*nm'5EDŽJהmu7XqkF&# C$7 䯭/v~eɅTg8?VuKzR$߁^|]p`#xj4,ѵez!Gh];S0uh(-8-bSfahwQ"yܯ.߲$)ȳܵB*Adnv)o\OGKFqiI7$CWi{'r_I w믴BL|ğ< G OlF!#foByŠh"\{oLް5W{[Q7tė\?u Mb1ژqòxA 7ZU2@?-uV.gGE2DI;>đA-Z1>XupfbXS9@ _۫SX#K3H%\HVۣdt#)<Šةln3FjX(e) endstream endobj 79 0 obj << /Type /Page /Parent 526 0 R /Resources 80 0 R /Contents 81 0 R /Thumb 391 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 80 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F4 295 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 81 0 obj << /Length 1774 /Filter /FlateDecode >> stream .*$e9"ꬲ~ݩDgŴ7x#0sMN,>/=8FStr# 䪄Vj\Lx}Oc\B"ֈH"_e_geVk-79s;n;m2pɆ0Ā3(mo.(4'M՜,By+\I&̖l wa0W"'KL=ڭFǁo B sί,5?=C=ۑ& )%YWXC蓨jT9̣DW!V==ukی2Tã?F΍8&]. &8q}:Cyoip3ġ49g6@8/PѿP*k,A'-ka4r(9%Ց;.&,tK%x_dN7EMŧ4ʧpYER԰Tܝ\QQ]p*`D`nH ܷM B$504 v<;!TY7iMͨ^3/Nx{K{a03#|8O41,Ar} +\ n񱍢K ,)#7@ЮV <ؿИ҃OiqkItf4fxgcD #znרP ־ϼQlŖh.syPHPuQ7EVToN#'NOh6m*9٩H$%@:BOfPBi vH. !~.3]Ms)V\,TO"J xUf5(~*]NQAV)Y}x*tұ T/ ̔ L6ٓbw2.FFS7[7w}4e{Ռܚ endstream endobj 82 0 obj << /Type /Page /Parent 526 0 R /Resources 83 0 R /Contents 84 0 R /Thumb 393 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 83 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F6 296 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 84 0 obj << /Length 2146 /Filter /FlateDecode >> stream NkԹ?$h uTdw3]v5YL.EqdySa{ Ҡ!2wL8C}^$7a=Sb,7IJ$zfQqUdmM?ƠF>^:0-})A>Ss>>HMc~ RӛEyF9[,X8w(/s)2'Cu'ONtxX`|M)%w58:X\c+>#!2zZ1Dѣ[7p"=Yf),@<ɠTs/&JvE7_4^"p:JV&1I >!7Ec$w| ء: r3q(b:7%˜*$w_Ôe~X \#KAib}dycD'! VSP4đ0Q;2Soi011‹آ di @#:`CzA2pu%=)qE\FU&HХ/nTy'*dwϺx`"DfuFΠ/:nҿvh@}4=@JoWXGQd%n.8/ـ}[duHNNn~Sz,%04{7 c{kpݑhEbqf ;VPGV[W|chJ*i;}PKRnf^쒬;tF?Lbd}$xQ==Ck $mcQofF{{$F}uA4g ,Wuj+Cs<> endobj 86 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT12 297 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 87 0 obj << /Length 3036 /Filter /FlateDecode >> stream ".MJQ\Se|5dCQ1/ёI>d&Z_/;ICՔ@BQ!DP] XqvJysE]ӥ0eY9PޞoaŎR\ch/bM^҅(ML^&'SRCև^|:)V+G;J>$2e O\ M]7&0-!wN =7s8:4%IW)ѝ[E .$*L2QBVlb c+–!/RbbNe#rFK.ϰ\P_3$& 1J tFzÈ;X /VQ2.󴞓ͺ'R"/_6x%tm0o ^ʄ j 8@yOYwT25Gfj_*۾*5ՔU w^C1r@YgͤE~ro33%em/Ĺ؄#\i-w EЍ'}kUD1s+-O_ Jdz+-T{#-D/vkM}G_jAx'W&hcm OWJ+<`躕L6:ouB4QcM5(:WT+u.ρ݃<ԴA -gn9=>s-I ~: uUQA8^&Ȏ(4j*41zI.OPlH?-W+Sg&|8 n7mpyھs:wk[:H|J%; sZuH xPɢUi#7jcC+Z9AS Ԁ촦dD`jL~6|p6{ mWdr[OxuL`v(R!oi6LD Ҝ<:[-ˌ5[J9]7߃b eQ)cD3}57?њr S`# G9B^&ΆFu)AZ%M|Y(ΛiDmECo dֻu)"{@` 9,s%h@ iJWg)_bξg<'ZgU7AT.l.it8Nz 4 S&zsx>Eєh"֨i+դf"PRҀ+9b] \5 Ȃ‡^ςw]#L^/}8!Sy[ nGoQM*5^HnKҬ}fNR#Snց 4p#wEy9pt >u,i'tGܿVKx< @ΰ5ݒ)΄IHtrVMcF3@0f~faΙ͹#vk u@ ?F*xt Kfdnvyl>g{lQ0=cvO?v ۗv("Ra;a7BF׍쇢WBO+Fn2.~'u`Wa+5 YmNY(_֔u]4PcI:@aCQ;[s}:-ɀppEՎvS[ ;MQ&z_G JLixރ+f;űP`@r42;k.0Bi[ܱlzdHвPI;<|激: O5YbUIؤI`j d,>'ˎ¶Թ `^~&+[+ν|*\Qǵ"- ijD.0t! ?-'v9:Ll#cêhjݹ(W}> endobj 89 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT12 297 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 90 0 obj << /Length 3778 /Filter /FlateDecode >> stream Q6-off虗U1kd UM>WP,O9qܖp4dK SDxv8#G%"2\4,f@~ujYb 沐ya#j kMqv+cMvaV s+fAo?a^pXoжץnɛ^aO>^9I#.'`Pka\sӰqfPg]~(υ3gSn8htP-wxV &8d;qGKJ=}>(),hn*,VU_kī2#2IL*hxנ>0bdz*/!wHסTǢi3XV & yqj¸*Z5{iJkO ǓC=D| V&[9Zꣅ.9Ӡ3]F!PvDg ;Ե6O#Ldk)NfkMۇjuR1{)ӈeV+>x/^a]JZ1H͞j Ւ`_XdD}#y^B2td*wEdizWlҍ[˼$C-CU Mҳ!ķv^ !d.xjO@x4Atʆ?>3k.[4m.WEBea')qO% cS_|o`}x>K"]~iй<7g432"K޻/=Yp צ9cqF߄[ml0I˕C։phRkV3.@h"0P= ulOGqOY Y鹧UiCK=liʑb^6?; uv/ES'6[!+׭E+WA<# -hn6i ΄U2qRwޤ#h`BU.w\9|92IX{4ɀ] FĸB :GFNG! gd;X_H~ Q ਩VU,-J 0VODp,awjBmNR;y֞偋!MIC#L~Ɣˁ02=CΕǭ} ԥ~/=' *02E5,5UXt|f4hf^[r-cek;6#gTfE+=::5V7GQRiϾﮀ%FZwLU9Utkdt_UT ߂>*Jet/jsX )2V?\uHvYJv H۾(ű=Œ>mCtyHpՏq+iBky$^t@WW'CΡlLY8il[/xo'-3bnR")rrv`A@Q2s||gNHgfw/}YĚYi^*8(~ !;fSO"!H FѴ/ !VOP< L} Ub gqk!=rYr;f@?ztă`;Պp2t2C@5vUWV3\)Ax%A!Ôwv(]HYK@U;Q ?΀AKo{Ɏ7UA<}|sȽI[޺a~*TѰ.bV+p{}/)2W%` yVG03@M!OÅ?HMFǓb0wS/:ȫ`oKAN3yA,m4!_Fa+DBA .fqfly, bAṹ b#q&RSɴl?C vOصKg]xabTilRlB*n<]s n%~A/m<A'pl4$@?ו_`*i.EU*aڀE{e61⾄;=nul+FJOp!T"ceC@)vN9Yw2ͦ/Ҧ̱ErP )H!%pB|ƽ7Ĥ/*B .Lc x5 }k Bgt":08P΋{Gj=Lˁ67 ye"gvoPͨ}b]RMkNGfT Ǧ) fS:q.u3M[53zpX`4A|sH D-lq%Wq@;P ½Ʉ]R&=9_!51\_!U-WO/@zH_ )ˆd|`>pxݻh. +Lu97ژΕFbY4ü9_nCKG@T`cSsa 6 iQ@ַ |FM6E50nRiLzӎ="2? #5%'4o/鷊h`Y}v$ ֬ aFҡ(lav裊RKEbLMFݳ!$-\Njǫ!0l=>~x.+a t]i#q8RCVҋ0ҵZ]m לܠx,]KsÊٸ/A`DO Y^"gfʴVNj!{'Ժxtr%<}5,Mx`վ@QVo+# qyR~Ȱ Ưy_NۨR"쒋dBAxF㢀2>;Lh$/lkX)pj^V -RAMفU6ŹA pS"Mov2|L;҃K endstream endobj 91 0 obj << /Type /Page /Parent 528 0 R /Resources 92 0 R /Contents 93 0 R /Thumb 399 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 92 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 93 0 obj << /Length 3136 /Filter /FlateDecode >> stream 8/zx6O3eWFG& q+sDN`X/i \]/|L*LB4E;_[jw@ h9a͕06imQ9Ш<9X؃ oUZߗwIm"ÊS.ӟkg_2[(5WdHϓdmA-1ΦWygY(>bX#ZYl2JNd`}f\P~'Tss ""1@( ΄ne51h*Tu<l|PY}%-Tx'_G= Cwr;)i86 '¨dD:9"u٤}`> eu~Մ!2ׂ,S0Z 0!.BA~<[|_х>ۨL11DG2s,5' QKhi&/<㓶qgZ)c` |%(,oSừoRPm,d8rT~k2U2V(YCXVk oZ#)C\O:B)0dEYm G~_ý]6du;wsڃ)dG!NԴq Hd]j˛UWck\x'QaK1*Jw/|V f;ϯ^[ Tk+.d-7j=}W1-T\%fQ^>7YhjYv Z/*_ePC,@bGqw?mC!,nW)d$B`c vTM]wQC}^XZ`cwTxy2R`<%03Tb- }Dg~z9?%"p S5F\3 Ta/ =1e1_tgp문5]PrI^aMJf-;ĥAt4ր:z&PX3;t6>BkcG_BYA8$5]ѱm߫J\{@rV|"F4!=CHm鱭h8-xE떇yFEλFd8YBjŢMpwdA]3Hʭ9W^#+k azL ܉ _@H?R4'GuR)b,$(+_1siNԾv݋pQ>Hm?xy % fVU P&U#+ %5YFK>dۧ2̨<#0~~~T=Ex"),a\jNѻSVhjq։X'Ҳ<)cs~!`X룎r$s_7~RϿ0rd0g`%,ml -ń_x+CΠ$VH"-p%li6'+K8 E2_JEI2rk~D?xbbY"q%HS8.c'G!ATKxVhjUF,7DL'h.'z~W֊1 T9+W aɂ2Q |>fXg)$tص hvtCf%?qD?$u5;tAhGTDA\03dE;u,6}U˻Hv(f0ե׼0EbZ2 ¤@|]9_κ Ҩ[op?zPS{rEF;,uŗE- ?O4Z##9<8E!2vs"-F^J>^jcnE;<"][bq4$9J!ɟ~ `՘|F'h㶞33g6y &{0՟M*K ){>=g`~օm%iWQȇ9t;IXn'#q*ȩvd*&mtc t'>ԀTUZ(2A/Ih%63==W!$owHϣXR\k+;_Q}RPt'y9Fy2Ha[;ӻ2ӯe'wx'nY*F@CB.&<`xUJ8atoKc6r̎]9Dmuޏ1aw+x! @\]ezGLt.Yn`r>ji>u𰡟WQ2SW>n8ο$Oyrg_~^l'knh)QnxoC%qdEFLE .F(Kw\4SEUAİ-˴ƛTUg| ;AOb>\I[r^6As΅'5Ц|M^ó?֩pU P/?OeK :/Z=&}#gf~˶=`Y]&綰to|Յr]meݰ.fpiO\>&>s£x]\eb^UMU7hЦDDUBx@>MEu8ݝ䈠rXGZSq\ylz>"K9+D3|0K endstream endobj 94 0 obj << /Type /Page /Parent 528 0 R /Resources 95 0 R /Contents 96 0 R /Thumb 401 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 95 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 96 0 obj << /Length 2952 /Filter /FlateDecode >> stream 680kV}W}5 R=%OI 3{yX)Ql"4*P2Ѕ ;o빗s]E?[׾r)*pOcR~ޚZL e(fpvj頚?F7Aɂ#6BCmTE iϒ;S\x dq[,rhqho7@a;>:e?@5{=vqm6=_-"9Sװt &_HS+^[&}X!˹_)}pU/䷓deP#LxWh`Bd|Hc~Dz>It^Y}?pީ0G,@OzBkmOd,^f lt׃@뗴첋F]ߦyt;c [nV]\k`,2)bY鐧N[4 % CY (ܨ":)IlJzlm PBV2HL(u In14nusn^SL;; j2!}|[>@WFb")dLFTtLp/Gz>"[׆ePKc4S9Նs ۯac&؄"D7 ݤgϺ4 [ôe*fSWq1V tH5͍Iǫį}"r*E{!cARQ6i mGmST긧A/u+=-cv܏![T]v[9b]{!#lYRV[Lm_CQ#>t[h++tmMAh;Si ̞Wv _+#I0\xvfh4Q ]5T.{ӄ ix[6vY`=ub{;P,n*5+⤫tY\i] \qRdTC+0F2ۙ`^˗5gW6iSȷV:W{,*b=Oozq!eʚ[]:awiMXdܶ*X(7T]aݯyХkKPᓿ?ٚԒeU S% y&]=4l"iT*d$iRm%mB͖ZApg6[^ߺ"ZĴ4v@!Dy%R݆OWZD|Us1׍Q\"8SJϋ/{1Mh/t8Bz)A^e뱱_\{YDw8ܻW-SENb|ePS}4Ql@ ~L D#~M#i2HEvQcjiV zwoa ڻt{~^\GqnIxKnl]o04tb\mB ^z}p >Ԗs0|8sԫ7]4ug.|ImΆsTi& p\2g0 AȇY3DVDǻ zy$'WH0o53%D剜+c(cRnPeHZH8 ٺ1sX{r$u} A4)<̨mfDb`{rAwhʀYvQ=$nq9 w,ȢPW;4VS^7bAZp endstream endobj 97 0 obj << /Type /Page /Parent 528 0 R /Resources 98 0 R /Contents 99 0 R /Thumb 403 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 98 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 99 0 obj << /Length 917 /Filter /FlateDecode >> stream }1qϏ7v>i-Xې6F˯ WB{n(5_4@%%^։~9udV32Dcß>5㮶q['oh=pN+x(pZJ 0{Z#>G'ĈWZo~U$5V.jOzܦ> endobj 101 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F6 296 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 102 0 obj << /Length 3880 /Filter /FlateDecode >> stream 5,s #ĺ!&GD"i"+˵I.}ŀfh p!I&se ~q$k=Us赕U3xPxuybx]^ 8ѾH] [ygSm %g{sgc[(ILJHZ曌I'`eI'PM5j,1%:0)1p(€ˤI3D/k}G`&T;q؅h(rC+fĬb9R/Rջ GRj)Zٙz%"#k9`hK">IJ lhPC@uGi2n)9GeSf'g 2lHҬ +Mp;m KCF';-bT1 kх^UkL9+v ɓkzPHX]|1X~PP50v4˜{:^Wzn<Ż[ E?} ox.鋶sOH~^P _T-S,Z&Yi!/JjvVGdhA_O\7VY®xT}XHm'bm|@$ј]jCH&r3ZXY^&צ(Q9=f59g'YٞePd#<"tpaH1MUH7u9h9oq<Πgc~PŠBg &% +"%}iqM M:ȂŠ{CWY\w2!ؙ-d(+!FS(j$QѐfjlPv$,`,s3y dvpSd@7onfQF,% c8i̍۳H'}75QTh̏VgHjhgF=@[*堹)yn F&^g>6?mgJ7tPڧ/w<&'b+W2x//G3WsCS<*\jސFG=B'JG/s>Z mM%z&j!'R [ՊXps $BQ}uc=6"~P,pMӘW@%0X94M7%#Ln/2H ENӖ)Ǎ8ӯ(Il%X#VfsCwX$f+l9w+S\}ϧXDrŸVgU(Ug>>rZe9l C#̭r'%;2 d}e⢟$h0ukW1opZz}v6A'0b\?Dxw2#}[Brp?>g~=YZ7+@մ% ] {[E;N-lnsɺʯf`?Np9$W5W7|=mf["{fUa3Ļ8z*<}H-9"?#E>)"p Eh4*c2?ś #m-bQ_L, k4aAGĥh ]n341O>ɲx_>9+8l`ė<,0 sQ="77/rI`*\G~b'kWf]ӟe*Va]s9q -6Xt)|̀o ߦ*?q$ G&~ 2D\Aɿa=*Sڂ@qcW-ы8=yHQBΰp]N˯-Qb8g;LpK̳M*کn$}8"R ]pzނEo"g.iF[PcY ѬpCb8Zya5o\0f$$o2}?70pKM( EY8*6a,>x&k"^|$[Cl)/ \wׇyH"lOXHd!(T#n5)MC sBiq[{EqiWe1O-dG֖ʎP1ॡĕef˩`],J@H-t%ד8Z8k\av_r5qM * h\!}׽pBp~ 6%߸w:XpGϡ-֟8d Ln_榏BIdzD1dTz 5 3:Ly\k K3_LMx3Z7lW!gH(ѩh}0 mڭI(EAs /R r. :o}9HO!cX N۷o]J Kys]_3%vkLds=Z/[G`sN_|8|z>%&3ZхNtyjCi}K`HLŞmC!\ĪaMc0qlKRﲻD޽ |5F ܜwqdx*H\Զ Vݳu|b>wNē=n~މR }YX'<Dj튷LdmWKk/՝!VSrM᱕0(}=8U_I_Ftܴ7Sl? vm=G&:;Gxnx:hӁ_IRgCDMWpН+; MQUn}/h9VTGWgIƖ=鮲t${V_|( ra5#2y`F,8dh[2`M z sfݨfOsGZ*x}]4kɊKرǘ!Rכ6}v3N"))Cwtub"Z[FItzthBz9#OȁVk\`$A x>Xx0KOǕG#p̀!]˥Pek ;"6c3TqlD87Yq4fȕ1(+Tݼ׃Y#7[h9h\" 9X>KzׇJ-9Xzlr}9~*,F endstream endobj 103 0 obj << /Type /Page /Parent 528 0 R /Resources 104 0 R /Contents 105 0 R /Thumb 407 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 104 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 105 0 obj << /Length 1766 /Filter /FlateDecode >> stream -nΞgK-ng>R.'Zˍ<f&=R4ݮ0ʞR;q4TFi0؛ \TaW$#s\̊o򳫘VԉUyM֚82Mxz8'c30g)V?7O sec:["+ 頣RݲU#(NBrc=t͇2GchHS>BV"4Gg aHPg{GdF6TCԊ}2mݯJ IK-g^j6u'gY:hJ-dCnV/ᵝr{ՙa( {QOAMqV鎲:h-.η]DQlrY_qxEh_0(}:)Mfj JV@  /jbnL r .")9( mZ䑺QMފf՞4T>Wr8Ft?4z{zFG|aJWuA ܆0yp UIBvD"!]vjԓ/XBYZrN?X) * y] W85e- jXcJϫ]X^>R]xA>>d)3 ξzRS0~l -:|F)\ d^$dPfkFR.^wFӄI "}/9="qAPLv#AQ\hP{87/?h=9:_=H]E'VG RH_Jn}LE .iɢ[ǥ4  Z\sВ[,|;bw&UXZ$.wN̟9Iez%R,:[wc'b]-`L`>tIKx [C䏖5(>5..yqi4%ZVSgl$`s6BG H /\E,w-o65vg^Dwy f1>ܘBrIہ6qMc${ $#Zjg.mA{(2m'zfgt|V+E#ggr,STA̘U1y6-<+}z keI{%EŠW\AH%z!;nVM޻T-~|9,c_0OfTV#1Z!GH's~6 v P Hqg 4>km[]..kofD\pc>9Ѐ{>j]2{&:Pw+\O`%r[M9s"r7IUJ/R]]xWs8LK!y4JeMAپh " ? mJsSGi]̣'E!DkiۭغB}+ި\6|ne{ޯ5J6&&8<@lIZ`R4jx#ij3 7{\({;Nv[Ce|{&FpVA,ł2 F6]Y;{{ K+ endstream endobj 106 0 obj << /Type /Page /Parent 528 0 R /Resources 107 0 R /Contents 108 0 R /Thumb 409 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 107 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 108 0 obj << /Length 1839 /Filter /FlateDecode >> stream cMʏ?\M Tw k^b`LF:s NchPE[j j'ZAV1Y (asi2&% p'h|&z=bq/: g#c FEG]XPU:5ؐ;ck.+ghu- #p+8ژ(+i/(O;jo.ǏE80$Ătsɾ8B'x1]+<-ncKqs lדmSK*pڀQJe #W65TLG3mw(4v'?"JkPEhyw'}#%_3_`xQt}{%3!ѳݹrzZ/$մ> [.#vO~0eV:;Zq Ѹ?$V#ZC3{&IaV :xb- ޮa-C녲Oal"0`(a+<S[Zot -Jg%0W5+ro<Ccl'os!L}g2# #FU8qƤsjzۇDcFwYk2u^\:7խφl #wU0: '5ٛUC\u0g[Kzjd-Eh!iQJJ'|0Ï{֛.1ݨ:2@~dOG5'ERiEvvu !6֭=#TݞVd#sto HF!8,~|7U.+ǟS b|^' :RW?'3? եnxnrw#?J5ٕгc8a.ѲOۋp`3JMw e6> endobj 110 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 675 197 687 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 111 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 585 196 597 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 112 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 113 0 obj << /Length 2714 /Filter /FlateDecode >> stream 결#ٌ^X9G'R-%'T@NlygMYPzeo%OSEvj U+A=үHÑ|)[J_?ImLA3m6k\4JUƞPW#DVT;!zOarȉ9u+Y͆tS@A7`BV7;3+ "Gh}%jT*? ";Q8 򟧂g=C !=_(DQ+7 *JfxAFW\{X <,g0->J(fWa Lb0Tu%w۶lG$YXonS:fAҴ?R~ ZSAQfS^rŵj4X>\aȡA>?,_(-zyYn`Ÿ,z+ .\fgUhk OP^|cϢ:)1cz;ٝzaBoS*$v sR|3Q{j^9V  8Sơgk R/M'p7`ʊ.!j8"3fR;1.!eG/Ӷ8oG,!,bA=&XK.LSAC†Orj(cf~'CѴY 860fl|\o~˫ߐmaK#&@8.Qt(&>!`Sv7hg1V8>CAл&݀@I(9 gg# P,@.u3 b${5NSURoȨrH=ê8; Ml=^ӷ[ kN]5$贽C֎'iFevK5@5 z~s*+:]&kc;ri3b- 0TqcW')q̊zUWZcn+}^[߀1% 5 ׈]"-Nb)z=cJj,K,%gF.Sb½$Nݗ k1Nz& ՔVMC飁*OJبP`I𪹺N^տ[R FA%X&hиs-W%_ndC9=^{N V,l}nL>* E$ P99]+IfXM ת]Y]&mňծ2UeoiJoץ_^i73WVϲ:Ǭ7BIW׉ڈŕu J彉fӞm9JL$"Rqv)%@¸qQ NAQ*Jl'R+Tu;\βҮc1@XLm \z A\y*Z/88YO͂Sc?Lg7(p=;7<{Zvl4!N-j@}MX1\,e ]6j &uź[ @ڟ`}QvXEf/L-oo؇i3=ЁN:a*Īmiݎ>m8b]kALT#ƛJS.0 5rvY`yϏt9DX$ A}rpRM+-wי\k7P?goLaLʃY\nfQ@; -5KeDxjM]}3Z2<²ŵmZAe9ز<"WnlNT?I;]r֗_DICa?QRik*~4) 3=ɧLr ZC1BH4CWɾMf'W?_c]NbwBC*?MY86~ endstream endobj 114 0 obj << /Type /Page /Parent 528 0 R /Resources 117 0 R /Contents 118 0 R /Annots [ 115 0 R 116 0 R ] /Thumb 413 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 115 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 705 197 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 116 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 602 196 614 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 117 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 118 0 obj << /Length 5045 /Filter /FlateDecode >> stream .i ~*Pd$BH̆\w.d |\e%i(9 k^9Vh㞬!Wg[:bKi}OHpe`=DCsmWeqeJ`7Tw^vNB 7w"DTA8d\-dw]/_b`IVNlh J }> Xd?lhd*Z`xեCIуSiA'~p(٬"o|yTDR((W[w&ɯx/ A&R1pI| |M/8c1ws4Jp8T߇U9ҳz3}R4VBJ5 Z6dlKAmĝVN (VGn}+-Lny-l~)cG%23.c9fǚL@Щph=5qP1ř',SXۑ҄۰ d%'wMyՕOL R"5u h]vwv_鞰̆vk吓 ~@>t\\{jE9!}+oQ 'I@q8*[FG p *tdOѲti $7񟷶-s!)X̝DvJ,aNHMЉLP)@i1quIՒ9b3% %uN/{2{zcڱ4+6hRɨ[% 1'bgI/X>dduQd}iDc!88 ͕\☃}: SQIJ7zj!u4jm'@-R\#Vވ9m,pStv?EKG]-ԇҋWoP,"y`V{ŞN?3ˇy aADOFN $O7g/H?gh&Gix->&XX`*iG9G*8!6$Dfh)<bTߪ~CqS5P>Μ<\E۪ AyJa)a!Iuy_KJ±'Y!@ue T~7Sh0{گ^ÁT*qDIur^A\RL-Br g8Ky(1'F)q 06А/dB[q |"MļWYd@\;|<pK$l~iAew86\'ȕ V H0mK GU2t{`b2P<:QasLSVOiP+k!ovSUsoHx8!Ẑ2zʆԹBb,IJWeT≯ =Ȥ] 3(`&3s4kUG71$G#e%lvvw$"}\j9`r>7HHg8@T;.ލCiKR}4dR=nbzJ]}-")+Q5F`{2\*kh7G 3QR oDl\ΨnORo Qߖqk;kN!RVBS!UӃwi`iCkY"z5 ⑼W~Ck{/~1vӸj\{0B@~ل$ZI>ODfD_gZc=/@2qqR~)w)|&X򅵚Ux q͞gk+NǦ7G-xI>&50(^MRٞG/1f Vo$i$fvL[ 0QjjZvWg78q:HfYP:Ex|6MĤj QQ)9ĩS}bk 8{mlշ~I6z8n@K'e{"@#Ts>{gBmPa%vG9yzq_E9HB3"S XAA_C'תczxjhiK]ni !G@Ҩh~&8W #s2m1e@2T^~S{mBƃ&r! , >eEH) u>.XA6o-fJ!|ahtLtXpxq{@|"^2^vWj""Zԇh i<+㞿<-O~$ip`Տ _ zpF(e˓Hiu׈rxg [> gI%s:IF ]a3G| N]u$B\C!.Fꍞ.]M`X)d|zS*̼=9%}RY#t^. +N-3ۤBPndŸ!C2Ļo8Uv$Z<;D&䖻J:#lJ[FE,媝% %hkdT>"Ӊ|60VtwiT_y6TBCNn 6\Uu#@A"ةc*)B>~ t>1$ގQ]n%Lfⴧe"&%!Xvkq<=)0ԑaje;[g{oXUoRo?GWWB2"Vzޚg:-$U?4")BCwߺtPe#M&x+t*hOefI7b/:/5;7Sp-vܢT?x^ʷ).Նw*6+'rORi_ShH! =9G{^Epw H\ʷ =j&DUv?96h$R*p[:-`T)3:O?q718ԑQ*Z܋+- /Z.dwnP.TqH<VwJAIㅒiܷ=3@"yS 7D OmXMPi:,r.Q^3azkLQB:fƵ +eKig% 6ոxژ5+4JD&œY%?s#k'[PYAڃf(RxC&Zxq13Ē`]m9~xOblyc'[DT58l~F+W}\E m(k}m2{p2KP%(__9Vu3^hxʅMs )%/Rlj/'Rξ`TWDyXa=D%ٞ)dٯVZBzYjS!"zѭJoUZ>eЗa$UTbP&J'b&B/{؞c_w^!՟q~l÷M90ȁXzEIT۪\Vucs:eP,D _٩ eOQ2]\@LFq\eI ] ~eұ$|^%Wݑ{gK MrKysgBpD+1 w[:'- M r1b[6+WT$vP#OQrKMKw8ri4jhMBDFCȑTvY"// e.*$д3;݌ߢL0'=PCTG]\u\gƪԜtձ#9 +QmI3 endstream endobj 119 0 obj << /Type /Page /Parent 528 0 R /Resources 120 0 R /Contents 121 0 R /Thumb 415 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 120 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 121 0 obj << /Length 4837 /Filter /FlateDecode >> stream r/\ 0|زv̔ <%Wio j!ȩԝ7!pɜxtpn0YT-/w>i-@E@I1~.M2Lhs.D їyxu%V:<Hz{r82?{ p=YVUעƏWc&B".\?FX.?I.v^?gq{y\q\Gd_PioIҰrf\R>3f0ڇXbv( wWB7(0%N} v~Hފ@yP~&n]Yl?ŝE!!}+Hn#;iέ7|;`m-*uA ٔg %.8klإ6>nt5MzB:c >8j|qVUUesFȑOQuxFD\AJ  3%s1_>q[x0\nK\Duє}=Z1(.GP$QsضlD~cyF'n"sl"v3G.TOf+=ULy>Ɏs6q0|H{}a'gs\*B2OPd+R>&< 7&h '}=ŧXI2:R UMWހħwm`/e#6NR}nIq̇d,&\>Ob+AlVqpJ?UqC'ůpb8(񬸰M9lq7T2K VS)M RP:Z+ԏt*$d𹴎C]aH&Emgh섹ߢWe"?.|t@oze}tH\Dg}i&h2bD(4˦/x{:\@{q$[~ N6r~v]_jb$nsc ĩOm9n*a{Hϼp S4ieri {u_z͐e&9,|>].):&֭ʼn#+l'M` "v 9e+9~Hb T*:&п%MIْBbGZ NTTiJđ]pjǮaنY HH W_HzqSL)bG4>YθZn;Bac /O}#>9/Wш|GQCi;̧(!vTv/g'6`y LU98XEr5WuWow7%41I?q2 NݦKx) Qztom:yS]1۫ݻY0vj9 وq`rݍt}|`[pc\\2kq/cbY|ͦRt@\yQ)#O X!Gum_Mv3.`ۙF!]s2Š/xHv6wBZsFB+ nǢw VMw Huel VsӻJ | \͵G#`!ȤRxDN0N@ӡ wձh^"(h(W?g5/P6@ 9 s5PO%‚GFB4j![YW˕qكY#z{<<TTxhwo(~20}p Oea-PGqܰ\2;SCX._',l${Rt.!:sj"s 3р|`>778DO7ijR-4l4]1۔]' Fؠth^XX6YK?-K e) g6 ;+<]kA*(_O=Χx`!yX}X`O2a{E6ցEިA3J;jvlg+Մ]EVBt|R/TҚ!m2~3!HSæ/T`$Dy{d,O39*r@eLʾj[Jn`Xʨ3y#H`nF]0yTCಓ,@n5.z_c]QuM ͷqiJ1 걋/dtj_K 6%X<\dRž}v*'UVs)GMcXj/l|ߑQ^Y&̺G 7%3N@?z0 ?%Zˈ`la[?#Va9d7#i8g~2%ImON_?7QGؑ.nqD/NͿ;l{5*G ~𚟞zHB i} @Ov8\1sT.KZFYTN^Z&C_ G7s>5,R( )b}\b|aN-^q/ -wo=Uލ PɛC?^@_ѯCaL1߃1rC8\fAЛW4pְ_K+r(<{ t&=P(PpT1PM^?}Z;XtOn6. K)*׹WPygΖ="Iw0=үҦq6^ic ݿ#hm<8%j9=L{I^CO;) ,(vti^P,[*j6ABj"喒:9s|莐ô@_gVsJ,i]6Xʫh} m)iuYdE^aTe̥?7zs` p9K٪om1xq U#fci]_!1Bn hMX563{51`׾RxV~2Bt\o"ή t/։YeXLPFcZNY-ɹbSuNfr 7g|$/l1d0ią|Ƥ[E=]W<]'ǿ#eH9NWlM: EX,so g` -%mbP:)yT tv0uHo;RIMOR*l=Xmj5񽅻jdNnyml b~ϲ &Xd!vnuA$fB3b2+$|_-낁5CrvOO]KˈڃKė5/!fYP)ءSH,&HкElA$BN ~vREvSE0\ Zܴ:~ +Tpyq_A0w}s_,e7+(T>wM8P?C[R\lWp}C],2 i)wqd;M Rqߗk2pZ v5S[q1XN9-)ώtihʖ_b d_v endstream endobj 122 0 obj << /Type /Page /Parent 528 0 R /Resources 123 0 R /Contents 124 0 R /Thumb 417 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 123 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 124 0 obj << /Length 4384 /Filter /FlateDecode >> stream ZHY?KӜ/;1K-_dV;^n=Y9yK Gw?ti 7}M&Wr55[PJc{/"#1?p>‚ˣ.5IƋ#T1&{)~z1)VVX U}%cF<~ؗbl<.ϰ6۷NE雟ェP, 8ej'ڑyN>+XJ'tKjĥ)͢;N6 WTO@ RUN^#dS? L\iL]R ծ,$.p(e>~ T g5tv~b2ظ6rġUh*հo`;}<*f% Lm hPwyH{\ξR#,Z{QI}DGi,m@ @IE6]Mi#3rCOhK8yHYcjC|d`8+a:vNb?Np:sLtT.'f6!/]mr?AAr!EZ([6ca;ìxQ%G5HȻuWN$6[Թh>>8]/펅LbGn;U OAv_Sg 7suqke(dz'ÞB I( !'{+⯔` X v4MV .s:'U #ulsC_?&2^6%+X%ƝuaP:).Xb wYLZޒQ 5@f4Qx"Σ]в5KIJfՐ=χ_Qo's;Fӆklj^ \<5W@Y;>˕g~Xĺ~Qѽa'gSpSb@drᴯ]i;q-G|?w~X$YzgB5"R$4 dl4 ;< LLu^ch17t9QU9=3HlM}@\k2LiϵH>e|M8cz@ a|oU\jOrQȝ~uuEz33U&33`~!/2g%uͣKDDYJ\Y|{iӝsMq(ms8Y :QtF4r@uGS|TŌY&u87+UݎJ$_~N5|cn_ ovkA]ꉷ9>u7hEbq2 naó(!Вncx!70p-@l ҙ 0oq"IT71i/Վ8&*3Qvgyp rܘNZwժ'1+y"A ]=G##T`r_`-} 9{{/}/na֤W<֫=wqf/oUΫ97L7}ƿ?MK3\P0]pcG1y3/{ hΟ+lRQT}bHysl*;7a(Jԁع7E? )_7Pl)[QJOŢ?5e$q[#~KʝEuX1S"\2a_ q0sl!0gĪ\C[h)xA#9|UVwc+XZthR@9h},54JwͥwsY۾5 l_)a(#jpiAo9! #olna&hN;1S6;%hZ͍ p6:㓭J.zn#&ۓ>;F){Vʐ@+J)2,N:cB Z#G@Ih/b{uWy/ _ 2>H…S_OMC@HY&_Kyp'^YCVmʐ냤-*pŶ ]~{=1օH]U5p'8>)kPֱQeI'U4+BeidS1eld[>tN 읋PTC0qJn4\ $m [P9GX* `WJIcN2(Ʈ}uw/s7g]Sc߲B+WΔ1sD́й>aZ+g?.Lɧ6n;Ma -B? \eաm7!Qf*HVۺmPz`mUh(Z*̛٘uAͯ쩝5t:Lc"#CMd F"3AڑHǥNsVNlS\w%ɯvtHSPo?ͺ6bG}{;vlzT7Foju]C *`>\0R>%hnE/Hs5*rh{(tXn @y|Z>B ת$%Bozxp'O#Y0Md6*Kq4މ^c֬]!O ˹cV]U"6]\ڄo=OHK 86D&l7 j(>  O Gڼ_P6 ,UohGk:KOE@Wk$Usư~j+tO;*e5uNsAƽi:C? > endobj 126 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 127 0 obj << /Length 1295 /Filter /FlateDecode >> stream Qް-vYTk WZ{q X>ܥ @|0l5:Kਅ`bGtO@$q6|EAU&gVQ<-]?򘧃k\1Scք୦>~g j$k oY DaU^3Ri{8@iڀ~ye)cU&-_k8GrSoAT8y||q)H 9X@KXk.2m09ʤ$9U򬹁F?ګBWb[ʿ-Ի[5ָ貃J?ye^h[TL؄`#^npA3ߋ? Sn "ܯfy.rJG;ג-ӕ@? 6Xg1Z|PPthbJxer:c)$3v5hpE^t[p[.(솊Ѩ͡>+f d˨|-gd18&Wx$a:5Qw?ʜOh /I林R@qd=CI-Jbq~9VK<%L $ w\AtotVU86qqL?sxw >Xs!ٲ2ơ*if,0TdprYsQd94p|cDZ:ZƘ-HSC/X>;[4' D8 [x B9PU5AobWOd:A,/}e냛]wVfc\)'w_& 6˚K=; 첚_4ZhmnS",r@rI=PW%,?9J/`x2*FЪ?a{wH=NDɥ-"AܵC̙~ҧ IZ5/fP0f`D@ 0?d)6٪a>;?>0U)w_w'MQ/9YhO >:d%Q01Y*YFڳXtTwNoRٕ|Vީ=0Z]po՜n²1uYN=6f@X,I}}&[[i }dcE GW]"6q )/#T4;Mv]8l/Fdt CNaEk_ԥ6~[ 5# endstream endobj 128 0 obj << /Type /Page /Parent 529 0 R /Resources 131 0 R /Contents 132 0 R /Annots [ 129 0 R 130 0 R ] /Thumb 421 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 129 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 705 197 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 130 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 579 196 591 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 131 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 132 0 obj << /Length 4819 /Filter /FlateDecode >> stream #QR7inGpæz9N5 hUcgk=)3]vUpdG鵇g 28# !L:ԶÖ|2ݔK8@K'[d^mQQ+U9զ7[9zpxH&[YՎ']ּơf.$,n_v[p]?5YfpSWY[wK >!7՚h) (\1Ρc`x)3@Eȇ ːYp JGZ^Z/KJ~5'deԜI#NդC}4 WeSR{q,k%!uЎ/ P~E%&uu{ܣc[.:k4TvƔ.mRmĬU8S%8>CT_87mQs$G[.MVnjޕ\3z;rCF TY*iaO58fU t]c n5v [?O y,) (vTvRZSʧ>JӺЧ'#ܬ}Ed=z7Í; 3G9*]|qǃ}LmsLGpGZAuȂoJgP[rDؾ=q/uV&H*[q(%<w~z3/3 E`V=Jli*shG.j/V׌1*72"8WqNnNt,I_!90hHi2"jߡB~0O%(2<.G0ѳktΥramQ,OV+jdNdbe3! E~ϏD#ϗJQKl]S0궡IRi9*O6{|{]s^ۢ6SJYD) ɵQWB ْ3Uv'$QWncuoM|P3y%QuY>9i7v2Eҥߞwh=pK ߩrf\ Yά4bPMlϜN:xèoТ/a> n2(kgǵ#V2 _Ɇͨla &z9o<4jƐhe6VjHjO1.R~12].)Z2wcs7NvR-is.ȉ(y} ۧ\`['^Aj݀O$pb1ٲlX93xqm9>\Y_Pf@FWd-[??59JfI|L7GfJ 6DS.iU'4KxM_ 葦{{7,~P.3?/c'$1d+NGŌG[ՙ̉u H]ONI|J9K&9dF9ZK0>6W74ji?B1+|%G,U K٭^l?ۺq[ w+&6}b Ae++RDqL&ʮضO_&.< T ;}+7[^j 'sËFghk؎$R3?uף^OA*pČ }/ď I= ݵ 㲡vIR/[`r8 ~׵sAB}23~93US2ԋx7[ʍ'3 5-:`6ShEm2J mWNlp$U߫ٓ6D鶾Gu47f*05)Ե;mH^iOUX 4vLAh.HDj9E⒯TzuNh=?z _!(b| :6^PݾA'WƮ #ĠO _*67Tu_٧g%QD^eN'/( V\+H?LIZ|JI@p*RE̾^\wD8pm"+'r*0͖zـ 1bJ/Nyg !k7?<;$q4)!]u'O3 SWtHn؄7-wb E $Sacj`>CCʉ41|q/sI#1>Y7C|'@\YkP_vLl>"v}(6 7(󦸑 E 7> Է4EKSrQUbAٙOi .XkvI SZ s)D.Cu8YG/tp/z> jϗ_ KzY /Utam/~té]Q+0L4FS)Sޣ3-U0Cfdxs6jb.7\<f^)_@L&z5\t:?Q׈&[ Gib_B ~XS0a&KG]2|p8 9SKaQz7߮%FܺlaX0ϊG! z=gU5L5s FB=tnձՖ91l/_lўӮ[ d =oi2wh-Pt!###Ϛ€35q|-)1䈗)Zįmв5sbBeѩ]+ʎ`gR)6u> i(H.LVߊ?-(^r> endobj 134 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 135 0 obj << /Length 4942 /Filter /FlateDecode >> stream WQu>ͦ_.s.^ϗbpXr]c"gEeC٪1V[ AF,v羽`9(|O4=͜l6Q\ҳݞGS;O1Zr`w$2xaXkf̡SdvM`44}]T}(EB4mҾ޷Eo6M%';JO{szbm}0(TB&бY?eX@jmD<9+b9A1]lczp3CRqRPdP}G:psI=$#D|lx=Bܛwxz7π@BӤfJxbEcڸDOc XEw|œo ګ ܒbe0»wM3m(JꞬj\#knSS"Xѐh" y5 >8q6-P \">s]U\nhk([,9p)jiZZ9G<[i4 &^yd7ShZer. x}0UKh=^yLi 26-N,`ĘnrS6Ħe 5;O ;d<܇f> +$ kOnb%h-#޵/(}R(vK1Yԭ؂^ԹLy\3 PIz"UcQЭszV _ XlffyNB|m$wOh~R4SɹƗ C`jg]|7Hka =\ߢ`Vݤ.eEXѦAf Y?n8mtS b?K`BGA|ʊW062f`o ,wרwp}"^-`9n\H1g!'zoWПс] ^#GwP}z*%ǗҲ7mAt,z5՚zCPB`a)B wb|\?p*"y-& i* vmm0~R0/8DJĹ#ci%W&f$qK".p6 jA-4ĵ6ٯr#wsٍH:tn՞ r`0n"ݡ%{R ֻ\AJm*<*:"5aNu x"QAdib$;ԭӎ/FqaVص:hvS꺚r]6R'SV L1;#QL64ZdKB̥p=*q#"q9BڵՂ.#uMJ]_ϱ~8V #6'h5h~WLcՅR 0T y:WaWZ'$،[("8%5ӕ )NW1{sN6Q ~%$H\^7*l "h'_󊾛ĽZ aEKiA~M] 79KC8JP}9,}Ҙx!}8 Vj[Uh"\8Qz.@jp{Q*ڗtjGEǗ#駰I֬{qHtj{#TqOÖʐlPPo \cX7O054C Nl6W._J ##2>eY󙊎5$`/91Aq}'C3Sv5Qǽ4O [s9nHV}B8$ͪgC[h:⼈7/[H_-}~w2B;d=>6>N5Om}a{\>Qj\n[f}/no}lo Ɛ$٩q)f(S;I%5\Gr{Gd~'ơsty;F>;uqj&K@bU*& /f5>G m$t 2PlC "NiIM*e%}-*D)(f|0~fmTᒸ]d6^I>@G,3` rp-2.@)x@3lS}Yt"rq~,Lf*$q9_JpVs W}0wr-nA-FZG #7s*ڳaM;$tU[wKp鄎[ <CJR&7BHWIڻdܙN|Eu]z ufKY6]zX@+*}bALe0Iv-܃}\Cmx W:;hD2cM,;4w4kc%GĐeH,_(ǔI= sY=%طkoWCKyz6X;6۳=icc *c\b\>,rx<$c ݄=+*V}epw=Dl!\%oZ(j6v (48~Vo[7}vl+q%%3=q%B #+.' -o|^IOV^izRUy:B* y@էA>[c6a B͑0p]鄻y1Léy1w~ũWq'/`ʀ!,-1hNLl 4SJF jr!$fOyj] ^'s&KqcI|164vG(ftڥ A sʊTy,˫ { l|n軭aL,:(Xt&xZHOWͧD>Pr1EP%0RuLB jua9&2&%z3Rlt{Uo!/TT^M+~vpL@Gm4 g0_԰OD;ʌk5&dˠ@! 7 jJ(YKRsolr0/H66.Ezv8_ d8C,W@8}+dNlK'*+uI{B%ittA~y\!a2-}GRsi="Ym!j$o߱cx?'5H-ڃڌ CGz3:vއ8(qZTCF#pqR+<\"{FK]p S6:%1> VwJr&RewQ7k՘o[ F {'0hI + iWuˎhl7{s x+t=!7S{^#ڑ9Ұ2W%jæYx`)S֙8G31D %7b/ap5ÜQ͉9Xf)pm#oJ1YgKoԵ%ⱬ2F֜V&̛tpH_0G# k)a m%o%;"?ͤpKJ2'W%z8m]ЙtV $VA\.{}P.nNZNoSud|VJqdS(n*ٔёFѨ^72}{ҸfCއ1n*]ZJM\pH2*<*Bs像;`n1w~^õަGw5iQJH.*%@ۈ>%wph,߀!@ƦY endstream endobj 136 0 obj << /Type /Page /Parent 529 0 R /Resources 137 0 R /Contents 138 0 R /Thumb 425 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 137 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 138 0 obj << /Length 5361 /Filter /FlateDecode >> stream  NR-o/@f%Z`q_ZHG?'٤$qF2 x^cz̿dX"dNX}=chX[eu-Z<B=ӗ/[ b|wJ㭑;x7wqOeχπT^D!$5)I%~5+d [2gZ^C/LԹbdœY ?bChW?$8F1R3@\5>c1czj2$#3]#|p=whz /xVt6~/k3FSts.2|b cҍ5X|A?+U /njAG|2CUۚ%mu-U4u72mcb{{XRX-]C|1Mnpzl;p&5Sºc֨|ŀ67=ae i=[zYͥih<4*J& ic!R>3%^m |-js#Vڽ8\}'qH='N5?asc|dEA stYV:+sQ_6^<$uƺr7xmSn#ta[Si3sE|$$_kUc:͈z9S.(f f"&0 %k wgM E{`Sawqz{NN\9YVU1ӄ-[ ~?zgc:C%Jg5Q掆sź[R.\Un7)OCqR TXJoЩ4YHQBpڝ=pyk1N`̅bEt'_qq|fVS kfBטF-`^Ծa-ߣπeZOZ8SOp~«9rsʪNa yoZK;Z#&oe(yQ3O6{vj,S 5lXW* &mDk`Ϙׯwl/I P9t>."`v;?ėFF ֩gr̈́:\p!_,z~KЄ :muv1%Was$U- QQ?Mfj6уr=y5t;;#xx%ehc꺇.(m=JS><.68 Pzx .m\d:FR f?I`ZlP`*C :SF0Pn(1o!nJatLܨMVj>C _~B }qvj14 U\GwG՜M=9H#0(x3xڃcG<#tuҨh>, |ˆ7s\m'5D>wJ ׇF?R bL &5nGfet6gDZVA #?lM&+: 7*3שA1!);j_MI=DŽpRJ'=Z,yxv[n$_ n?)f1Ǣפ=q5~8Cϕe3%SG0o-u\c.]$9A3ф.ռpdX+ 3VظRV BơN~Δbd&}l36?nOhbqX<ږz*0̎L W^ Lt?2p`3Zz>eZR4KGX}DO6rp/z 2lG}rV&PCIắZAvә[~k|Ƚ;g\Xtt lU٪Q])JZftv;,NR#=(=  ڔ{/Uʼnq %+]*eS+H j8Fӣ#0 c7Xg|NӖ^xpJliLWAy"ˌd/aG@ihY\_s$ujK'}fLBf qP"̈WT%y&ɿMZ7D.PNjB-s^L`֠E;t *A4-磁 _P;BMӰy©:;ҽ"E;uKZbBHY8ZcPLFbAkSٽ`&v암bxp M"u͉ӷ8wha;, b[hAҮz\L4@_QuXgH>_j^wtKN-д i `*R5SmgSDn&eV2.Ww91A!L^0˦'#Gg#!<׳hpfzŒQ\`q9&E&ԓroQKӀcbb!p!7i:#J jYk"#7:CщnJs.ps͐3%!ȴݖE2wvK+i~:ڝ*+YOs+2|̿!&B&t˷S3z bN ?vt }MYsocKt̿dq5^>uX hk(FAA7ӟ8)^3 ;9Ju9HEa^IQI(榮RDLFFJP]FU39'5GiaO4.\3|D@Or"T\Xw];Tt%hFa+$̆2޽rXc&|rScwP(7fC:(XNߕ YD8Q6ǯ)* م6.?1ZCS88d>ċK^ߋ/>){Dʅ[KzH@E("whRNe|fjW9r.*T@R[{ se9Ć5fgwԌb$@iYNh_!PMAAMNgjO@u2uN%@F,Z:2團r A-d2[5Yr$@w _uA7<]~³H2faZ{ 2^Pd TzuÚoNyK75xk5*5j5ts:e:_R Q D p |v,狒G/ r(\FcIEuRW)JO1Қ/?"k0U=yueO{:1]m,#lYPCsl~ B $aa֠ m2ZO,p)[ !g"]}B3] a pBMcJԛ4dl8P|ܧQe4KCNiQ ۮ$  endstream endobj 139 0 obj << /Type /Page /Parent 529 0 R /Resources 140 0 R /Contents 141 0 R /Thumb 427 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 140 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 141 0 obj << /Length 4313 /Filter /FlateDecode >> stream o|JmMId\ձGR#vX& to۹( $ sc^G5a|۱ ۼ 'X惾$㭍e^c >(mtyނN*`w5 7 qiP.߁Rp6Qb|~m#Zr0MȀ. xr.>.)#A:MZ)M]ӎwtb[>?-ٜa#̏\:SsxY>X̊xTRѻŤK6YWY{10 Ñyϗ)ynzEkO1~^Y&Ͽ 4 ;2Oթ ߆GJ0ѹ_ K9 &!bBQ' ca]g(՛P{ͷYW\~Op0u?kuM`! rx5n Z KHP<  `Z>נ#И#'Xlc9o3y!EA],g?/ P/赨hwƒD{ͼ|C K3a)1+7u n!d(.GO/ɼP@[ ࿪&p)r5M;Ei{e*}d\y$ R[_+Vɏ _yD,pZ oْWne^`LkR?'`:LJG`[*tF迦}{]p&Fl'bC-MXΒKD)AG]m%cxFb׿O0҄Ne0Mx<?nAuM 3}X^1h+Ψ`J~/4{t ^p0+HȔ̅Fƹ/ָ.s*!sVM}S{\Ym!'KWm)އr ?nw;:̧X@v&^dCFjU9SAVۢkC-[\U:`u㇒W'R.G@ٙ'X12^a^~ ^{0fbE v t|;$ɿBf!q M $hnk\jqy?\zO[9|:hv|\J!3A u&Db>&iϙ^ը,dqͮՒyXǗw Vf+.A+|/*aҵ?pő|qM/{^EgH "?J9 "-cj@aAZ1Df;FK`Hz9B {8`ۿEgܗ]]W^mVf:Y՞%vR+s['2DeNronjoq@BԌNIkJ\Hfp=a||[(R1mI7BNir7x*ڐMBY!ڛc&֋?dkh4*7(Xl/I6TENԱ5= <2T30Gv\F $.t72ǒ¬7Pbz?sҸ }nQxXAK[4N&jvmZ۔4QelA빺?݈-asƒu? Y|s"ڌ%g Ы&$7Ojd Ip0`C6l_c" EُD8kIogWW$0ymܶ1sBZUj'(+oD,#St95]%L+`9iO?2Ԣ0%R#;#(Kîx,VbU # !#TNh]+  eⶉ1< WШ5[\C1s&f&ɺ]-kL/opLCcikHO( endstream endobj 142 0 obj << /Type /Page /Parent 529 0 R /Resources 143 0 R /Contents 144 0 R /Thumb 429 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 143 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 144 0 obj << /Length 4556 /Filter /FlateDecode >> stream RRn)wOR/0d0VB@M׍ݬmeI'Aؖ؆`iE6@M[YJ, ƿTpD3jo/WJ9vLt6хʄ ՞ƠCNGs۰w0- DMAi`7^SP}::<:Mlds=R*$&Z[<+ガ YE-Gހl2HAeż\Zէ9ҡ;^tO_-;JvrN Qxr; 6G:ǥFVO* Ro#$`ޓi>$rny(&fs9Vfv +g2@ɉ~U:';qFjhq/ '%~gϼ9@'m8pu1".Y4ZvZGO0ΝoXBa_4+d~?9)[Cq1?f6W7aR^C^Iw~9*zS#ND<%)]$ r4,^&ӯC쬠sIdk+`L14yj57RoRji|whW^IUV{J8Ͽ6.1&5(T9)$1/ɁG|Jrx1@fV)X|fV*Cu!G؟EՍ _HW@DEVOGzC=NC(e-g{[F :2`Iie>zz7"Vby]Pw Y-)Zos7{ ['P:Cmޏ[ϞizL8^4 Jnn_n_uvymOA[^IA1ۣJKm8po҄_RE2Q+ygx4BiBdn4@z&O- 7 z$խXN)wIm(EP;i"w=Z\ăw˰%=œ=ʫ`Pu|D\DU4ShY BIH]EW@ʣ@q1ģ{28)fЁvzP;S _J;?bW,<,pr(D!8XJ;d3:ysݔ\.^M8}lsW}D18ԗ ѧ23F̸x&5㒍ҐtAsz4脮'C~?ǹߣHw!Eh=1{ ŚCؚG=hİ!rPH 9MbX0|<Nzu92.DCd\_(bs"fqivɫN0Rу ek^>ZˏЁm".2ן8X焛Df<1KC+faPwM tB>#Z'QEXZ}In1X6B%Fr۫=}u{^q=2|cC]t0@mb,Z<{u}[C[ˡUa p >ަ`Ƞ뇣J8- (ׇr5c:6dhxjOƈ8˂w KӁ*RwQQc9x(o\M06.3?/mDG酆?`Xyy O蠯%rOȲI-E4kuc )DXڍ4tkڎ&#dϠ_y!f<~'<-`FS>$Ǩ`ȯ/hߏML4G@"=~-(h>0LT>E^zs1]lvmpw#BV|Fs@_rMȌַR`osed8- ZM~%l,AFTVq N"e6l[D50V/ ^ີVdZxo\=Mm7[)Px㍐ KBZiv/?,܋n{]wEk^ˏ ,bi$k0rq"li;>nr;̚ƪBh$5 m^P ][d*k+5P1@&jTM$k3+T!㹡)?hd`* .j(~1Sr!((3}x`&6]' A#vC#[]wet>\Z8jhнE~b[I Z/.=#i]Emrx{H_m0=W@O)0pscdomlOXo"l<#jOf·Vz=mgo+{U~{~f$> -M{MhzǦ6ൂ\wE^lSJW\E +'(Vθucq|3Gh55gCۃhȅ]>thH.p{*HJЀnɴh< 0fe?2#=&b:)1Zfg$1;# ~rU FYe$WUV}w=ɒuo,D/iW9xfi{:Kx5jہ{4`A A#o6Y)HMW"fpbZ/T| BQݚM!<O11#sB=ɇ==vݬ@J(*X? '+rjB-Zl8{cQ% 8G$ưJ V[vSaS*м3d$Q4{Vp~+B.Grto1ojM>dJ(XG?n|}LIl ŕ=q-;Z:hItţ;R[AFMH"X.˼ k 3·} C_v50ރ endstream endobj 145 0 obj << /Type /Page /Parent 529 0 R /Resources 146 0 R /Contents 147 0 R /Thumb 431 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 146 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 147 0 obj << /Length 5378 /Filter /FlateDecode >> stream bK4ZÚS}g=x%hAy ֣Z!s } џ};vR"/}Ёw_q^VTYE Wݐ'X.&?QxtK 8L c>-O} bH4Lm EJ|W`[q8/r yt(iIuoz ,^WBq&Le9iN$%6*>W"-EEPd;,0KڦVMxvI9JjT#dS1V/L7g+^1`{P*OE&JXD:jW^>^'[ s]sq *l14Ul j$mhn. FRhͫz^&g'h|LLc|aֻac0Խ|W8g#M%/+H2+پKixY9e!drzh⓷Z*9d92E_/[dn1,b}>)/ݓL] Hՙ  ܪI&TS`^v/m`hs5"? r G{Wz f[kЊ:RqY.K9PL[ħV.Oy:,K5V9{oymYr}8DXljmYsSܐ Gx @ X "w ^٨?A0A解$XT_P֨_^w8vy&++1Ǯ WsH7Vcmf AֈV (ZF&F~P!R3P}ZkYذ^h3P \zl j\ӘyȬ͕tt) .f>Dv)^cm3u' i~J쐦]QKT= ;gmC$ AՆJLcHFmu8{Q˒N7\$Q,X԰Ap eZ.q P8H P̦NI+DmA+oc42Wn^L=]Wa64|+ͤڍ?n94ݺgUawT>wwufT&߸pɁPm^r\pX XE*K]pgBHb;`i+47 (^%0 .7KҾrMNve V&o(+'gI舖rz4Kj]s!v_$}{[`H!̤Leȱ'8&e[`I{Ώ9\/lkxC#ջfqQd$ KK&R+PFsw6[˦}ۜ?B l65ⴜWXI_>&120$L!Tb$4gY]2Yf^ N {ț }*mc߮OdR@m=&. i3hבS5iF)Mުh}@`$ /oo`9wS4P-QW35]Z<hܪQ.VicK8"z5u"|eI' ޭe3:;" J 4-gOsN 0^*XC=isÐ{!mU5ғUUf~ L9붋ujt7tÛ6ΫQ}][?]A TWV7EnX_c-Ap %)]A=j)H_Җ<;'HԨG'~.@+G0`oBOxx9H%eSl 7-y%#\ޫ w gpW GSȦalfx+ d+KG$Tö*^F!DplԷVH7'wiFΫHNI?_'af.ָ?c?ntIZQNPP]ޮVJNbRI>=z`p^ }F n-K~ҙ+# .( IІTR*j֨^j7óފ^[#D'y(.3m' 9GrOnXEu) Et߸>缩DGZgFwf"I;$,ccvLdٳ&~QCicR/T8d!9WPS)M<8wtɕ,iկQ}=v9Ŵh9#!E$O[{0'NŮ2<$>$"ϡ"4dBGn(#DVɧ^j9*VgkVo-]Rكhm~!A_̸gtK-HU̞`C!{Gj | r#WlXY_CAۻJ2Q(xBCX@T@m# aȝ! ʨq.V'lRjשcsțGV3'I5d^*)FfUYN̋ k$vp9`{6Z̼!a{ m*'&+A<2OH8Oh Z# ݫ~xE(ZR  e=z3<:[_ h#Uvy:b|2u(*4;`utqkv`5C6q[t(N<,Vd פ&zEm# $2ݡqUk)\ IJiHo^H$߿_n3ǓUwQN mJ:!N.[m=g"~[My&Ƅ=e+~@{) \+׭IgY {^Vxq|M!p\߆7뜾Uz7Ќ92:\TPʼn-KkBfO,Y͒#T1*e_0-1^x!Wp% ꌯ| Cl_LLd*|;9m" X8@8M誗sל#gZܽUڙ29:+;ц۹yg%5r=Yep$"3WLØ(Ee~ r`+x@Y NfkrָJQ" (KBhHyixx=ߛ,RU`sOG(%P[Ki z@Yk<90Rq\v+=@5aqS2_-RR0:{t ~"س5o2m!{Σy3Δ泆g0Jm|skzi7AxdFYhf`)J׾^gw,ˣS/R^\8byj'1If/~N7Db8z/hѦTH4%|;>(-|~ ns7<ͧ4(9t41'4ؔq'gϖ2]"L85AB{Sm{69M~Y:;1[9Ǹ}K8rU 507bEG֧#?ydDkՄJ\ֲRZf_\CfT<~O-ojc'&6oBlʪF%@sڽu SIm8馌Je^RS~ghx4XLN{gFko C`ۨy2IQ#@-GA8H,5S.CO4oL-x39U4[BjxÅ.2,e wۼ(D@?P=?jA%xJ1FN+v.GYD_R,jTK̎Qmvȳ3$\3eH,wCl\93O^X GMX$XbeJBt;ēlz_ LPI~F";Oi*@fdBc/~<$sđƋ.+ kO:Z# 9 K]!};$LCF9L9;'i,_.=#U^Wq:7MWK!F)AY\^bg\E.d.F5 !F.gN d0RO^SP uTf5z.[8 ρz[B~|Χ f+j o!l 7p*r$֛< nXЊ/8n#TO^9,}z?ncioª@-l#) p7_3 \,y4gu[xkE`Eӂ6i~,Y1$BT.PU~iaՀ˯$)= YR+PG0sۤN-=ͦrSs1-txaJVYՀ kurۇOzF̀ endstream endobj 148 0 obj << /Type /Page /Parent 529 0 R /Resources 149 0 R /Contents 150 0 R /Thumb 433 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 149 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 150 0 obj << /Length 1380 /Filter /FlateDecode >> stream NevAL?mׇő:U^΄oh]Q'+*#((Niݣ6ՁcY\z^[Ukԅuiura7`*.1N|srlSf^8!9:w*Z]vM򿴣}f')6yӋxx:[zsn?td@RrbEp­ p%P֎a %sHt:jW`$$oVod\dJ@b0t.GG!j]+UM>äp'ʦs8_˯8t{}A G#/~G07 ;'w'rSSY>=dϱvZV-K90lWY9`o۝SrE*vo:oȔ[t+OϢ2RG"4Se `cKDJ8S7eQe <^Ь;xߪUf8$m[tM]lg.Vi #FHro>5BF.t 4{RA 4 Yvb| 3haN5w=]kU=  2R_4w.257ت-9g3(eC1g޸}ing ϗ6K^̼a I`/0/sD` vpBV> $x1w 2u}gF ='AhhBa_{ϒ)&p>5n^iHb|o(Ķ|F+_^ӍZr W3; `SP_yĭJD !bj&DFo5ՙ \V?#GRzȣ85ĉxź%='ዮ?A&ߊ7`e{2:acvbԿG7½A/8od HSi4܉HIhiP^@*lNGgcG> endobj 152 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 153 0 obj << /Length 2942 /Filter /FlateDecode >> stream Z?m~[ (ؙ$L<"@9gE(ݡscSy#uTYNJI!ly+K?;NHg (AzY 1;(jLUN݆Hc $xG;wgЧÉ^`ŻmT-D_(WAcSCTsP}&g9bZٍ.|xdy~ kmÑPB\bqْ%]kekEu/Kl-~*XUtN~Ktyճm[\;KG^K;ͅj&O oW|,$㤶mGuqNt uwHCH(Xif+ - vٶ?K#NK|jVN4fN.Լ&G[l5bp%2)fqct!zAiFAZCĒZӗρg♌R}O]Z~J2ֿl'?s89?M(/M}1m`}H*0Oi AP<*|[f;ꨧ;[P*?D:7z )<[--uOA _4I`<({ ()O7z,2^(<DO|OfV6TauR knam :lxyY5?|{\B_I+D/TvUBWo[&UK|YGU$6uBq-řdA/#?g R6 'J B::qymW@[2N7C z4+ay5kPrYlWr 'bR7 8oۀSR j4G tQ'&ѹވQOPD~j0@oYFu>!hHj w2aň@p'#5DniK:R FohN0y' 8'GnN^ L7 {pf@u@6 I ek0`ZjEƼzTQba')$x T;N6]:MRs*3XK³F ?Rfnh q@vA[ កxa) Ğ;TOf赊Q _N$H#&OFb1߯*GnGb*rihuآ݆n>Kd?ͮpjJ`ai9|ɺcB),S"??*2oJvlL ":tծ %񌓖h+yv VPJ)cz|7 nLh;}*-Ow:߄2 Ru \V52ij70B|38zΣ38߹"G'zSp[{ ޾{gݼ ƒ*WF:EX endstream endobj 154 0 obj << /Type /Page /Parent 529 0 R /Resources 155 0 R /Contents 156 0 R /Thumb 437 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 155 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 156 0 obj << /Length 4418 /Filter /FlateDecode >> stream ݡnCJpG#& |RP Qbd2k.#ߍ[<+OS'8 M#4Ng(a l'w'OVCtoHRWws :'MZړ!I*3[o3f^JG1K@P62zZ.=W[\Ph䅢hpTX9e9+>`1 +$ M|riT–M'2#X20{@΂lRH>7+9nD=#6z~,.}N̯HL$yDviAsG%+$7Nع_':1]EΤ]z8ȊM@N`JҎk?0uqcW{H<.t .DqkR؋^q_gVRI\]bE^^(Q^т7<#O 11n )ms qVYtZk Q?9P pRxK?Ɲ옭8q{ <5ȳ4ճKIf׹Y'|_j|xT :4 ۑV`t.RK L\BF\#X7&)z-l;xEUvkAHepR?CPdPl*A!r&| ̐k|׷Xkqkf)Ls%""^C~P>e4RMNB 65%lе/zq&_ ~WZơ=~D1.>BACfE9꒎ A>O]6{R 1dEXRUO#"F΢odС`5pܶ}nxPNcZφ)ǀ^*ȯ<ƭOxq5ENT2A"Pi# PV ],luO_~?Sۭh_3D}.-)A4l7[Խz hH)gW%07MtvIg ;6흅 :~Vߦ4zlg 5#Ҭߢ${f]MH0dI(|U.b-)Zc~ x,-N*ₚ<>Dw;16 c۞pX p2&)lz{> L /a(H5!he~5s+3CjaWE a9̞HH뮰,l hi[`u\daLpP%1?t$tiqD׵Gu /􏗡j-3[@+E"gy>j5Z1/rEDÁ=!7־9*FhUŪNzY r)ٝXlnZPۄ٣&4 _5CDr`.4Ee31# 9 WA}E؟לY: NsЈr- Vr\jusS(M{ATd%'Y J҆| f23ˋDï endstream endobj 157 0 obj << /Type /Page /Parent 530 0 R /Resources 158 0 R /Contents 159 0 R /Thumb 439 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 158 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 159 0 obj << /Length 3176 /Filter /FlateDecode >> stream u?LqR ٫aC/?:G/`%/q!0kA-(=jkhefcshV I>L^l`l&Io0' J@l#l:G +ÍK>0.PV 9ga rJ8;` +9~Y/$uF.%Oy6V'ޑeBt3~guo_)g~!v JxMJ<3z;0B P+\dm ns,nO$TM lvՍJ^HI$9p4XDEZՓ[}` /q`ʅ/%)yӍ-UԴ%^]EFУ^7ϑ4ť+YIfxnpәMS(kё}b1!6\͍KlTcÝqMA %؞m@;Sj83r;p2nnBD%}.$zVji=KkB 1W-60Q&{% :O NA2-˖OqjAWzqIځF5a2Ch'1wB {;.ϙhta ` 3hKJM|ŏ3jpvIq~+z kZPx}OZMY9a<":!n45OG\qb19 =1W0?z̶D=GlO?{ܷ@u47nǧ6H'Rf% F$GbfԱoQW;Ko&+4 JQML~X7ISHnͰEpKj\`%rO1]zj][#2-?~r`L>s36F vxrP?#6Py^OdKIMW/V0;6Dk&1*-h]Ite*E2eKc|(CR6~%8ԣo~*z5| Կfdx*e$ )$uO#rxVTgMǼB͒eJ|dWE93Q^ E2+M!:Nm'wqЀ(Lb>m^ʜY2;g=Hu `/9hs `6Ya< ETM!:ZQ5*H9w~CTWޘo(:8kmP\Пl0bٜ֠3Kn^b U \jGӢ5Qޗ򔍽5 šrFK(" p)ˑD!^Ys&Wahc^V mY$ɚn5`kWy7Heg7Y !uhtx uWMXdڋЧs`M zK`0jD]$]Bp4m+p,5"~B Le)#lֲcjX a p900j[ (Oj {ɷWBܗkҵـߎd* MKXOe;Ί,j{#6s2YߥĜ~8>5WݠcTLc J!d~eпf]GW(v!tK@Nƽ!\C\6Nn 4J?06k^~$#P#"~lK-ӢvTYeKH#?uMEc?4) ]2@V ۳GOD^)# tS  DǺ®) EnQ'G=QmSpjY޴S/gh_Y 5?Dl36#qc%}k@ĉdz^>pV,Fj,&Hkv c\~]HhRJv<,NN>7SoTINږР\N-GOAGG^nѢ?FwcXaϢ^XwK0 endstream endobj 160 0 obj << /Type /Page /Parent 530 0 R /Resources 161 0 R /Contents 162 0 R /Thumb 441 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 161 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 162 0 obj << /Length 3089 /Filter /FlateDecode >> stream T~qίWK|c:сн[dfg%0)}W5"UknΨQ eӈ~'kˠ2;RJ8L\rdkث=Q[PA97R޸%{]k] U雿͏Z_!aD:ܥ%ˬk"h^.G 9pqKj `%5\yvİ1R>cէqgemzr-LVc/q0p%Nн^#Qn…5B -$`XղIsvl* x3!v]ך=TD5%x V*C!L} MaGeNxaHGg}gh*j^`mc{= Q=.ĂɅ@$SÉg'.^ 5:`5V'E@ih r$BBe82# FN&QDsB_5Pti,י,*8-'ĤˊW?&{M&Qj1tzJ7.agtz98ϰOt(E䈭I`BU%dC z=0/buVTPsPDע!xXK3GYe3hG ^St*jOsfW>s(jQ!]:>`҅TyH ְ??]{'nlkT*Zf-> 2ͨIz`<72Nm*ЋcfqJ) BQ<=%_<\luh㓕Ͷ*o݄0x"E!v[<1y r[ ѝ7S)(2ź.l5DʐfZj$MM0%a~QPMm1K,!rsj[alRlk^.0+AL ^3nőpkkq*fA_J&L{G(U,5\o/%jB* 'wZiPEá6J\aw2fЅЍf#GX +.E~d*967Pp /~,HI@U?1a匽P9Vc7A0k$؏ F7|F]ςT}b #Ǿ-DNkDrG׀D4w&d%hC\3*dY`s0Qے%ݝ[HDU6jDڹw@E=Q"/ &ի( )qG**CJ'"":͏ +aXݚm'ʘl0`GF?ڋd߉vᎉf,+`N ݓ'?;@Ǚx"?4 4F;`vISƸcmn@BT=-8؞ r.b8ִL Gmo+ķ{}֞~g-U?ٰp$ɸ^k]Q𽊿_If2% Ď c ̔Yhjm.?Uk2rF\'NV| !EUֿ=ZTu6 `.4x NcK~#.|lիvXUnG4nt(6PVXZ槚nk–svA ck=5>1=!4$S3SCn2"u˃?^J@"}BeF+UZ-ڌ8)̄ tsjxrzFMnNo#^Vn)M֫,ƥ qJ-a.y#gÎd\ MsOo飺o]#a7i܄MZ7PbkT/rT!e {Wd:Bv4REz+//Bv5[0 g&%&+/<iI|ςEVu mY":7]kFn骘 6BoΧ}lh^ 3{Wy 0 endstream endobj 163 0 obj << /Type /Page /Parent 530 0 R /Resources 164 0 R /Contents 165 0 R /Thumb 443 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 164 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 165 0 obj << /Length 1290 /Filter /FlateDecode >> stream kgGɭV+m_@ MX9s?>sG_*0 紂9"*Öhě ;|^$IcE-5 = TdPX #0"BL!_?XlW%l$ )~ C_&"p.WnZli_D%x(gvaL[氂Vێ6*bR/N_QvCDg,lu|V>!!W@Ge-%?;GOb,Rhȳi|?WGJ#{95 ~"d=q"]qP$1Hs!=:~*0O |dS8%-lc_v;fXH y+[4S"hg")J4i=/;gqIi=K KH~?;cA}(]s1E3',ۂW%Ą2EGC7 ܖfb!q(0HyA0x()kO3^-}3OÅ3.}P<׆A!Y#=TJߨ'<q/\[.vp$|LxnOe\1R6Eh!͚eJm1G_c`O*X k@EJ|ף(>Κv@a,n  / 4_ 3X'ϗ$,= 5KD@A*lsL𐿕cok02D^6gy N5y>{$Qըc$|^6K&B ֣3ۡ gI+Xoց21%1_I>RK;=c#*OS0} %ah"gCV$XEc#"F7*/HK`HOTla2Ն*@4h31gn!>%t.0JOb, )k#J:o*k ꌮ;[7z9aˋHf7"L;'>[*խ00Zv^%bR}EQ$ n rnpuM[,,`^ㇰIBn$~ר)#DzE,9*|n$vXTe~-(̥@g]~u endstream endobj 166 0 obj << /Type /Page /Parent 530 0 R /Resources 167 0 R /Contents 168 0 R /Thumb 445 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 167 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 168 0 obj << /Length 3115 /Filter /FlateDecode >> stream ZA0Ax,dzˈ8]b4\qЬc >z ǝVMJF(vڼ }eK&܈WX6Q(MBMÃf)R5*v'G^Db㶕en~{+^E) RQ|Dmh)Y`}F!+BәCz:}Ki8/R1Z9nT vVV@Ӎvz! 0[m@ 1M2/`!Ǵ! H2OB}֊2OvX~hxz rU%^ 06j;{2Ɔ`GPjbHViNkNu:ގ"w0F9F‡ ɋ;ҁu<<#JN5cd6:S?!ok_Ʃr#\Dnv l%nSHQx}XXkgjv&|CͽOFW޼Kc2g`Cn_Ћf!sאx%eX|nLdy Ӡ'%/uj݌,/3 BeUBRN],y? u u1eVA?4>XAbnVy W?1Qm oQ{WN.Ջa >NZ\i4bPX7D4(W o"UͭCx(7 X*ϝ(IA*oS o:FU! |Eu԰}s_SRVu1VyJ km|.zKۮ+詞1[ÿH*lzݠ3D5Xp5 THH.-G5񰝖)cYeơr=> 1cY\)QE OM̍$rsEkeDM\`nÂZ3PxJPIgwX~t1V(_gyfD7TnsӌpI8/G3(VMKI6`P!P nEMc;T^`s oaeT*)߱aai) 쑔 k?+Z4}  /tQ JAP QZ B /n,D0igP_y.OvA52b}=$rny7;ׂ ¿^8];{NVH*ZT D-*\V2Xk-2+L$/3e-Yb[ 9#@ ݟ^`T䌽ѾD\vj*lV bYyۆd2_;62Z.BYg*n H^ Žy]QP$9qq/.8JM5Oį8?=L ":I\CIq`ː1~uW#]c ե`&O IupHjav z&?&6N#bѶ+m*Ma;kN잝2bϡA@b'Zu͂,v׾^rY`s 9ey_=+I([=NwA?\Nhv^ `:{{ o,et5mCDyM4s}Չط֖% WS'mgrm8kQp~Ej!>SHztw}̯O~(1ǀ[T́~H$wD0! Le˙wv7c0x *+o[6`"ˡPxqErZKI,ePv26kU~zO^FEF3_ [+?*cv~i|@+utZH֤p2 /$:dnRH)`Sbq?z FԷsM^x爁JƉ ZR} 7qհ#9N})|샅<4%sWP0(b|`d e2D1Τ ae& o=μ * lZt(\fФBho#׆ޅYcU!Oy>|@}N߂ɴ<\ح"߬/}J>iA:O&sͧI [:Z` &ή":DS|P0UYeڷipjҌ5rҝư4!.nV4M O7%Y[t`0c9aV| endstream endobj 169 0 obj << /Type /Page /Parent 530 0 R /Resources 170 0 R /Contents 171 0 R /Thumb 447 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 170 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 171 0 obj << /Length 2140 /Filter /FlateDecode >> stream ji&F+ Qn 4 T$y݋C,m/H.+߈X7!g J0 cn5icJ('p=J9YA@""@!Ŧ M= ئ҇Bu|}p Y  ~WYFhvsgOkxel !P dCn&MPuf\xx5ޡ0[Qc( )B0 BJ]=)g7Bd,p'/Yzh }оryXNų *gn+Z#fcw:TvQ.-ajX9;i!'badg&|Х޷aZ zcLKpuR qA`Op+r@UT%׳Z HxRFɈU_zY249qԴl6t2|;w2:p0tUK}[ N У|pFf"R²<7@.F$\i_yzXZ`B7.Kj@T(T:|qI+ke !Gk.so a"dGt': Gg} -.[v(K3nO(5Ib {EɣfXgCqՠknLSIEn#n͚vOڥt&&vlHQ3] hIRyJiagQT!n7 KD#SY8@4_<@-6(߶^3hQ_`競f4KnYI;G4?jY?mȲI Շ;oA^}IA'?Ĕyux0~w/VDd0pUkM xӛ&bfp?@j8<>BtblSMk_h5ip>-8豣V\n-fFDY @N)NTY26I4X'<,mX'فj9-r.Бm\ho%O{GZGE7?aNmHf4çvP 9oP+Q>!Sϼ7kh 2+fݯ`AM+D!DGl4i% sgnB!&}6;DC쿴Qv6lNmhtIƴQ.=Ng8 $H.t0L[E-J0ͫJ'_׺cJnYGCbgt@>wbU?OQ]o# @4k0_׶}vjΰ1c)=E4v$C Df?F9̦׭7wgx]|"mnqroyCk:6u(Ф- m5[^\A*T6hu"K$ϭn)> endobj 173 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 675 197 687 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 174 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 585 196 597 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 175 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 176 0 obj << /Length 2507 /Filter /FlateDecode >> stream ~zެ-J%<; T\_M3T0hj]"=o"E؊ר[2*?_"p$[@vb:(J>vh2o2Y/+N㛽߱GWgqg 3w䩏Q s[ˈ?5J3ʹo翸9uyk$\E)p֨VA:؈*|OԠV tܣ]kjClA٥B[w'&i)9[HvS~j =\Vޞju)w؄؏0Hc!>"#<x=S-1j 1qhp>Bȏs~{9-N }P\Vhkw JMyBFM('G P:.yi~]& !Mh <ϙrk0ak:UɽZ;(O ם/5B /m?OYXfn9;/tX(Drg4TrEqIScO[$vYQkIi̧Z)YR<cWk2XCQ|Ւΐx_pmXE>d¶+rQm..^,YEһ>8s`13lv 82v f~ F{oS*0rӨצR\SuڳM10qv0F!%[#SG -YQP,gF~tmݾgK9Qޯ'Q6C-͚  OFMsڈz}e2J`ir&"@e̱QuK>$UG(pӄ<a_mQϵHYEA>x8bSrNCچ;1bPI_YySE( =3X\3 F4c;ԶmLD2&aVKђv颃 @@MvTOa'sb9+wl='Mmr4J Z&؀+lzD+$ÊCAs6r1bpT]񋲣:zA586E;.lDV ,jFZtKu|b@*>\!S+_G)=0fqBD!8{3aR5dK6Hm_]Niۼ~\}5⾫"h?`;A3°/1pO+|B9ݵޓ{g83^[% 7a{E# j#W0n_@i2T޶X=7ꗕ8*4^xer )he7Ac˺cc IQeVhR9цhQ`P -nBWbV곍AdY{$X:hR?*6+L&FݠҞG׼=t]yl' {r =~(][߉T-_;)C; br3\#^Ն: d=v3]/b@և9ˇ}?|coGՁn "l3`czZDC5YPS0'7Ӳ"rI\SՕFGX'H-g9$ŧb/;:C.dR$쒌MLI$A0>:q-EY -uT}=(Ʒ]:O^(jk@Jp4@Ȳ Z? 8|rD#R"{p30KnNewM@(7;}B9j9kGÖi=fZrfDe —QndN Of>-oa,Zq+GQre›e VS-Qw~42O mf,5^L;Yd6vg29uLı{miCK*ڈI>we]t1wZQj$\TUA%MsWtZ2W!N4{cDum@fI_%ޔi08v uh[U&i CDjp!p3lrb>^xgyfA@P_:b9nOqP;V@ߊkj,;Za+,7[) υ&ʽ endstream endobj 177 0 obj << /Type /Page /Parent 530 0 R /Resources 180 0 R /Contents 181 0 R /Annots [ 178 0 R 179 0 R ] /Thumb 451 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 178 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 705 197 717 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 179 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 557 196 569 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 180 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 181 0 obj << /Length 3294 /Filter /FlateDecode >> stream sAOLKsg*~.-xB|Bl:HZo!r0}2-Qvhi\n!Q Q3#oa+"G+I"

6UETK3&kca\81Y'j=AAJ: >PH'R7WDQ;|qUF7=p Փ=`qjD#WObS*lQz!n?x-d2Η(WGW=Bϖr XsVF' )gTuc\R2q;p\@VAY/,ps]%|̄</krR^~r oDٌ;y EMYio 1(;a0f>M(FB/l?eÊC4>D6j-l |S5?u%dQ3<"hg>M% N)j钷~k'tvЈׇҲ|Ra|q4ǘ! $7{w)8lPI*ǹ*YP tX~>d o >${[ogF[}0PFvݯLoȋ.w ֆSښ(8ruo-8&^Ϡ"2sdtYTN2a T9C5ܪ 6p=FFeidfVm'n߰nǕ34Aoj憏y6ch.ͤأ 8wɭ7Cjd~/!~(d02Op?XʝT{߉ָnOo}9F9 q=b1f4i5k}Y.F9 (}&>Dm7s(1ke)ڬ)%% ˡQq3 9)sHq_X,jK&7o3;d,$tHCs;(&{tUˊZS53c8JUGD5YȑsWʚtz V ҌvrZ* ăgI7n&ȗneoTdMXo*Af\3)3$[צ GaXQ&~2$#?E!f?w*8@ ,*O(xR!"eON`κ33A>S2lWx&!"]i-˪P Zycbtxr')\sd P`js;is 5hNؔ/`Yl\Xk,^1!(WHT$"1zZρRR헙%cw_+!ߩ`練 y=?ɿSqMnopΏaLkʓMD,8)&M%AqWm'9e4I0 $eRlDŽ)tQ``!YPm($k'J8cT8dھI}fMR"wr1V{ :Oߧ8ix_\h_CFF| q- oKTV[^E;FʂCgԿ ]6$.'\0?ca]ݹI"R U[S ccYEL_i)lHɕQ?$z(A}&uW|nChʢXZ5VsmǨ_D^@:k _KTrbg{L^ZA-kjjjMQs6_nL9]A!kUz:rFw<&?W6?uL]ŗ Bx6XsfnGkI>k}XMH5Gvu+.^ c7h=e,=>ʡړ՟]z/4`5[FОw=`m7AUBE ֮q2LF[\}~ULMAO]\-CzVOLxa* Gt&yb^{A(Mw6Ju֥"Rp[ Gc։f4;@E|)yn F,0B֏Ķlǡ=])ilS{:fyA-u|.WbTV#:HJ֭;Kq}@!>9i!&4974Hc9Ѩ&0}BF~ -y7ӥg2ڝkha|tar!s,LD|ǚl憅0SEi4-rY;y4"zL BgPݑ]eS_h/MbhUX"'-|OR%a endstream endobj 182 0 obj << /Type /Page /Parent 530 0 R /Resources 183 0 R /Contents 184 0 R /Thumb 453 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 183 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 184 0 obj << /Length 3149 /Filter /FlateDecode >> stream HM,{|7G<1>`1[M1}/Uyj.FXYV#ҬM= -]3QApOV3yLʏSCMD>4=Q4fG@k]œ%UQ%&!R68=B7:  [.ݹF 4|kTpjT" ҫÉ<%GdBհ")3RiaH>n)5SjUگ;BWad^o!@m<^О>m80hT|j7/ݝa1B:+! f?Q?-;YG@b=pESw נaq0ڒ #IvgsJbxiɳ OH98[1OgNt'%jX̼ xЕrV|s[CxSc2 b-}i:3p<njm|S9A%{oT PxM9ჇR<$T5+SǠ4ڇ-MSh7)nB}瑦;[h=h~ [bw%m<6= Z>{QV ^H<Ե6e!/$V|269?R w_+#¶#p/m=|6 2ã^)'PWE\/AF :շY,Pm!}6{P+Ffv7n+WUOB4m8`UNkFRehÀ4RZLY,i@/ _\g۩eFc4iu=X1DBPҨOBpxkHFkF"Ug\M.quXB;W> d,{6*.? ?ǫA27,o ^/zl0dx^])PpT"HC>lݍju{B`u΂L$D}:'np+ ɳ^`3X,EukWQ_ phD AYP[ f d@4" |+-)~"H48y7HV` 4݊,hUwE0DGfkVHfdu"Uu1qeVo)[ݶO8U~`.y N䞒M?&4=lۜq9dU[(vEBVc#7W*T"QfX:ΣBƧx! sL̳XA5ѿFSXo(Tr7<9L=>cQo2| F0&wT WmN7Um^t - ]lc)E 5ѱ= |v[1Ծ B6$ LYb|tOg836Tԯ.ijMP2V6Båԅ^I u^D;3Oҩ_5y7],ŭmww$0fئ'۾qt +8$<>{52n6]{TƔ$a :6QmykQmW6AAbrsQe y]egMJyC! X_֎)}j sem9ٷ)9(MF-g4̤>wM'tlЏPξM}aJ.ద[ވη+gizǸK !亢IIA*z xnw4D|81GϞ,вj`Պ `O~4/VHq^4@!1Yw iɵf04zt?֊5aE'mg==3s+е]u-gնs VQz(?8z~9f~BjFih^\+Xy7 &#Mѿ˵tz7/2 9v<Y*6>#r^4% Z`']ޑ=ζ ݮaYcf n5Өbka߆^!V8>g̕6h;@۵Ֆ4vhmxiҜ1}L {Gl,J %gą^M } 4Np]a7xU'\<S#f a_%Zoס8d%כkw᷹,<_q(]ާGM\b@pcD_mѱ K? GI%((<ۛȓAv5H(5[vaA p1ne(/~f{+J&Q(ڻ7)1E/ Ma Ye <+m!GdH&[0UuEAxU:G۩E@sTG_9;gKPt%k0Un:~9ohxE endstream endobj 185 0 obj << /Type /Page /Parent 530 0 R /Resources 188 0 R /Contents 189 0 R /Annots [ 186 0 R 187 0 R ] /Thumb 455 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 186 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 168 630 197 642 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 187 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 550 196 562 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 188 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 189 0 obj << /Length 2721 /Filter /FlateDecode >> stream sa)U+mJ3a.NKgE/ !?,4*G7)FLXĐT](]MD^zd.2Q\+Վ $K/>'IҴ$>7[޻n8|«"xIYxN.fN OJtբe_M%H@y3r;ḑ#cfD榟~:}۽2 dB DD Yoo1ȧF&_*Q 1fg7soe݇l&+{&Dzh3Ok6XyaH:\ .C6dogC\V*mا,L?unXd )A@1E+LTldԙjibzsDߌ uhP(&W!,QJκIoDt9Kf<]&d{%0,DӦ l1ax6{ʟy_aNV&M=#Dw,I&of {i2r}R_#D)$qd_h6Ws[a¦6Pt3֥QBJo,f#[id@2#$WNSpGP/UA4Obd ?_2$ pdUY4ћ-Z E&[h;J?<Җp<]ctrm0sbR:~qf]+w=ck"ʩtn 7u㓕4\׹Q/v$D%WҮ" Nh`(;@ oʝmh9Q_{ȭe_@JN$ !!q}@#Y*uzTaSDyVyC5E8qnҏIt(ZvҾ+E{l~pwzⒾ %2r:C<|Q,dDlPؗ_=ޜ۵M5Gg KUva%dU<hcؑu`SRʵں5,$ZI^#OL Ύ@Ecف ™CMD])GEzp _lޜY0cf ڋDUYdn4i)pwA‰Cyⱊτ1'lSX.<5DV:C[1L'hH9l7BK~DJfBpf, 3O CR ygn}hm7 ljڿ ׶ޮcQ-Zf5=`5tE@jf7-T>},r~ 9;NWxݴ*z}eEzعX c܆KhWf9OGSP0?ºˏw"u)%(oq.frE# !V۝u%OXѝ./Ūw7Xb BLo}sȌ7h]2ݛC0{`+xѝ0<7q9TS{d}`D$e¯Т%Hrͭ<+4/8$)Jiڌ^igk߈,\ūmHfNd L E:~},D NUA|LK$n~2\w~̈́ʁ38Q9^k?7EՇdi8ɩԡx_iHˢO[毓yc6 rƖ?A)YBB'{|)ӄ 1#%V%k=H?1jCs[O%;Eh^pYRV`W)ho"_m;Ξcgð& p͈Ǭ^ĺaa+IGkԚ8Aa-{;k3-Xc ^B._Kμ5`"),*W) !/) ˿]m btXǴ1K"cT{z|N} ;Mͷ]mMgpv ,n gzq_0q_uf7~=aNIשiH/ey ZNkZaPP6miFOx@gf[0]'])=S4%@(xkN_ 3$t$K9S5gϹ?Cj(;їW&~&SO`O@~>[լu6F@}MRִG[h,Ra`>xb{EK6 :9.fQCRJ endstream endobj 190 0 obj << /Type /Page /Parent 530 0 R /Resources 191 0 R /Contents 192 0 R /Thumb 457 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 191 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 192 0 obj << /Length 4080 /Filter /FlateDecode >> stream q4eu\%ae]rOcI8+}{_f =NQ7LXvhs9'(:vtI#pDM . !J-yZVݙ4Zn)Y+S+X1Il֍H8JJv"F hpL^mHB-հ ,_;P)P0Y'q.Hix {C"5yIjbЪJp,Pe+VVb1P#:@Ok 9d#Nlf0q5gln*Gc,DrX|#9>NR*O~R+J  bJ=WӃޚOf׌wix(ڿXSC|~c,V~%Po*ԛxZ[u^Ǔe0G``{%G\#DN,,D{!~BK[!B؜uzv/Km|-wb{7@v-̩GPX6`]n`JfE,>eN%lm&W ĭYr,Mؘ *]asp'gZ'˙ai\8tB[v=GzL%'U^? ?*.PR?WHY1*} ڵ8˸Aʼn Ms:D0*k UI  0`?5'5ݗ ¿1v3I S3d>*ǹMgF)dJrz0LTY()؁¡r:S|1[UTv5BW.G^jƤMBQ"*Jh{‰k]O :D}~Xޥ>bjS[v7u]['Tcg7rZ0aYcʼuЁutðD~\u[FF*1 cXfy@)5=B`j<q%xS:ZdB9Fzފe$0$E$(A|y5iɶ]zզأ},T^3C*$ЇK]f;2QpFkno&XY;*6.qk0VO39*RWs;~iZ74jn|kHyKqM/).٦%l҈XP8./"I6&uL 5{S&+Uh-۞[@`HPþb\ޕH4ƆEJfFBȜa;nˋ HjI8_B $ nbaJBPjDnӒg@Tn:"r5z3_&hgn'"/6gMv~&WnyZZUT\0m8T>@)"sf3F57 >V:1X L2Cl{k[@n{Nh{tTo|!nMHj>/JU$n|;T*J0 F"?B-6_ZySRѻR[n9vZsֲYܖ =@ƚ~ z8ti!E_2eJwB){9%>ᧁ :ʖH,-;n1 .})0W_kg.~GOQL #i AvƠ*A(zTԖ4U6v =D!X֪]j胀yEk-c{5 Z^hF!+W`gR<4fE1vS-PWFG=<52сY|li?a<΃#ħWg}'ap|VܡBمMŌ:ϵI>`@7'p*Q  =N.bYș>528+|GVϙ@'K!PTX5n+,McX`4\sX?J͜7;ؐ!9{?F|{. #OQnSRtfҊ#NR`ăKO3d( l2wqkoPK蝮M4 E.QX47]-\JH>Uj`aY!8Q~?ϱCe5۝uME/7"frXjp(II`ǽE¼CJ-ֶ'_s~Qu% vB@-ﱤ*XU>\i};GY3),pd*:⒛1ۃ]Ql>9l_&(o\ل O?Ka;zV {=t+w5P:T@׎g MڸxMx~4jhxWťۃ3:p'bc Mw46iV$JB9 \iN jpEr$ȏA_Jq7g}xY uߡܚ1{-sᜡSRUT:“FI "2lU~FË$CaF6_.^DK ISm31 iu_+9u"i+jE]ᘺmI"Gg> \`'J7;qޯlqM?+U`BN #|l5.xQyRPӶF3&P5hЮdŮf'Wî ru N; SJD; &5DEimY2_nRћ#oP *77 N厗 :D9u9+>j^EMA@ abg`w3jA8]C b(g术yKRH&s 5`GɡTH3kfڇ8~aTU֥1UNJ|D ~R ѪD5A{C?뛟q$Зg^kYOle}RZ6o?V k[iMQ7b?WB>v!B3x UyeGfFa3`:V[R4g~r+rM%l=` (a endstream endobj 193 0 obj << /Type /Page /Parent 531 0 R /Resources 194 0 R /Contents 195 0 R /Thumb 459 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 194 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 195 0 obj << /Length 4272 /Filter /FlateDecode >> stream QfΨqʦGi63lxBZ#)>*g⓪YǍ T(0Bw٤T~,Sjh*9qv'Xpل(Nwa fo`6sk_X>|Fr@+h׬X"Lak^U KİϕaKXKt]r*PD6o;3`) 3Zg/cr4i.5 _G3E yGLB~c@N_ܘWZ_-ebdSTHjw,+F.?&e | 5|h^J}/ U3a ;U)jn6\)P>r< 20-pow2d{y'-]5(~[Y8 oٙ( dVōɕuCcs!I]σ%8α|U;u=Q&Mů~ˈ-w,V;}-?ݷEqmE>ǘ'IeT`r(;2Qa"k !N DC!9;JseS9mlډUV.z}w~K&aRm|nu3} cژ]jsG.U%6`!$ܭX0~_ 0gD@mUnuujhZ xwSwOIC++8 gWGHYbl2~M% ~ZBĉ0VI{e8oľ? XAM/ 6S! S7j|Dxf`.h$?Ӟ^^3k+߽o/ Ř2uD{68*<N05ns~LoBJdT|Q4<>7يWSN#; IQ_ 徦92爭 FabPyV]S_mJ/$^whĿMtIn!䃧g1oM)BySP׮Ш]zׁˣ ;-H~Y':t:|ƾ r-}4'fA_bf~v)E;lTdH<x>]y߾ ӞnZZBniϟA>d$~^z‹ ׄ!st}M:eضg >PM<1todIQvTV{Yw/\?yKτ"Lǎ|LF尹"9_gh2$%n%Vk,4K= hp31H**\5ns' 4?٬ҽ%.92o*@t}b f (e*Kȧ`OJnijre~{5]Xvk@|-5 9MЌ=yBw*W-DgGr脬x6.=A rXUW1{s%zYERp K ;JbbkuVoqI 4I r^gW-RG[-YdI1##~YNjI8/6#3k& 5Կ0 >ʵO""c Xy\ͭީc4"=*֖(y ~ux 6]kSL!i--ƹ, Ӓ! c5Y KhZČnk*廁_UICNR/|-G,nSa@J'2?Hr}MNdprh9 g,ŔiY*/bh%pdg WDwV_w(\x ܣQi2Fޫк#,@>uԲ'n0THx~0l]3 `؇Jdk݈dCPh{ej,i:ޓ)~WuBVa> whp{7;3]:0R_" C8j7:Roxoxee 8-eI@#Qtt"Y;9 #Ry_$T"lX4SѤ uτM~*VpQT/^\]- ["&׽`G d!M-t-Xq,vk3 V֔VZ| 05?#2ꃹa֤4%LAL=E?\#(Akd|۱j +H1l[LA뉿j?sC'y6fqmBn|!b/I_pm ΢ bU͓HȗP'avHƿ(ݍ-6mB̝Xǐ6.@!o;.=kr@V7ȦBEO0vX@WR3َ)P|]510jb"}PĽE- TON+ryuS<[ -bmJՓ_f,V!FqT$XYj3^w`njdjL `a8VSgYY4nmsv)d⭂Xxk/Ad%WIDH9% zؾ%cd"D[|3!W#jpGWZ4y׈~+/BW;S |^Zleou 85XiꅰqCO2)ܱlNɌ5znkW1q AU0s~rX~'ͮt*Y/G) QQ1P@75T(;)AkՒTC,";1\׼uq rL1PF zb0|xޫ $ƫK7$EZAK- p{uj`ȧ O-{;ʨSM~: D5nQZHkYgV/(L$dbIY٫Ѣ6m#V!; /oX-eM}Z.ؔ-[9(с+ Sjyl)ʪ9-~P3> endobj 197 0 obj << /Dest [ 82 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 169 519 196 531 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 198 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 199 0 obj << /Length 2096 /Filter /FlateDecode >> stream 9SjoGrsf%^_ p;8c|g&dŠ)f ᘠc趒Z񛷵gBxeLo7 ]Er[EgrG\*X:KP]eEЍJal/$rsoj#<'TeHѿXX+,i2j!K6'4!j٨'0x y0qbPh,hi~'TlZ%sϛ]kW*ε!`C ʪA2[XUҼ49c"$R<+ @"'opW}繄nxB Ï@baIlIǫ\jIZKZ3UO&:cfVY0)(6vԹ`tݦMie~O;&*~"~3p\C7ױ֙,Hx@ChFmҍrӟw~wMJ*Gr67A ӑ/@$ƭvsUm.;U[#y !Ra[hA@q0 =R[": `˄uFK%1 ^^c^A鯥W9$-D0 z// ymEhT|߁(+@xÎqk! ςud@{vL D:Hb滸PZ'?'֢~ZO~` Fܟ~t&1gv!WS5$RͱA1)aCf&ZJR tؔ'XQ̹Zj> kƞ񤔑l=2V5B,?=(d4.1?'݆ Q XTTZ4ٗl.93N+!si2Et!a`1O)e0F`Z0fV4[Tņ>> endobj 201 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 392 582 397 594 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 202 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 250 400 256 412 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 203 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 204 0 obj << /Length 4061 /Filter /FlateDecode >> stream l-`h_2Y{/V/;;@7:yP}c ],=$$,FiI+skO3uɚRKnu#4@@jw٥&o˳X7?;i ]K3 F|orXЈRܯrFQ#J0*f\'jL)x<&E`|Hg'ud Cye !?\ L\>RWC9fkB G$jZa"aS|ʹ+U/07[I2TJ)v8(lӍ|V?%h**uORdV]5ޝIYac5IOrPXjٺp GfnJkjvaPMtCa2u@Q؊ 량3h >p Ca~|_y*o :8Ed %MZk \6='Tg>>2 005 ŌL-/0y:|`zϼ /-xTjMR8,}A`kQNmp E@Z$U8=$˧-_6c:&^CG",rWmg9)R=}; @i<7}5ҫRܝ(J@ s-d69Z0mn|l&$9*d}5WFM1K 0SzI@NębG@ +Oo}u{ ^mY&LWJ6ہ:v@pHꫝL&]xW'~dVQ(!`ڧ ݯcK r[5]ZF|eؤr /ZM;.U'v_J"yx@hm{raC,#1HAԛ6JTId~ipALuS5€*.KvD~EM'\Yh~~LH>菱֙Vr4$5==og pv2}DC4ld*jA̎F p.ZMw2w2![|m 2)mДJ0kK 7C^rB~?uEOw,@bf?1 ޤGrK@~z4r*N8jٟr^Bđ ,FY < n r#X,NҩV H ;ِe29W͘aǖKu2!3nԻK12A#؉}N{m/:C#\̀{En+7QH~Ma+C)rbq!Dє6 S6[I'_ %͇`K|·ҷgD)1:cY" 0=fA#4A|/Ens))z7YB#y^fg?c=@&lQd[Z%9z%H8!f\$S3gMf:EݣE:nr]`~P<):R~4CHelC'u3SuNA0`;Ad-/*(R.Mƥ K JK\{ikZu*F7;?0Jlb2z‹͟3h"{y\z{s+M?^LHx qcʢhO2keDdY#\GcYM mSan=$/RvGiQu3]p`ԵHCbw,MzO!IkQI.^pm5ϒr9apF C3fDZ,E#X`X.M;Iuaf-cu͓䴓dBϧI=I5҄o)jr1*vKl_oOq7c=Sޏou(R4qm9B.Ct ?GytlZ)-Ͱumo6#Y }?RN>uHlTn{]7MGV@;'=8<)nY##i{H CNl$mnJ 4 KlF-E$IkJa{[dz3:4~SqUy$P]V)J;-=#&hK $’z ,#Kt?($6`vCAz_|" ATI퀊{7QM=<+ܾC.?R^v-Uq9 H*OYе'v~d/fh} lG|%}> endobj 206 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 208 600 213 612 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 207 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 211 462 216 474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 208 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 209 0 obj << /Length 3351 /Filter /FlateDecode >> stream `2Eeî:V2By@ߤ U&?ơ=( [:X,P.3j<'qh,a4/1!~c:u&'bjkqj|4'&( loͻ&)$&HW**xTwÅAOEJd$R#MD-$n1/0rM"ch)ĄjZ\Xp4lߠY?rJ!h/5]mW~g|j:M`0 'E77ܥ⪺&rau,,!sQ64 h *~UG/.p,z0xe,fw/Qi&/𓡠>>J:vyLN< p[@Kz~ɏ ]vG}za{ 9uw=gBq+_58 7O`PG/pb9ߥB3O;wTDnl,TNG0ޜ'IRɹXRe&3-FY,[Yt1]K?܀>& xqn ^Ѕp9ٗgƈԪՍ'xn{Д97}U*u_0[0so$^DN[]p.w/%iwC_[.V|JeLwqA`njnǮ??R7ٙZEW6]EYA,ϩrEFb8AU`sL 9tA “H*N2s̨QLP6V:-ُ-tC,i zA5bvuq;kh8%)=Zno˳XPPC3p U1:y}`?܊d.TMIIkeFk[[WJE:G]Lµ!SvY¢lzX'i,]ÀuGx== 9 e>l@ix{̩=׎RQs[ mR 6{{)ތ9חmnexL/YqW JK.rL!㕖 ۸~4oEzGm\r1,} ُOgb3S2Cl;VC딜4hT0>,DcO:u:/1EݖGY8V8Bqm\G!O7[4gnNu)jl65u3pQK|unq[.RMMqU2f$ƱEEVW$`&dwP <>'ݯiVZYO);b(8 V(m㮰Q@: Q#u&ziL*6B oKgb=?ܹfha $dS(yh4:Ëq RE" uk?q47TmHK(.\7jS||uEj$;'[E+)E9 am%. GECv-=?i911rknwz5h#JUlDa;VIh8S༁ ֨ )NuNp7RAhB{lrKCVi+""r˶vc&AʀNd<}+A-6e,?qa뼮MhPR'<ڝy'٧b\z?Fb,H>ȧy7(ơ= Wx'Qe(Whb?4_l&*#ׯC,SلXl5'gRamܻ=s.ͷYW[xJLZ4%E !6gm)Nj kR~|ϔm! $pKx}T*3Y#U%_!LPqRSfЭ-oq82I#ftQ>uϕǂq]ݭʇ܋ϴ @ÔM.ߖUgdub: S=(W1@#5߃䆧YȶٲZr*@K7YD'3 endstream endobj 210 0 obj << /Type /Page /Parent 531 0 R /Resources 211 0 R /Contents 212 0 R /Thumb 467 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 211 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 212 0 obj << /Length 6887 /Filter /FlateDecode >> stream -Ly9Q'u9yh}` !.AGYi+B-F{et$LZK݇J1_ʴ熣5J"3Ɂ rNwra/+Yj:;& =w+oB[_efp{ʛg&%Cjm|~TY47X qAQ%1P6/R*䥿.i|1.ҍ @THU5 7DE^wY}7Oױ)`A!Q~v3b5O^UMj?oÄjN0!:7u8{._zi:՗4`i!*֋a$G l/zbj43̲Dإ'u FLnDwpއ 2HŇ=[CɁ*̝ =- Udwl|A,AC9Sxq|مv8YN4>*ʯ(T8ozg\ k =iam&zKW\‘oN;,9h߶P;qÒ͜RO ȭ#`R[Q(Nbf}%J^Ħw8 1Q])o˻.VL tW )뺋tbKo&{foT%[LDJ~N\,L5t*se_5k E m5#Hdq>߮KZ1I .bqB$o! kl&'o2o~ќISU{5$ ßt[EYb9}bw?-lڋ2&3wWJП Dc'yX7Iȩ#48eⅴȽ+5}_?[\n&ިc$g}C t+-a\eu'+@ ܿLb^A䚩SGV\xrm}$G,W>!o&ȧ_ԄG%ڲQ ;AÔ ϸGUtpuΜpyyuҝP$N/Dz;ⶳzEmc.PJ̘A5*C̄uH-=N֊G՚|HG3t L-Y`l^! ]A4d#vV,1j\CqhĨ2鈼;vB쇹:ָ2]Smw_bOHx0L j7dՇMC}fdb4S>78Mχi\U-:aPwD+pu#!xg\TJǾ{j,.Xnwm](jUC2%}j؉kL We ةjZ E$TJ-"# f fy@FЭz3LX*mpY~65 [7Hj*Ey1c.$Ҟbԓ˵A[wգHoQo6~?;HNJcPl{["+`=Kg8,ԗ{FmM,I@JG@\ox D| -uH=uKR_i&\O)7,:O ϳCDZU0qR\Crgk* uYH!5};|&ֱCަ W1-%CKzmR1``lH3{0+™{ta (4#6)3f!{|V+4r}/6 zZ0&D0ːK l@j aCf?f| ^/,޻fp2ٵ'&q0,4$qNAV i0@5tQ"@JV*۲ ]$RE2VLNf _f,$~9~m/BpX'DKpd$iU<\.; wĎ#-xscfj2CG?O,X4L=O9CCP3>l.Yp ^E};(*кz'F$=MenuxR#ĸH}-cزљԙO<;*DTS|Le܉Y>aWk!6?aAOfS=/x\U}/=Ul{:Xz ;pׄF,e]ƈ}Qf59?z.WBTlZE>{9[%t {-CX'\K;g4Lp6iɋa$9Ӳ~m<Üs ;3ovSUH dp*0Hl,!m e,#SuUf !46;!s8ٲfAoG6-ZR>v Z]tI9"g_ǾVxE~ħ('9s'7F2#8.kisJ5bK<~I%˼cÇI?*PLM3Nlɝ|uf[,C&Mj] HihQ9sӻybG&?g7hw0r&ҿ@N"lěYT{z!O%6D7h$d]l!aXt`T]~RJmjO|01Dسb4Dq/>t›g^Vc}+ YQo4Cf|p@t9%TO{dc(v6f<=$beq;Jtk_6!}$;ڇ%!U/PӲcٚ.xDt@ J#\?[cOTEf&1 -9ԆJvZSJX%[1DSTO֙4Jh^yꐥ|Y::2­*-v3_3(]QF* 7]D9.M4؅tsTMUn`e/? mѫ5xe3#}/"Qn" T7,g?$XQ;k78h-vt K+|*Ϥ 7/cHp:$->E8 hY}DZؑ-AŹ $[ك]"}'鄲v w]v8鶄?1{3;By]/=[«$9wwIT1Fo@8}(p%TDyoh&37CS'$?1bJ2ޣ*flWwYKU]CT!.tBfŃ FZ7()̈́ WZF״ Ōƾ{L϶ݚC0&\7Ѣ4=F fWښCA ~DD'r$ .)8s.P~_6hn=[a2,)Gd{=v2$_P;dDqȺP;FG V1x8 bZ2H-f4GA\wXhq$v9uoP-/YRsBAiz=t"!gFj&Epږ8|,#JǯP[0vve^ϭ@jlQԗNL 2}&Beq mO^+yo_?>бR*(24R1oMDx z-'"DkЧʂgb"JMu1R6ZPsCr9.IwUH?`dVRB1K뽪#g/g\G="`#L^%(̼ƅØƨz "2&_}Z*,sD$H!$e VbbAjk6;V!\dxĚ2F(䵞U,nxUZf54{I;C1ޱ#m63TY.@_Z7i{,!e.>~L Z\CEt.Hy"ۂA]W]K*7hovUj OT0]WS|e=O,"zwhmaҖh`5Td]͌}ck- "vw>Lo3}#9NN]%kJUQx]T8$;UK6؆ִ,#o8Tԧt}XwBd}dGxٲ2\kq $O;'h|abvp:Q'9ObVc! {t|;K@UpB cA5ib_gɥE GWޖʩK!ho-Po6*OLS퀡dUn>bm [;B >J\B; rMZɟ/y DSh W姑j";C{G*m}!{ 4k2 ԽiGZ63JdGL/&Sٔp]wkY] ȢOoO(IsaH٦`Xq9ƊA& l<(-9Upz92/%&5,\벹9i Jn$IQXcHJepaJ0I#jIKKj-{)'C*p@TnO8<8-u/2逬42ue4ƺ,OVe@fVK|C,>Pŧ&Ϳ9*S:J*qYHX@7 Ez췅@Mc#~hXQ)+$kc@0hݲa;gMqXge2Q6PZEw=ο,eIZQjz!YWw&uqD~ɺ'(%9آ-^bzc3D2qb-C.VI={~ bY)6ُHC(|cleNCbGbb Ҽs vLpȀڢZcr\ a @b`᭴i d@3Ma?dfd(򮳵2źfvy)AOkȉj sK0غ`e"y-[ EDE#;i.zfc5EO,&-*1OƹϘI6J۹m(,$6yرdÁjh..ߊF|k3OZΣt]o lD?>[ p=[K;<_.~\J!`(޼M&(NBl=Қq^@eX*) ,[\]5_y=! y|uVސɓo~鰲)- 2[\^@d Pݢu/{yk@K/,X,R֦bzeɹp>Sx@]EkzVkj/(yab\d\Aд.>} endstream endobj 213 0 obj << /Type /Page /Parent 531 0 R /Resources 216 0 R /Contents 217 0 R /Annots [ 214 0 R 215 0 R ] /Thumb 469 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 214 0 obj << /Dest [ 200 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 211 405 216 417 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 215 0 obj << /Dest [ 154 0 R /XYZ null null null ] /Type /Annot /Subtype /Link /Rect [ 149 344 181 356 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /H /N >> endobj 216 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT14 298 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 217 0 obj << /Length 3986 /Filter /FlateDecode >> stream A3b,OE&Iw]X!mG3PpӬZQY,ja*:Z=FqW|8;'wwU=pI -"L>GXWSAt*A[)v'2Gn59A0rG|U]W?u2nep> HiH |Imk# >nW+/#9j3eRc>CGHrAǣ$8VAxVc\S}:qˢ fqkq挖7Q%8Y`juJRJZh@*ĤVʨS!%i s2t /i͏<5tx_DBmP~EY"_pN+^ 桌7Bc(%k1 pG/̀9*&qN,Ug/r "n&sߗp h GZ~q QLtXmðd@x7h"+VFHdC ddx2{_<-tӡm{gBf_w_ {6>cj *F lg4bMIJ߅n6)XqߵXˡ^;[T&t.M#h 2^| Һp:e`lh#]Z̫,A*ٔ2[TaːIJ%y\~5|u9FWA4ߓ*8Kͻh S$+5 Y?jTq~&ӲpQ[,~$Kԭ)A\ p+Z>Ix3\I 4z DioԢzլrI5b7 }W ʦz-v]wv>׍6jr&OEvAR)#y@#N2.:zQBuvK+ <N2_\ CUWD1PHq"I湞>P3|N[ MW >?Dk(BRZfUEWd9QN(l%/mix\y8S8+|o=K,+ͥM\͢IH]pv/3|nS7V 4Rc 8C+%- 4E;RyjvJ Dz^'s$W@sVnx̫&V1>&""}ܷ|.\dV,9h2o4 ={,[jW ^5T N|yX5pI[vт9brIwv -DŽHz\PCБߡ6"@MU~NJݼ}N{$rĈs7ټ"[P*5TW!#iߌtm4 '!1֋=3vYڂNZ$x?`tԳ]rn k ]`5>_VAVz,Eъy(w5v_U3Dtr Be;GxTamlu[.@]c88}]?LJ{R HI0)F2s29y ]QkX/mr46kY#K.SgseVcU=-֡͵ R/}BщAf-s7kz mײBu >ΰyHҌ/$EGndy|pc1C%Mɥ1T(9MBFvU]'},fѠ>U`*w ;4Ȏ*V-k`nsS!2]6@acy [} 蝋qUaXM:x4E#[Zة 1~ݝenBH /@OnI4ٙz--AZ2" RZ_Mu,o7ٶV09YKJ= +P;*ԑӃ&[6 @:q:^FD+ xd[ٲp+O`ڈݖX+0-Mbg<蓾–j˾?n{Mh$K936qk\ߑa6I?I,Jߨ16:/\^[C0B# U޿^%-7Ԇv9 #'FȖNi1bm#I,*_R[2s{nBo1$K wvB閡V?% /@9;M {4V9\ ͚;ʷ^_vHL@Y`kW x'R̴XKf;?E~\H((I}6WKrN}^@_2HX">X.Ӣ)** SOI5K"`̡nC >i硌%')x~DE(o4S.RZB ̨S%1HYV}xeJPf]>c, )kct'4ð;!&,OGޖޡCrQf$>HVL% mM ЈJnBTijgZ_Qwe>ț7[ O:$ΩWaSprWtʱWc枔@Oc: ,|5m4" ;/z{*C/=SOz +"k]}%*vS*㥔{ұXy]L-Ƣ]I؎9F)Ӂ研hA ")~鳱=w߈z!O1vX8W>1pC6{i/ȣc`X ݦ%{ȔaA^d޻wpg#QL"q;OzRh4>K9@SDx^p 5z~H&9ȗwBd1Edl*4EA_;x=5]L16c6v{U&a~gqXϷ\y,wMx?/mQ'.nEzo8<,uv@9lSBŢ3bZ޲!*uXd endstream endobj 218 0 obj << /Type /Page /Parent 531 0 R /Resources 219 0 R /Contents 220 0 R /Thumb 471 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 219 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 220 0 obj << /Length 1079 /Filter /FlateDecode >> stream H@:-4%3_΢tgb9H͞BB%h,7 m4'_A Sw}'WavFN_DH3"d>39aPC;-&M 2=G:<:# SBOǠS{ya2%cM anqSn0|e7TZ{HyAf/O`ʡ9 ҃ >w% Λ+Q**S6 fڰaS߸Ԙ6jB=5*& mbV˾^Gg]vu ()ܼSoiJ$m.u;қb# ^ӺGj xbN?[gu,EiMNכNߕ{cٳ_; xm"@,<ؤ%DQtz]Z34K`;QEo<&ψ%|}t|‡_ہWEp5zlQyyŸ#?пJGӜCHyFV[roBcLƒ21YWj/CN>d06"8oٱ'~]O4eQ3wx%\aHs[x8ͣ 8x;%{nMUVzӌbqm!Zg Q[ b ̠;-5ă3}@bmghps.Nz>t̵S7T DHTk1~IQaX^1D?2X܆XdSKHy둀/ͶJz n~/9($ Am?%mm'ZB\cHۛ|{ 8 2:T[K@20]*tG(VUB>$q1op\- ,E VaVZ8E0..Q)2CDcy@J ̨@`YD*T_ xskv1̓-eA GpZ{ endstream endobj 221 0 obj << /Type /Page /Parent 531 0 R /Resources 222 0 R /Contents 223 0 R /Thumb 473 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 222 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R /TT12 297 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 223 0 obj << /Length 5246 /Filter /FlateDecode >> stream H]w:.jʻ/F&|ə[Ac]N0jRA&u_ |Jztʹ?L:Ҽye3LON@ ۶b)d١7ʰ@72BՕd& XRnv ? sϕA0GlzªAj3[2Olu$B&,xx;#_֑?0ؽ֥]}lYqcj0Y< Bj|RD73 ϑ4eT͇h"cDB΋CZG3:ňm)g&œAx%=O'a\&3xw*^A|tdEh,Gp#2e'(}$NsjcKi$[qFKD_Ap_#zP6c'\߸uh, t"C C$^Y IE$P` Y|9O|$I8}KOr='7u{ SXnHaq@Nu7&kد^@Cхy64-9M~躠|o 4o8{KYjwg h,C7s<^s9>Gdw1vz3ɖw۱?5\eZ;RA)d(f,Sq(E$2~^d!jBx!:dS0&eIHaC;}a15ZQX*Dlka^fel,R gsh4 =OOY"5[!bL(:sD|ڦe=P^C(z͈:AݩdEܺ,.eX2:ũNY3>ZQga؎ȨQu҉tU{X vmjXG" 0q ]xJ.>3:=8r}mO ﻠАogNc*-4tǭ@mq >YWiboZd3?Qͼ{.93ic =)jA 2|V'"FٷŦm \nb|Q;Qgx8z{'o@ޛ D+\ͶsǑ{Tre>ĩ߬"2InXt?J3+p.( OԷY nǣwJ8UaX!+mZ7'*BV{-Yy$a"K M"įn<20%sqP6ʙ`IylOдRs- ?_HO; хQcmVKAӓYmAߏ`U!}ۊ Q%E$?jҾrX:SiUD-ԍy#^2b.BD I">/qRHх. ˠ4{M٦ɜ hɽ&2Y~I[T7jjGpN5Q)dN[\{T0뚅A9bȮC=-ŕ)za &~2WqJw+%64f6:w.CmPn'0 )jXcb*g5E1HRM'G}{-4CW4m6QWӝ7}Nm-b]uYn AWc0uVB!! $F'tg2S@ϫ#y bׁ{P 0)^OGAG пCjw#?'zX׊H\`$pvk}ɏp:-UcPCc)_z;u2NI wTWӑ$q f͗D~D16tbb"p?Wp>mIFo/sXF,5}ZhN?-оTke߄Axh ~K]ap]_\,4)^kqq##C[0*1g$w|U1ҫrI.tH#RsxpS[a!lr;&n%ҘQ'9 vI }7Tۯx9Qt {19n9KPG? pɱ0Zw_\ :jp" DHXpM3WQcّ[:P<&@d7׵u&jڷjEPOoh>x+/~a,{ya1Q= CMM@QFU[X@^I)pT$b3Z䇀ONYFωw{q]U3 7hB̪`)A ڼrqQPW mgÁ9wPb%pg_i)Ipza .L)f")xSf%X4rC:6Lk}>+bAe {G"V)B&wiQSqG7P!Tr,kp>J NC5#4ӕwx2@87{~7|wZ%4ɾhTEChdyo=$LPrVwYG Ē!cx' XI΢щ {vI"H\?wpiR6>8ƟLz$Iƹ~Vhz>a7F^O8wciO* Xom\jЋ!m"le,oÙW_|U 4A+fnA/8ϵMJC*kn.{*f oVc<4<P$yiWcVf"o *]+/zWߥ $rL hV~Jå fvHra,M@,*gsf*SOrIqzbƋ Pyڂ,hrgw:AmBn]TFcZS^t*Xh* "&uQTm¯싏8h3nܱ?IJӒi,j͙Pa"Q 0T:szkȜ\2at/M ~e7ba8paBDz bOL_O`͋*ep5OW6l;{Up<>@*H˰ %P^O>i/[Y&,z Wa.R;Q4,lu Qq.*$P bN$u\KW[:b"C>ԅ<+B5>% asU6մw j4AXbC0$} nDȠ‡9)uw)Cʼ| Omz9eoc+e&ɌˢZ^o mԧ^S9/uӍkR,J5 8(Jz櫰X :=')VEH>7y=NX?TŪvՁqOF*&rnqٺ.:h?U0](&fE6Rc͞JN:6Zdy)`g u4_CbU^dnl0+ē@fN Yu} bm2OX#ͦOj& MLru™duvSie qɞè +W?ۇ:HlW?O%"J~{h[yAb8˗zg+ثY$AzIAfpeXx 8f/B$!3,l1#̸EhD|\TXL! Rq%kADClp&U4Ҟa`q`v][LvV{< V D,.QGԥrS_۾s ?IwX g Vj'Q>w: է[Q;Yo K Q}<2ΤN(!!믨gC?@Put"=Jtjh$Ѹ@^Dzӷ qn75lYmן@NvIoZM9쪡 k?Q'+_8 (Ik4 ѿxl9w^}V@ĬG"k? D>X KR__|oyvIl endstream endobj 224 0 obj << /Type /Page /Parent 531 0 R /Resources 225 0 R /Contents 226 0 R /Thumb 475 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 225 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 226 0 obj << /Length 8338 /Filter /FlateDecode >> stream G9llVZwwռ0oF#L0DjWz $2(waY~B:A̸ODt_Ⴗ<34kzJDXe051QX2kW)S#I( kq%&Zcv@d4C$Hy&BǙoSc0SP1 'E$Ćosktw鵩{TC^+\QQ 7'z\r *{,85C ++rXsW]C%0 < +tc W/YӹH-,ٌ)_/E6YhS.XKՄBG^ DPYg`IdwMGh+$jT*y;:! UF){Ǐ!Ork\8"ف8$#UE,M@h׫. drax 55s`!dm Qpmo^iq]wʭ3E"W%E {Ovl38ZDW-LlebwdD0Q bµ 89]Sƕ=}TvphG41㗀W6aQ&YiJ3e ,4Vߓ:_$#f6ЦbaS;Fb. p 6>ٷR7Eg8&P[-x9T_d0VL^q ;[ϯMw4̲ڣ{иTXU2yʺy+g奞gUEo(T, hTm2*dx6-RNCmA4[qk`Q wٞ/l5$WMO)qn[aC]rӀI⽧52+^T+or˶˧tIp1L(\ڣ5?g hRZyXHpcٶkac?W 4r#؉c9z!'q7fBdD:[7xSC5sfky</#X娧%N> K2>:؋ӈ>93s񇗚P-$Ju'KJLxִ*2u]Bw?E^mCn6{$:Syo𞁠$ tP'pfqCIL=4b! =N|uW2m"!7gO/tS 1Cv#Ѫ05/2x;R+2iH>N{TΊ&A~\@XY_kSl71\R6Rg ,ڒė&2$DSH"׃AE@!}r!;xbid(*K2wfXN|v*y!gD=yJ/iO3`KPk(rtb6J{E Lb霦l1|<܁v_f ,f4}@&Đ͵n;h$~\خ>{w4&԰{M$&C2g#'Jizu>S\M`Tz=BTNfn6M}6VWRJ0H _.܎D-@i#UKZ]'p,Wx;$u3}OJn14[ER#+<q 參JF62MRۄ\ koJs?,l"50Jcƒ s⡩dXmmxcT(J{`r}oTF{Cgij5~yP(j;&ǐhOB J^Tc]_3<299A@GJ< t3o~r ՝m4mm!O Qh) ~e^%XC0J410*99  c~&: w8 |ubI؞"k-50_LhvEG,)1 u2k[2)z:~gGz?$ҲO|n,j18cskOC9BYئO*z G[Z7{n@YR*_kՏU b  Ħ1m]gshz:C l/|VAa G< ս|8R[qr]]1MI6I̛H0b osIbs/bO٬'&il*}k|b! qC2XɘNgZA\Fk帱^as =Pat a$o _M< `r:k:ͥhDIElMMhFlЃ N_m&>^Lr&6cʢW#M7c`@n8Ľ_IT;Y/ε$C~FL{& W88*S;SO;v>lYtWiqtaTÌwǒt£)rF 4ރ}*}ŚFT.q / ~Ĭ>{S~8m$hsBr'ۚZU⥑huBb-ķ=Y9@C]_9w4Hy,zj$=*vZh?~TWq?MsB1m%+ypSoE̕-95{~DzL }vO2'6CKXbtx쾁6ѤmO~/uی}PzJrS&N!Ңk8|)@N+Mln˺Bp9Hb- ĶQa2Ybi,Ȓ"*+cו-!Y"%[Saeb31bncG Aa,$/DWB%<7Q.=oC?GUo]80qJz*[XS$SL0Uֹhu OU%#.W*FŐNrK?$1I2m8]qv>ۋ^gR+ҷ=iMwc ˨|@_ȤMʰZq3e3smav,h[9@;GASdRPGOxz wBKl~f ET'{agɷ~&ƚ }BsggNYw80}.[ek}54al LڽKZzɠ)jߡ}m}rH&ow96RD9M)6 +&)O:f2E|C,TTQ_ +$3v$HmZ}P&7Ķ N =@270e`؇,1>T/]D ١oη44L.`=+]OǴ&)si{L9(X*gkEH?Me Us6bmaE=lff`54AI#wBu1j؍X`,cڮ7vi坟e8M1"~^*9َI2i{fjqTN f 4K0Lb/fyin2{>qX ͂'`̞ Rb3fGەDJIRXxM/z,,Ԉd!A,,īY@RJ,xc ڜ ob,Hjh8$PD2< ]ùC8+!#/Xwe0@Dz@JEX1 d3>̐ e9vFKxI_6$xix3$&!oROaAᨻa]FP E0,1)dB) 0,U78bc.lm;_4+~Ž8CQ3bR/NO쮏q?8JI vWUtWFiVrGsJr]T8E &~7òU˶Uϐ{܎T,YC|| mH.u &V@FWpd4Тۦ (~uԆbSHms+!.nPGiQ%hbcaGgĹxu>-0nVoAR&3nf)r;fv}a9'jgXu]Pc3jt휔D6~Իm4q1Uh7_ 唕(x8G%v@ m̋T /9CcVd"a:RsO>~T65*(bMc gn^yE]Fh 2geY =.kFҊRND?QTF?`MIGd,{SF&siD[ FXOJH=GϮ -y9HG:g =q]c8 ӟ~vq#A:!dN|„g3Ps^d?Fŗ[H7++ kP^9j6^f% dj0T 7 2]@zcKR/r s5.h$U#o٠b{qaTD.5BОq v&aAV=oN 2= ;Ggdrkn<iRu4bi@fx۶i*"q{izTwßUEE>Y/rp=ɋr<8!—nAX5`#z]8lmKsρJ7v?;t1"XTة2 sp =İ6;/0qH'ٔof9lkyC#,:JH$[ac:u:KV|'o`bQ BXcrTq޺6@ُNl*(Ǖ[?N0J\Cj#rڗ`|_4 41)ZoǂiVo8BQX^蝛ȳt)wX#=Gc黧|CoߠA XCgMe7Y9_ !JUP[ުH < k^;b&5UZjǔSIl p{bqXܗ;ffI@Ea-0._'OG[|lTۼw򶂷BVҥՊH[ZzA?|r#("ZU*f8VfJ 0G5Ǖ@N:hDrܷ f0:Tdd2 Y̸`Kǭ$=2si;JA=Bb<~8$):dFqgPhkٮ"|RNș5x*'AI޳"p0I-SNJ8˓ (6A;pb=y]^*R+mӳԎ#ͷ};k(2^7]܋Jh7vnER*.ƇvyӨy9O|;H:z\sbKQP+] u!7Տ &PgD |.& gAc!1V8lVٱٚr}H$߁qu)&BkU(p l+QXW->joې2[D\d9A/)dA)QmQ='l\ǘABHqPWuVy7mP~{a~ ۻ7`/e?PmKd%[`@giUhS`[ ZbsK'ӣG)wZjzR{Ah.uTA M{Q]e<8tKρ8R*#fl1opG H/r!݊D!`+nu0Lc;B2׿K)> endobj 228 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /F4 295 0 R /F6 296 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 229 0 obj << /Length 2910 /Filter /FlateDecode >> stream !(;%=A_τ;H[n:׻|[B a3lαP7PewFZ~fvaT,r+iTSE>F% hF)f=VkOy/NMѓq1o͡>2cI}c ̢10waB cp7FoYe'aO ّSR7uVju}H )S9"]^'Yww 6L AR^i]׸հ8DNȾ,_b>SLʝ>Gu LFQ'(C>tUun?5v˕%w${=;2XnGVZ1=cSy"ǨE5[9DjG7rd&t]k:ecW8Ę0'8!tYQ^n/9U54G"_[hd|R\Eb2Ⅳr5|ؔKoelȸ>cP!Zi)>|;.NJehKom$O랁G+iL Mx3@Bh!3_}x6_[g׋H#)DU&v3ޓ2ٽKIv` AnHgNib0 gFMhĸ58hBC*qsH7^*OLo8K,ݓa=X{ -06i'O \U;)v5Wh@q0+d]0GMW"2٭}_ܷ)Y93d}a &ƀBųFsӷ@ڪ? Fr .޽ ymBLsQH_gMa<7򰌧 MoG%= 4 RO;Rj7a fCC֡>_ h_MQMg[9ќfHDMȿ`eGF3~F">;+fQoD4"~+z;mV z':`]Yz~k8r2N8h<ɠ4jtl$?w)ܼ7Oe"|K9gLbx *' %GLC%G%r>?싗 Lߚ53d)9MJUb{,nG;M&o,MΉ6Ek#/vc5[^ YK7\4+3~n9@83&DWײQ0<{ד ȡ{)XS{dOL)ȇ@]Db6B׶Ǔ,=y?D7vNMn12ń"hDŷwB?ezݦ8,OL (y WB]b("gCr0`-fc<&5!F:O5R{;Pu:2# (C+#wiH+yyG ݗ3ٱcÓYm#vwb8[tp~=+)-T, RUcPUbΟr{#ԏê֪Bo.* ;EYYvx@#=뿟] { 1 *㬭m븉: vBIAOCBroS0XT[@14ؖS> endobj 231 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 232 0 obj << /Length 1277 /Filter /FlateDecode >> stream jjۛ>Up)AF4޾1݅8kn)a}|U-². [瓞~IaO9(etJ͘"7В &bR=:NsR{Tzl+3tC+ke,w6Eji߼O>"* u-`܈'r9,9$w9vAe{R:l|S]du2u/}:K![=d|[ -1~lUfĻ{R|>Dq/< XB.1<s/oGP#íj}JǮT~a,MH+r Ȼ1ݷN냁 d_y"1IUmw5vhY}]$]ebSVqgRnFٝ/`LjP#,7+aCEvajn^/0I1_P[T _SmZ2Ѽ.{[ 9D`Xgç<$)ThH Ԧ<2y[uQDwF^Ix3# VGH=}ke endstream endobj 233 0 obj << /Type /Page /Parent 532 0 R /Resources 234 0 R /Contents 235 0 R /Thumb 481 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 234 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 235 0 obj << /Length 985 /Filter /FlateDecode >> stream ^Cʩieu#b(,pxC-FX$XyMBM{h _ʳT) *g}) >(ˬהVNnMHȒV 3T/;~[%@nMx3a7>hHS Ab#%2^:*gCS?,7!ͤ."V0y߇KU?$3GN(B8/_ȕЍ強Oφ|iK\YZ/`8 pkWCG jŗ|A*58)_Qi7ØrqeI|yK[t"3# Y$^ńDk$<=hXT"5-`UƀBS,@l|ڸv"l9%-Pq(ܐ k|Ш(~VYj|iڣ8ͮ\:[diHi9$'%>:ano.@ݝΧm^UgVWLj(YQat)x7Jϩ΃B/C+h8;(@*.MLTC jZ,NDlj~p* ؇N=ţP@@='$fx> =%1lrQX}O!%'oeD`,]E Q7fla1Z1׼Ge`UQc7h"Vj 46e]uȺ۫@d`"7kWv3l|#DΒQR1#>\0MQ/TX?Cgi# ! endstream endobj 236 0 obj << /Type /Page /Parent 532 0 R /Resources 237 0 R /Contents 238 0 R /Thumb 483 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 237 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 238 0 obj << /Length 5402 /Filter /FlateDecode >> stream ˹r.pxP:謑 {!1+,ϲ$ET/F)rC9Ew'_n,g?&Źkp #{HuR8F.M;~(Ӫ ,u> 2O~G{򙑀'@I&쫖IKBJzxOUxŅ5c?/EB?Xt"f6H~[˸6'dCڮZ6Kzp75 6.<.ꖉLww9 C GJB>ygi YzKt<vfнWk7JBάabۮ&6.qMhoK_ 1u,D7lb'E_sK#ؖ=!@9>\=P 4XQWe$L5=W?':ƩAi.7QظYB}:XOI8l˳t|As{.)B'^j#U"m4EӀڰ%gX2%:lIN=Np+c 'çeޒy['(]A`bk#y,Wy}DI'%z>#zSqQ]! çՕ!y+M  l.E}e4r (E -I8McjE(bHip әKf:r 6`80T<-uGPV [|T%jU4-vλmz'ށXu;>e^^0W:aB#$ ;.5E@}/VRt05uIDh_AhaTOdjqn:*U\vH HX tyK61؟&2)C<b)`IiJJ|$kkQ?_qmKnK CH?G5:#xE:N\.0)'sFO`M0,h6)3=kG ʕh]#w'9 EϥuBrF Iנmyw7/ST xwxbrzxV#m&=Ts2IU.Ƙ1t.x/2lWutCi q%y]dCrH=V dER޵bJ`M/R(a#b'*ev3ƱL#@[-I[=]i s #C] |TeFsy(ٛgRiJd6-Qk %"6<;gC?l?5 A!<`L83*+P5:g YyUxvhݞYWrD!/Q4=Q#Gl=?KNDduw Z,9ښQp\z`U1xџg @ma4TB.'鸢BC"Aѥ#A( N%nKޝ*22?|0V9uh !0o;x0B_[|]W0;^IPR-zH?O7f#M9or(u (#tk #VUQPeaw!+މ6J'Ft)3^)^36uh$ oĂGlzG߽!'uP-vu.k٧AX]&j W[8F+7GXό+՝@&>{վ!~e7t0(.4+#7,{1g;EYY5X\y΃p. L|zjlKZ@?2\Ǭ#v)3T,3b db- 0t9e7heN WsmD-7>ܔ~UDZuvM]jey͢g;<1|?a\T؊m$1A::vë'˄ke@q%n`:ucm%]T~N]$ ϯޗZ^xȉ}U`&s%m3$ 5;-A(&CO Z8lu7kXCJ{Ļyf,ےdv~]v2DLy|Q;Iqt߼(%Fpz9- ž]75z50'NuK<+1eJ _7,>J:FMM7& 0# [!8!%2iwa?IV+;<лxc#bVR C3}qTm >2NS*eż&48P:4C>$[sI}$5ђ`6`!.gZ%fp\2 RfܹD:U3 s2HEdh%P_i-h-[GG%}B%}G/lt#ۓ? B [IU/L񭤊6菢v8AZXVnqC#MPcoEK.׊tNqsy(6wt# wUǕb&m_xw;9YGoT W16Y7')X8W =XFșnQ6jgC~$[}5Ȇ9kȩ̼܏0u} oନP3=fK߉`LHB(v=@Gr$"ȕ5r9"\ʈJVknMW٪S3[-.{eMq X[@U/sW۱? 4IOQ\Lz/Ksғ z`_<̂/ȿ&k#E <1%ebX->*d'qX׀.lګ(/]r7 gdnV-CfŘzR69fvZ=&(QcѷsѪ$DV.qS 62@&e1Nj1Q^XOذ˭] v%Tk][\l! A@K{OY\[DcxoL BWaXnnWnT`z-%F31s$|D6{c}n1hO#LuWC~%(IZmqJ_}5a}' vG%]Y!}/r[*ȁDT&>lz[c} b`{L { ~$Ϭ&_<"pN6 \9#-Gέ*6 fMOGQT `giP aA0Iry4,Lp/q1-ϛGᐻSS0qf=٪z"s:i Z endstream endobj 239 0 obj << /Type /Page /Parent 532 0 R /Resources 240 0 R /Contents 241 0 R /Thumb 485 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 240 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 241 0 obj << /Length 5410 /Filter /FlateDecode >> stream :FH8m5޻)K$VhW1{pȑAWo6Nɠ<=9➫kDjt$\0l4yEu vM9~ EG,,at  X9 U3o< B-hόoq5(yRj ɬz'Ms ȋ~7s?QqʪLK}R lz%Gw%`٩ c!f\nq٠VQeDأB‡7)cЯHuJ|b"-_+MAOQ WbJ?;ٴ;oKpGOl9ݧv#wK[3m_) erJ#ǎ Ң,gvx ,ּW.bG15;wٲ+I[I刁_ 6(MiJl %D}M~/*)⎱;%Thb- K>2}$QҗC7${h]kZPFt;] `vHoTM"dI'+GfPu5HVq)^0X};oMmS{6EiPI3қq?M]ӘmMˎý ~e2.ۏd{v]r(?؈=y$ѣad߁D RAQH||u2GqGDoaG)fJVdN?\ s:H'U;hE-p=Xputi8IE5o.L| fՕ6J!og3Բt.$pAG5cZH עQ8$7.t!To,sto@<¯6@$@ dt|X\]3C %*o=yHgWI!G =&@-o=W"iQؘr*Ϣҁ!.CN48K5^<:,i[`1NQA4|o`Ky>>Aӿ+A{ɼAbh!K"{$ ++8qYw3TWpg0{ԎsNY0'Zv ǭDRuiԕ<0z%7ሜw =)ĕ<'u"-㔺Xx#4nAWx([ZhǰrES|#M-g }7/>]g=o.}>j.4/ШF*-5ۦ_[bڈ xnͫ<|nX [yޥ/fU%bF\q)t #n{.U_셕{E3?hRMgR͙̋WD6̀9ly l7Y+> ӉaWE K f Vhi &ث3.WD~c1D3h۬a.ބ3&3ȥqgcZ2v5/PyGPZ}P8(8Hx~UTJ>s: +tz!Z]@{y0yNx@zB13l-[e:Ȋ&U 鸔&|6EeY{ҮPU]8%T)>wBS:7ݓG@0#ɹrN$R|1bUC#q&\I+N'4Nݿo Ȯq\;o4m|7kSr+rqQ1v7ri^`M}g)3P#h$rCgK7<s?DJ 2 c2|)_Oylq'U1 8;c6$˳.ϖ)=;6ۇ-c̔ѶV ;vGe tIρk Sɿ U#` g0]+G:UJYT F%yM)P̌>TwhL;1cp5T \&eB데R3%y7qkN̵%f~OٸqM:e1p%<ٹ0Dno^tHV &߷Tr# `c KGjDĒiتΠ ^do7 ]il4PE=gh i_>(D4O3bxLyWn\HDJH5AQ7-+աfvOpM#o#!{O@KM0| іLYITcnH{DNsw(d[%бq٬jIYm! $˱&4^i1νHQ_.| LAưCh ˃{p(8gFuVmlꄤAh^q(}:.(wHP V.ռXrK80fFn=DԬÑX PeZn<}Vg 2V(zu#TΧ}ϜRbZ_@C%dw 㬵 yebF'[BaZP3 Ψ)wH^Wz"IS 3 7Ԑĭ#ROFS'.A," 0*3q^p-a&>_uT.֪W]%f3fr&xk^uG+-L꼔$[Ry0]:>Hɢ*N0ju#bێ5 چ_]tHiZW 6 k'UX7r$iFaXq)(xtcly22oHg2mXgɶjiz4@_nOm\  RYI[)1L|ԮTR!,D DxNV.ܥE Z%ỮPo't}mޔ|4E'~1>WL3&c)9<.smwt3HEH44'b}L m>̕CJFHA;.HTbZnEH1(tߗ,&a oB!6^]XO.0Q!dуОBȈX0-Kȅ ybx &1U+P4Vs>.&*! 3Zy$)c7⻪zGy4t []!hcmD`Z^Tn9Nߙu5i\>&ެ2qX]I\GlHCyݠh!>޸ߎJKFhzCS=B0(~A?#TSʂ)o_8 ?Ӯ: T=kS=wn>W@ +UOWATjH+li,{&DZeI1 *EԐ B6(ϱ9%s1Y0 3y@%v}z󣑩SȒp>|FjbCAů\qdU&&y ,ƙa/\M v,\ywIS5rZz<r\:kA9*0ABAB?:+\oSczsM&֒nB\u]D5%}kەw+ 8a-x endstream endobj 242 0 obj << /Type /Page /Parent 532 0 R /Resources 243 0 R /Contents 244 0 R /Thumb 487 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 243 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 244 0 obj << /Length 5576 /Filter /FlateDecode >> stream ܍ۜLKKEL/^R+$cAHeCO_["Ze&!0d!ѱC|%: ^b3!2~LAM KavWΎ;(u %>Z]D33l3'{5IC[zjF~b{;O.:Ĺլ"rڝ:ZC4D*] !uG)M05kR BDvM}i4mF(BIlh\j>S!CzS!j+4aAZj8e[qz,D[֎j/'Q> ɟWC}$L#IX Rl EIj? 8fLt&wY;NP&0bW>j8#Tb٩n s蟼\I*5m΢cf7br8 Yl&Y"\Kkxn :M7 ʆ7zS@u7NsUO%Ria2OMzmc`;Fsei7?m,w+ wgWs(vHDP׶^Vu/$P~> a䇁PDxf0g4Lr͘{٥#zL" 5% -T juE< WP9( )} t>R2zRD ӈqouN嶜Ol]))I2뾞PzSQRAg>[fr`C$B]J5, p*NyUiҖC*VjK!56;Taj {x,\#[i?uGI2x՗>)${M%XT[hz3kjC{)&jCN"F9T;Y+DH`eK-D+ډ+^M>?Z3*yV|â6PpH*m vxUho," m7?[aܘ&l'L>Rx %%@#ɗA*hW2295R,s--Պ/T=ngt߃na~[- Hu+q:-4zxSO%} \\r3sЯ]eQsؽ@@(\2'[^o>dG MЊӢVZfRC$kɜ a*nH"! pmNmB;ce>ټWv%а 5h⯐"ȡZ:J)5*͑ jʣMF,OJ eRx'KKzj ~hAL.m)[%3xAx$S{h>*#nf;v wulG s<&U|euV dnq ӿݬX H)HDf6OMRmƃolRˆ:P~4 'E:3SD&kgvw'}shtU#4{oK_qSӁ̓K]\I$d0&2fDX0"'E$N@;EWkK Vm x76NW[a8[e`vj'ϖ-AΠb) I+nx fU$i3C{u1`mCaP?!OZѶ "3i<ޭhɉk7xEb^JVasn_HYj=GK[ꎦ`ti%0 JF9M)2zwjy6{ʳ`SF C'R1K~ h**yVVBy( J;y'dl&hrf)Wfd#02̢nm Tン8B/,^$*e>(|?ˇ&LmXЂAK:;dQ EJ"mGILqQJ0\i2,Ce>)#WTn N?z+DKCKᵪ N%i%ꦄȬ>[s() Wll_)HϏhvOلheQT ?lc8Y$mm«f/nP~ -!nhe.B?[~L\5%ѮUD_X{>!! _FYKY: Y1[:eU oFb׼G-iӘ TbDL\u0{apyM>#<_Q@eX8"QKi${^\X |LE7+KZϹ?o7m39&Xo3p,On[_rƿ. }DU6h~mrPMAq?jGsWFsf_:5oFVO7FpWH@3膌{qBx8$ӤNV<D(ZmT$5u XdLiM߃ Qˍjwd OfޜFBQxeΉۨӗ<;& XE.^ U2YN0BGn6ӝpN}\ߊl`#kcO:W>$ľR x7# })Dw]%Hyz8YՈlR ;! zRUxE?61f䦼Aն)7@ endstream endobj 245 0 obj << /Type /Page /Parent 532 0 R /Resources 246 0 R /Contents 247 0 R /Thumb 489 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 246 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 247 0 obj << /Length 5798 /Filter /FlateDecode >> stream ːcAg6~џKO:fyD|a-{^陓!FM.Ns(俱$ k6 n{!:3e,(>:.A"tW@Js# f)MfHjQL^\2Δ4̓2.h0kUT]HN&nR,(Blg1VFW"9K)ISxliyo͍?c/gYӋc|&2xukGn?oz 7%`gma8] !Ipu{r.ώ{Zak~L9!; v.HV O1hiU7 =v Ÿk斉f;ɋH6Rd>8zyWw=RV =/eD/|~AucvcI1~4ל z իJDUę}7;k,/zN-!)UқTES83 J v~ү7G1![']A#qscu"v"$Q>6#I>siAQܣɪEXdyhya]ȄLWx4܎G]F8HT|B!Hs=0f.icp?kn?k2Aa굃Aݟx\sП6Ȼ :DTm&QAJ7ǛjDVk7HżwZSP@7Li:`o22T',b.қuQiky>\*dYDYz;+ju o%dș$~=l(0RcZ*K /NxVMohbw­3goS6=TTvlA?d 0 BqzÃ| (vdghj>`2i@T꒨q]#|#Y|P-=s8l;́X6Iަ9-$Q*ycj9W kľ['CxڜE?*=ٱ%:\ODG]:@v> ΍Pg^.Ӎa2LM{\J@^zECv7Bgi Y|_Iuܕ_ܫ{;2%qFC"Lg{B:eEz=Q ̻mMe}i YQh);s"S ퟺf$Np=aA5h OGSdyv+󗅦KMm %F-AT|5'EtPXo3 2)?1nK]":No)KU O%3zM~n8iHmK>$-?rhٔ _a- ̃6hc{9e~9ڲtnGsK:;}saͅnLS/eE@Ԧ@ ̄4jdT:!2>V?t,Ug8{(0ihMQ=oѲU^׸FxͷvTcrKb #oʺCٛ-n3jXݤ55=[~spIJ Sǵ%10ʇy)z&'HawHB7|.}vM7QIM-"e׀ 9Q(݂v߼Ka#U!  iފ!I?4nL9.8/9*ގmJ{)D'5ӞaH5uygc2ƒ0T#3/DMx'5V״\f?^yq3E9VnVә](o|3R<ݜHj>e}b[je75 ;(sRnP(׫*k.G)r`{}`R8&A_%kX7~R)ws ?Ѓ+eOs[d]i*-u hG=ϖ٩Y|$<$\\ib2W//1yI·|c=A|MMZԫDU ~0?u0 GAB,ŏr;U/vB ?5Uw]X!d fm8fE++;cX,ʡŜvCERMPu^&FmX~g~){-\dṚ>ha9lYXh*ڜNAip{~_ n[D r R77F,_izJ@!C8-!|T /x{(eڣ1ZcJ]MN6xN"{rX~J,DE27.Ȯ:K)"fi$ܟI#kE~mXnlޓEݞ>Eh99ܼ\JWxfDY,N;2'Me41\)EkL7ycꂁFb* 8WH9CM)Ʉ?rv(#ъF@YuX ^0!Ao ߴLGWSZx<[(}T)[Fۍ:gN[98LvƦbQ&pcI1 \T]7'y>}|) )tp< !"s>-#?|I ,6z?oh7Z~??~X_C!$_~:b ͭ#6dTFLzheJ5RQ[(gylo~ 1kt֍&!ƌo͉ɡ FGIw'v:h_}>2ThKP؏Hpܙ{ ^[;QiSC3af5EIY֓{maÀ+0#'=Ewb.,X`B:gM՟Fžzv@ 9z] %m`uC-)^CHMpX>D j$WfL3,NӞr#IJ'jVlFג+=TeOGs6TPSwWJo";Sq=IzOpbcq*P{iLFV"IL/,/:}DRZ bޕզ6\`&@2dHX4zjO K:I~9i*LgW</&-';䥩p]PKV0ڣdY /3<(kRq\"1Cjh"vɬ.``?crC/>-k#hl?ڧv{#Gn6)5n&30ET+g̹[^f'W}3newf%`P[GU*9v5a = b*YቑxR2hYi>WG }BA-\\HLA񟒠⍺wGA~2ƌl]A7ޔkOWNr@.9my!A.t沣 v/(ifxИ}' r`rwfZbKu24 1W w¶-P}g2զ°ʢG]A,F1i=V"'N62`Ә'>a,s kCh"%#A#lK}Ľx[twL(#t%I(Co g!FnR&Z#Ƣf %71k q`J],r~v&tW!Ps*NݮvnX.~ 'ۛv V *:%76η3 Qdj3oH2f7 `v}8R6҉z/젓0CyUSY_4F"O`:@ɦEncƽDL/ȎT jb}(I[;<ߝ1zxaj`Z9xki8ŭe jo%JrZVСN(nlsRH |ե;VrNH"._+5G0" +2ܻhtK Sg$+P MRؘl_ dlI&KȝWTdձЪd*ď5A^o hw&9H] 5Dx*?+]2Y+g|Nmž#TDKWAcQ(%pfsDx/^h}M6Stc3Q@ٝdzT NָFBE)ul 8w+էQW]1nh2jf,8)n'΢g*;;V,$h0^$#4OY=i24nf%3p@\ ݀h endstream endobj 248 0 obj << /Type /Page /Parent 532 0 R /Resources 249 0 R /Contents 250 0 R /Thumb 491 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 249 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 250 0 obj << /Length 4239 /Filter /FlateDecode >> stream j~`!4o1x0XvhnV2u>5Bf>3͎U'\$c_Il ϶\.޿C'FDɱ`Hz@gu)a1^s o?,+8c fWK9wb6R1lG9#ϱ;y7I_;-D[K.qM-G٭d6 "  ew5ܩ#?Iѩ1Y|?u I ՗A.iXlX ӒWdFenYK 4i m)05QJ4nN4ٷ^?MESpP[N1m 1YvdY܅-=a:l ٫]!G%=)PQ/i8xp,K96KtE밦4vM $/hIl 0Zɴb(N%"ğc]r|`sPe${F٭r)\*;9-Ŀg)u[֎j2_g;=O^7T~E?zcݴ"W72QeB'}+Sk̺/꿲lS6_?+$> d]EJiCofI"H_A6&c`S]^4RSLbg@I Sfx} ioQKGRǍgM^[KV kiR%vKą x[c ?2Eי5A{ ڱu-_όȅM\na(eB2w]L ʲv`Ty YjQr\B]+hɭW# YH[+ŝof>>N|+1 ^ϴlSkr׳*f%M_~U?UTCWnϏ"TۄUg)xE\(?3^_SP`D?WD?@y]B&ϺW/ x*s3$=:9q4vڶ@KSI41ݯ5kRM5 s 겱 b ۉyCU$)cPoFXx@IPP;>& _'(6 zS %Z/xO$b78}i[+QRRB J'x)S2$zMNT5Qwcynn=q4 yڟ@%\8qq$g]Ys"ڞWtxoyo-sODK%FE!n̝P OusjKUame嶓TǾ>k7K+}ZY b}.·9D IY8kҎtNeLVewvƓnW뭜ji-p\F`$=BJ-0x6g/iÙ9gOpӿ;3T!\@\Frݼ2NPp%yI¯tХn +~̀GI5)v\NőD*,yLhzl ~'DPv81g+M "d 47iry \#C?γVW3hhzOpGH{Lcdoj1[·]N]a=ѬBD &&uivq VEʋoe ۈZ\ ~cC PzAi*7>Ix\N-9u |tnճGO6ݐ%ېSJvt!J,]-ʧHJہ䇺3\LlH'8@1xKbEw(,aR&(M"RH&5y(0 LdktS*FXՆ_ӵcWR^*ךKUˉ/Nj>E'cpQ􏏍 u?s N]zC0Ya}|R la)q KkÛj !bcʹfB},[ԭQyE&=]D UOAI U8AwÏ ;`IQ}CkUz(:㏱yS+0E+uGc_(^*D$&=s=FcRIaUnkbW8cf[M9Q0({{S.6z3s_AH۶ҋo2kϣA%? ۧD܊LF:Y_㞲xIz׺$6N:F6T l+Dc&K'HXV"X4?^mLLASdh>~dw@ Gg~T_Q[qJ]ڠʟ{[eqU8hĔ7+C#HՌE>7dr@Cq ^/dQ<Ri2M.Ynl3_:UC'u`z!l| 0^;mB f!lW#)vV C[zis_4#1h#x<1e?$:O>~sH/Ts\f9GNF^~ճS;1(b 3Rʔ/vweSyMqS /I- 䕞;:?|(߅ϪB=,Z%Zh؞ flG Ȏ_"e0%'&i8ޏ´*! C'*(BOЀ^:Fuy\rrY*/|%$nj*vISÙY"Y+q;J׾fonjbF1ۂ4}, \TȲ)nN[$!%h!R['UYꇣ zzeSD`M+6OFaDN~Q * oLDm)jޱd<9SHݛ2@ pakSws+;7dW ?(~Jtp5n>y9eFG\ y[W~Jdt,xR$AԐklp P۰T .E-'/jOwcXBwY4MGnޥЫ Su)<B++$FLv{gCCvYcl%4prh{5ConTȥq_T9n(rO.h֩ PWUqΖE2pA0n ol]J~|jecO\l}kФ1b佫  ʣ endstream endobj 251 0 obj << /Type /Page /Parent 532 0 R /Resources 252 0 R /Contents 253 0 R /Thumb 493 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 252 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 253 0 obj << /Length 6648 /Filter /FlateDecode >> stream ۤ`zV \<ɶ :b6%Dw_%o' \ʛY#.e9pIlv=u7Zn0acQEњ`7^=;ax3!ԒiQ/E߿p߯ }J*_Y`ruQ0yb™?bS0lR•TP\@7P+"Y?35z/xM_12 UL?SR)1nG۰@]^$SZP_)LSn"D/۽>:8qƑ5 R\ ?UDH/œЮZJM;ERɽM  \M]r hrh f 8eΊXxȺ c.`+Wz_Q,"4 i!@[enS=ZDzw6BDIG3{#-^5~cnCltQDvwɓ%% R z9_k DDm!Ea tzdy>$}Vz Hp4̯JR}@5P#Efib6UY`7(^[PpSdI,o4\ D :#(fhN%7_7c{#df Bl8AR7)]<% YD@L`kp5ݽ {WIڞ?kj ; 4#Y}V ?q~!ЍA+ Gi7e=ϊ֖>$aYK=̅-k{ ! ~+9_'ED]OPziHl-JN 7OjJ$CbA8^}B5込!_ 8 /<2Dwii5.bXuȮ[HFJQ=g !~Jfz>#^ /}Lg%?$ 0\n4,3{wl"S1Rol: U@X`~%ɵʶiC;#Jm۶=2{`'u5<1 @sD0$-!N/ oQJpKI䕂xm1ȕù*J[PY1PĬg #,TgNޝM+-@IX פhOźuHӷxp6-$yC sІ EzI}`6 v .e?;hv H9S# 69(QޥcBb`f)Ca,vK~4= 9qO6%cZ48&4} Vӧ.Incs|!Vn5Uz{+Z?.]>:+HӡGWϝAU684]"ލ9niS!@^?|o^ \&aS9f@\o))ռb O*J;RY0s P)i7DLdBؚi9Ӡ.|Q g홎 ~>J06YOl PYkfyj]8B̋eߺt:-ږC1 .}_c1D!yvOP9])%\u⠕AGTUA58cO騧,(4+zp P,̻/T{6? e՚",v%؝91^]՘z΃B LJf@D'8h hǹoDNnǤ:Xg w2W9QHkh53Bz^qv>oL]Z[d)5 CKT޿K&)6:s!-=\K\6rU /7,n\2hqw{5zӊZ$?fZl:#Q~<#}l[Y Y*$[E^PCS`Y5u~?!@/O&7<.\R;y"M{~7qXWu+,x#!Sa23" wZxԟM8զ^ I^pDGT&OlGޱ. SK̮եQnr4ǿsDS!,OC[o8Za-a`kqHǨZϛ篣䎜{{ZN#@ opUߥtyo ɹB"챼9ߘ­Ȥ` nhuvDgzk=Cpi.|ȕ F\CajB}d٭EL3YNEɤB!"m .߳fʐIRM\Tl;^/ch)! sJA"ck 8T]D p7D~D`xctNT= h}>ѣŮ/($z9e2gXwT`{V.E/ņP Gkv]=t֠Dh[aq`Wvot!ǼUa`? 1T~PFhs L{Ho)C F Ϯy.B.pv-<y ]'X xUMjW>I\H/=cʦkt.qra@1;*doE/8Ḑmw)յjpc5eY3W.dV"03'e1>P=܏f_>/@{`j:/<⑓3|"O@w-HRwXu<Ѱ) e.pqF ڠU$x.ΗiIs2{F`6 Wkjxsj)"[s 9<ICc|`vi|GV Tʁ.}ԈkqҾbY>>C /;KY>jhhWL(_ZR-sISAnl6uۋ;{E٪{B")lU"'GMh}R% o8V 9qkP8}926> CL蛙21ۉPtP(8\E;r IJhMJKeBBWHB?{&٩|W3˛Kq7C/,"(xǼva&۩JNJoTVl"MʼLEFR^w0;O]{]ortat{jjrDn|TlI60P/Q! bMn5PzM|=<&1Z1&4qwvm\+풸JJJ1k h'z*Xm ߛ v$rcݚC\uh _iY܇tJJQtLfBX-tM8+}qԖ\%tŠ(cI R6nH ѿ˸+x_h!m&^#Agc)z$T;':J_o Qj@隝NϙnLi-#QU+FâZ{M R)L$ϭ5#hv ? )?WSv(Xn [nQ7oӻG-} (<JR%&`ZxOTޭɒBT:̢@6uӸ%a( PVê I:(/uأ]iЯn$|;V٦CrWqqO_ƐV8crB^  #|J=KRNtZ#1!H ۂCQ(R .LB] },ZwU̜.;+4K{/=uLQ3>%`⦏իlHywd+Öx#tGգrʴKzx^ ?ek3^ [/-#q QF%ޠ`;Lk1M \װ<Ǖƴ6z&%wR2Y}^3<+(i7'x1 ߨ)ʂc#[WI3$BcX52Ls~;REA'=W@yP q q؊jя%D%@p,WEl?SY2 WxNF ͅu Xln2@}þpxæꚟe%oƅ6gƋ2ѽT%=V> ɥP 3:4YD`AyU[n&2Aj@k!Cΐ endstream endobj 254 0 obj << /Type /Page /Parent 532 0 R /Resources 255 0 R /Contents 256 0 R /Thumb 495 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 255 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 256 0 obj << /Length 5352 /Filter /FlateDecode >> stream OrN0VE[po=T=I Z4W3JWXps6d1ʡj6F{M2u0*F)FuO_۪ݦVaT2{"$o=1ݽSXVr isP a"' )_+%F)[شEp*9uh3<dc>%U%bغ!ȼ>5:A6 a ? Dmݡ0ƆΙxF}bP.6<0(IDA*q*:b m-!uj ׈jB*CRwE;pDU][5a.lk5D4 LIrL 'RQha68&5_TN7Qfv nU@귞\ "t%~˜9_c'Pc2OwuZqFiks}%YItDJpkPRNwJ܎XߝʻucdWOB)剹X"ah"V/ɖ4 e%z~UMݪLA?YK!ctq{gx`|&w}( Qd^U۽IB!:UVfnꋵ/Tԇ;1 NNDIkmIڍLOMJc$+,a;4dErK!KQ3f8F-JiՕR74[D݉MNvőAJC\GO-Wb|{L4@w5#9Jzc9Wԩy0ۭf3EGmSe[i&U,䶗Ot'LupRvxXfWDܷs2j-N!@Yhl0j>`l p\ <ٙ& LWe =>?+ԐR~SUـJؖ; #{zoN7%4<4iH@P5X2`umMYQ8^nj KHIo_8XWtJj5qֽgv! 9☒M"K"UB )ѱ|Gg]ԛv)?ѩOcOyǺp# iu -[ hxSm2Md;P~4-efH@an:bAZ Dړ740 ~JD x+.>\};%Q< "H1'gE|k*dlR^*|ru1޿SZLFS$\/%kl_ M&I~@l&_ΖHߔi>XFV'C@I"|L֘"HБ5.,#*CA3(@fr?LJl$~kTϫ|W&1"MŽ[cCbzh.t atOU,:2˕D7j#]&I_fJ>},JIIsCc{jGIva65e嶢#oͰBT6 򑧰W+QAIu9(>}leb^Gn± 5={fA667wy" d4#DLh$z +,U APbmF ̛^ nwO{^? #p*e?5,@ c!6KEL&,S9aGm2Yyt2;)gK̏.=`po2r:V##r'i ,H d 7^JAkL9M~=&6@3  xnugU!~|3Ywm>K{Y=\[?E7{HwT&+ hm^6Y몃H 'j_׆A @gQIkmFZ촅Bm=7st.EnN 3qsr[>G2S%LS3$iZ}!1};c* z5"UC8iXu]d~?8/A9Tz/B" /c!VULX.7))%O-`˷fh\C9S*gG$^sdtڗ-: !N E[NӝãQƗRA*;8ue: Rvꯐ3Vl%vj#3s0ԟg9#ǃ&M(M`:I9y]'V8^8-nc~0 5r x/$I wcE/S˧C"W7 k$M:]Ž3et1.]zH/b㌈0Hضk&> z$8_I-fN4w ?E5Bj|KKX|SQ( du 7"2s$2yLwqgp#.dy +\Q^p8gE׮u < %*|[4, "E~ 92> endobj 258 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 259 0 obj << /Length 4662 /Filter /FlateDecode >> stream ^.PQz˚W΅jac0}>~:Kݨ 0ŞaMC9uڸ"[Vc:g-a.TSn%Б3l_G?φy2SanSI3 Ջ9D.cdWpMJZ~vFJ&W GMzIn ʥU`Qބzc _Hl  Bd!mdLP"3[!&CQzP&UyzvJ ڐW:؋vVg 3u{p#BBO!OBZaẼ*#^yBEA8# ^èIJ@mH9qfۗ-pǧ60g}=h=\It*3$ZprA vIXhvrkd# RnS#24;g1~[ tgq{NvYM4;v%~+cG/&3RwD%&LrBdP|~ /!d" ١70"s .Ҵh,Fe%a.hKR\ga>ϯ1)G})Va[2ҚcIC;cdM!-bHdjYx_~T׼]"c7S'4b)U"snܑJk_q-pj1Wh #C==G03tX 5D?-e О(,2:Y{sEI"l?cmʦaM{+h,ÙjtCV,b$5kz27E$h!%~l: Qo l(٬}0Yo&|x9a ь- `"|Y!' [OIV!?3X5l'6$sbOafx5R/fv;Skp7&g^"}l[n{k*w^cXw~Ni>K" OH潶 -Y`%ON¢04M̝<L3zlUʪHT;K_m@;rG;+c{f}Y*Ep JX%L _i#I 8 D08Dǒ}tV飬W\ L5d[\Y*n5\@A2 Z܉j7*\pݒ(e+T;2gƭ!tUvJ*r/}G2kL}^緫y_\MOM~7UT래㱤;Ս1xI&tU71C$Mn@ЬݡZ0HNV)H18$D+>@~XeT'p<ԃC$7`X&|s:A_KjTZ7X܋Tt~:=APa4bz-3a1.YX{#g 3. S86uT,^UE{UN3<2y)`(-֬/<}odf8VӰ k|DEevH\&3XI^ %Ƣsg/]OfH|ibk8#2:z.lSț -\5xȈn#Xj1s'Np˷85h z33([y] Ul˞ ECm*༊D50wVղT jK?) x.8չ[ ׸oI5-s4hɸ+ևYvEפ{:ߣ>).7mXlH+FH}1 kRnkÌ4QIPQ{PB}W9EG>3Aɹ]+NIn ӊ͒<ҩsz>k >M;D}Φ@M;5$nե`^=߭beүg^a ɿgSۜs-9hn:ɕ|6弣#SYF~9(KC(h& ⻘ڕ;.8aTY=DI U:M3^\5y1q*䜖8wXY닑ȍX׭A!-AH-98Fҝ9@^XQ6`tNA' 5~]뒧BjkW@{ wė0Ѕ6` x$łv&8:R@<hfQ~Wb;C mbDLAJ¤GrUXyySr1Otv)4GQ.ź9<]<\?RНpSVw5҄\]40-+NIc28T_|jj(fAӄzMd(c 3+)4; 3+7#cKv"r%shqБx 9MǢ^sN.Cz -ɾ~F҇rqV;0+6FMu_Wyk^|öl"<y)!H˨0  ^_hHukp endstream endobj 260 0 obj << /Type /Page /Parent 533 0 R /Resources 261 0 R /Contents 262 0 R /Thumb 499 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 261 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 262 0 obj << /Length 5255 /Filter /FlateDecode >> stream 5qQ袸L{| Mv ,8xJ8n1o}a,rmxxOB:_lֱ0's0dh)QՔ*{_a帱ɨp&3oV9^8C-gK&սGgK ֎_Xk&8?}h =#mZdL6 `b@v/eC^ kj YhpX֏S!/ԐoG_]5.8ceR.+pIQ4 glF+񭱯9W(IgٳIqŒ۰v"&5 TήF]yYMX@u6ؼ-# fҘ+ZPRmMA6/ ~V Z`VȄ12EpoA0͌nm yڵ@R1CAOO&+#&2Rl KOܭH7c7{TψgPyұ{4jN=wڡʊ.8p\  jmq` x"SsDZ@+>v,muXH8$zQkc@T!m8Bfe2mzC}jQ&>񪷴8 AdAyL' ޹!zKcaYdUx!4U@Ƿ釥۩;f7xoq(@WEr-9 & Izi׏RӶ;Q% c\nvuEEar}ZJ#Y.L} T}> Hl'ppK2"]^;mjr K^<@a ]X$5s'my@N2["F"$۷8ecc,X!r;NUʬb Cgf#0`,%8p2% X^`O (BnPEF'xPa}w' TiTI~ ` ܣ;K(u8U\pߜ%(J(VaEǒ)򃹓68(*/kEo7픳%pOƲfY%/zfD@X'󓊖ʓ U{rE^K|Ϩ}ǵŽDZre̗hS (O: 3\-k1/ o%WkG$>j8r[zo2w#;-ֶn4{4qQ O|#:5kzvy`Wbv"@j37;IfM`@xQDT%ꐊF=3ֹC|9yVPMA9rOk)k ;[ qxr2wL O g0nN󄔾_H=W#9 S>2$ _|3)uHVlִY hrS1rkQEg^zj}-ЅUwބ{ ObT/2Wk&Y:Idow} ] ҡ} l ڼ6]>ʁ sP"5Ix3 –9rBD`0&~z|\3UM4t㑑L_ϗyǽNY:Qhtsct3ZbVzNv (v~?giGb OǠe&ӏܥjm!%$#B #ɕHJRErWa(6<ݑU"׍!d4O9K9 "bF>oʵͩVXJFG)|~m_-sa-yR8 ō׎#42Um\sZnx@Ƭ ;QG Gא'_\hE"woj;6>ɀ884ӽ넖\mu_K<Գ>-V($s3d,L OS  mw˫(ItPkYn,IKh6a(Mvmf 9I8l螌ΡG+\kM$L?ciymUȕk6k_XA]U⦢_/TӋ2'<+Z&)NwWƪej"AW[OSF}BiOI#?v~M+ɽBָ"596"KHn9zmܠ4TiCLRAdy%P-NlH_F=P}۟@YdchD i.-aSDcuI~ %ʈ*z"BtbjYͤ 5n>%yX~hAU:VHĠ(!E (D碊&U /P2Zz:em|z,i` d{S7=@ M'T-1 LOiQ_Q\miiWqwdE*էDjnĦ ֆ͠ɂnx:,Q}$[˷,4uQ| !dKx/񗿗,s?-#d ey,TOΕ]Iid{g yMA~wtv %CF=9\E3YnȽA̺r"tºf4yGibdh״<q5?ټ +t2Զ].":y#f,\tu>%rGNJ^?S;[?㕧py4fYC&)d yOŻ7ɲa97j]\)eu ?k>kboIJ~)fMEJxM^~'CuCXw@]w( +l bqe3Vyx&3\ALlHZ@.޲=V> H\zMKmuԥϏp8_S qa xf mMVR\ƫz 4P#l"HZFGt(xy;d":hXT=(,[ٷ`'=5xF  :MRkL׸Qf0(=cw=%MN7Ɣ&n-Ts Ub7%^h#[&z)3^G%@ܞ= 8ܩe@A_~}*1xqc@dI,+Ari n"eU褕iA F%5v RK. ]2AbY0lz2WVڅ3y&PC!zBԒ&AN ꟀK.1u*"\ Xo5XĒ?^u ɲ}ͩ;&ndN >DRU*^R ="þH~kiYe98<彝jVT6gqj)oDf:聯)^q\p\fb%㕩 /OnIғ5˦Kk !X9.17Rȏ#-w`:'`X&!sisE:8gWۥZS2H"IL^J{:$*f%W qNhw^֜.X9o{.'=P9I=vSDG{i=pTFNt~vAKZĊA &YuLNG+]5(5OF7BW s>8ڏյ*k&|ڄYx܆:ew}N⫖v7ēg%3~ְUWhfC>*/p'9 >Rj 8C9}KI[1ŏ4PY>PJ]~O &<FFs] %d"Vte7N}X~(ZWDa6N:Dqc)UʙJGNwmc-t=_oAX}1I%l(?OAa u0N 3$+ *:/Ƃ(tK5pz%,I%F*V2C؊Mm,`Lof[!2n{`V-ԍ?F΃v֧ݩشmũ 1e2Q`;3r܅  b endstream endobj 263 0 obj << /Type /Page /Parent 533 0 R /Resources 264 0 R /Contents 265 0 R /Thumb 501 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 264 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 265 0 obj << /Length 2313 /Filter /FlateDecode >> stream )-7"/MtnRP}y *yl|wL:wX\uوՋ|:AWb ,}Jw#V~Z#_swZO&䦨wIDoT3]17"K0ttaD+V/6Q *`\ayj:lE`9Isߛ ʺB'~;8_.el;Zh`JDw+&@ݐz)!b$R _4c*W}x^)I}#+&-6 aQW\f(%k܂Jp' L<z'y6PJiWʇֺQOH?#d_QM lC*Kߑ,$w˹aڃ ˆf>;|ۢȠb gB Zч &.+.8R:ul n6K0yq:KT Hԁ -Q>1mX9<Bm d FVzK:[!fC;A'6LŐeЬB 'h!$ Y7qo_.u_%` 4e 8g5 QkNx횈ް0dK*Ɣf $Y3tq[-EMx{Aqk/ q:w]^%~oJo=3w;MuZN➸H;guto)fddXS2{kMݙE?jos@OWi>SSe"C+ (/]7{!z):k)jax'5/(H_GأBtJ@Vʴ(Cda8?ӚNAd:PB_I" X)Npdo}4Lr)eT wf;Dnm_;'ZJ #|#o`I 61TrHFeSHI(/ɲT\nV Q.>RXchĒ "S+֜Ն9OuKɱAd{xXkb6o;_EBW!tڼ0py6D-Jq̄8 jjq&;=dqrp3Gc1fP۹lG C+?BTD10y$WY2o!Av97{ Q#2-ȮBqmP5{rE9% $݈ w*NDcU5 [//)0r# etrpʀOe<]@6NURbVx[C- h]Tkn8\=tOuoh.ËOFY*e_ qYSbi3AHr6 ~ endstream endobj 266 0 obj << /Type /Page /Parent 533 0 R /Resources 267 0 R /Contents 268 0 R /Thumb 503 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 267 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 268 0 obj << /Length 5054 /Filter /FlateDecode >> stream \[.1fJj"*M ).mFǦǷyv!&̉71^$1Mn<8g8;=ՆM]9S/w/nzq9+ 4y xGKLKr*2 p|U_shBX@Q7cj]&ۮ gwf-#㴋u*YsB{NJxUل{JN9Tϸ Mmh6o禀191B7@q~B픿DTY=m Ě?}~~HOCٷ^ QK;7 43TX~+g ŎtI%kM"M)t?dx~;B//ȡUg#=KCeLl!^ 4qWu94EGefXyS@Ax.2SI|B!v#@(à+)d3[д lщݫDyߗA|y6cje Rb+ԍy+@߬w 7x| p6o qn5P"4)c>4[c oQdkњ׍}1Z m&˻ schTٱ"Bw#ۚNi5r+:lxUbېbӞ>ĸKٶNf akJ<$a].0:~ė?zEװz6=2 rܯUAxD5Kǜ@qRᲫ97&eiݲ58Z4R7MYK^v c `L3 -l=MX9l_OONRC@$EGuvb6Xq.* уZ+ߐsP@SL^?:ҮRi9Ÿ;&ڗѡb!ֺK|9<ȆSDߛ;JˁZD΄ffݯA $g221܇bSqٯF9e@D,BI=ͦ(Jٶ[%&U "si6ȵ-ë3*Jpsm#/:vt7DHES,v^@zgpXZcPa}_s6z5Ҳ0vFlMTrYx{{봑l>LkqlE!rx>1Ƣm|W1~9buDfӓx>M^)_@Sr-lRn\ *$j|R'MT1mUqļYlRlѥOʇZwvWʠt2^*aU9kZir í]$.STwufwoRR݁nX"i\va0E Z/=]~\1fa$V#c~C`CޯE)1x.s@K4(=cK /vߺ5@Jmb4f6]PhG>՚UB40EvMGm1OGy}o]UErRFG^h\9d x uMtj6Ć`'`pQY6&tvY6WCecy@&Il-bz[Y\pJ " ?F|a/$uQdXDan]I84}<&}@EQ^t_`;*MC;0d螦y SAjPSd[ =J2 hZEuFH!`.Ra˻5qĤbU+"dM!%-$V$?.w9?3}Xs~PAM)ߏJcz!\xp6q/-Q䁕=|8^S MDdam.$!Z%k֒pV"bN1˾rΝDȿėß{N1<8lnUe3M!Y=|{~ puA"𡪞:oȠP|lPp#* ?!C~KŏhL}tO9&p+SP8v=i DcxadxW7fwˠu^&iR)r;,$GJ=5(&l:1*(E(ܞn`oH˞۳[`+#IM2YG"%FſL%qB@aհ ٓ@ 8D=d\tŁrjOu_0Mlx澯$((0Jp?J@E"Ic_K)3e _omColZ~MFʽ"@K|:cV@|J45y}n;˫x~6Cq,QRs΅5N97#=fyjG9ۃGaV|j&Kj~^\hp)j0hmiW~]!.mFjIoO: ܱjqH:ic`sUfһ8T=JFMx$A 6JQ.N$姣 ~d=iNA{d+fuiIeIB]sIaʚ_o?۫ 6qCFS%}ܩ(6S=9G-&ϒ7aN6ַ(ޢ;o$lL)#lM#>'go!DRH4Ib*-|Hd!5S*om%.o:(.g ug븵ujG-YVm?h"oZ .l?Petk&~?5ѕɹK;ɷ=0?-R#awe &Dդ7حE jriBYM)lK5t|/<>^,-Y=s 6q1r9"H,lt!t*t0%evwjRXB2K& <"{22> cc"& 85$\GFjC!,צC`3:ͪ2`}uzY<,}ڸM(S/5dSrdC,]Phl iTL| -yɽQppHAѹ(qh%~u/]."$Q_67V{zU Mz|x-4b[nPf07OaWUGVlbfC'Qf9-3Sȧ _es3T, aۉ9:Fʣ{8"5hh hNQ3>nɊsq, C3Y]en; 3;kf=¿Hcr >O5 [ NZ5p;Ө=1)G6lH] Om~"'vO`>.Ö^nX‚7p֟M3n)01UҶǷppNug56¹͝)ͬ1+o4,(s D~uAjыt" t{%~^q3xcu@dnlѪTA ֽl+M ѹD%.x[2|òHeL;GCOգs=ЯR؋S$f""P_;>/Ps9]`Q|ص}{VEf'2?O-8Owf_eN5x*K}!\Weo1W6#y{JBT\KUD[Yb7w$cq?@h0-B6C+X_b!C5% ~ m, 1M*oRV\0\;x޳7>K0j$<1B!TXBDP+og##5XسQ}!-  >S /[-SFhqKB}5pz.LqoU)7 S/[$1k endstream endobj 269 0 obj << /Type /Page /Parent 533 0 R /Resources 270 0 R /Contents 271 0 R /Thumb 505 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 270 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 271 0 obj << /Length 5257 /Filter /FlateDecode >> stream V"R_ z#MF8eD. ҂dI >w~rUIMJmŪnAڄH8#᤯n-7+hU$wLvgX3:ɲͥ(dqye|:2sr^^C"\S=B9μz1mJT;faNi0]Öz, UWXшg|t'댮X(toNZ0m"l j[XA ҽ,϶g Ef@q_HY`F0 |GV ަBsZ|_Ԧa=rfP9_C|L #^AJJdKzǠP;%ؔP=FLym(%qtv.*4 IP_蛼?s^`k\1qF&;eƃeaT8Z4ӊ[pBUW AdӉ# cV%뎕5ZCV:7Prsޱ/$&j=}°2,]CRU:E6uQJ)$1jhD/,bĬMGyo+(?u1'~e^X9VF9Q }!˜[yX@~t-5 ˅9᰹}GU V g9#G56@vVќ (dϱڤ<ʖp ̼XDVsv,asO-ea%{x_kh`fO`_2ʘ/ gtگsaE!=I?-l!3 J(Tقz*:b+#K<:~ q J\͸Op]&5YLEK?R {k% m#]: pgckiN I,і&VGaz֘?-1W4}hDr%>l2egK)ՏG e~8ys"5s+`6V|HS4#zJQ2T~L+\jcJB8K&0#y.!'o4]:Lqžt;Ly8utD(HV}\w?Q0H/F8I-|W"ĞR~c%ylV,UՂOm#1U@ 3(Xe(aLaJ.0G2-m3] ۚ4s[=^C0H q>ڱ>+㭴mMN-Fbsa.ͩ;;C_&DPt"8+0  ÉoVqNlw" b U!^jBja gv:+% !n;۱L2J5^탾5jxTKmE-#'2#Tkqoa hi+4nTTl9!ɔ,Db/d%&+>-Re猅OuOiByÇZ*Nx,/v_OqZ{V[IۧoQxZ:BlΈWĶ=!9h6L݌{yK-6֨tDwY&p!l?:/tF2V{sͧPvt9F7oMM1D*%\-k鮑>zޣ[bϗ1r*un};&'l)\ h8 "cBk|`:]t9 wo1ǒ>M/ hgi1H`"i#ri܌_J7r?(!%{$]"?V r)7^x9B5=ע ɊgĬ1?6=lWgT6]T[tZ nZ6yUí^m.gPoTغ %;i ַFw.OlR_xYMZUVeh5CsgT6VU/r Sd-:%t4\ NWlEKJ(7M&9\7C@9"h$%XNz 't]pІjo!fKǗ1_omF)Hby+3[#QŸty'ALߘ&"3Ւi*(eR8Fn?JC5,c5Qn((>~|bڱP$"'xC=RPnN(N,X[7If;pSׯ25" ڍ`\}=6J&*] ?Va#Z1 p2z%rp] d5AK4F'͚*g9ZIQKlH2nxS57ŵ'i5x4ݞ л$Qp+6ޡB5,%b.s ĺ(2QRlc}fS'|*2]o1-s;z+j070{FVa=dX#w/'MbQx@pffՉ*6 b;9n(9(}@X! lڐ'kOVX<%۾hZo¢H(UDc5o B@5RGj籨c-'ƍK_% ^ڠ̌h7%&ζԔͦ;*;X# QzV%8lBkhFr-C5olqCM31ݢ>dBYPH,m-5a%R/` ǂTh;'1 Wc DseŠbc&(}zq;f@/#K'=4Lx%RpVs\ìL), 5UTm{U]܋^Atj-ޅ_"2]! ?ʕ: ܗJ xK%8؄4Vdy֑ N?t 03J#4M/v=$;Ƒ6(o)9qΫ<,qkǃt*=Nz8yh6N@VNHx(KQh1{YNzu$Nd@,ҧe]_.,F/]ʼǐ(9#VCEn>IVcNW]hK̀ *ɋ[k)4B]/k@7| y6U$\{gRҭQ?0A:Ӝ:@@9ĶaՂگ7>,zŬ~`+M&"2$ps8U80EPTGft(btrɴiOڔ0D6rH]?8#PqU~ ~SBC ɠ~zwi4ɫA%&xrc70ы4^*Jd?nORR. Y>"ZNU0Kz Dii@.ˇ0U4%x# nKbo^(Ƞ@(S[O}3 nޜTFDcIQ:!}ʨeÞaU T6xEZ!#Bh1O1}Am.to"3iKh<f6'!1A .@:@H콟@fE> endobj 273 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 274 0 obj << /Length 5554 /Filter /FlateDecode >> stream #SQY^چhPEW ;, " m EW^!.Xx\H sY# RDl=]Q\!|UqIx]E?kivjUYQT"Uk Sy-j{}PO]D }HMdՁռp(r:~*]AEi)e5<㴘_@[_#B\38\~6+$,V{ٰ8I6 []ڸ9Q& /Uod)!F|#RAZ#p`չ %mZ%^yȘvUpb `U~ y LtKf)ɥCsͱB+X/l$3O1Xzn IX#4_ҒEʱ | E7%\ |e2+Ɩ LB'D*㏁0ִuCVޟKy%||.wJ7*Y#TK eg b63:QL?DW"̷Ϟ66{V̖Cw%0GdȔRd|rqc+{wō2q/*1D9o > *yV1bfGCg!MNܗf?D/°@LƓ4m]]9u̸\XC$vf@mtǙ7 7>@.p}8MynٴMpF}=sF>[J0 dgomu*ܧ[]ּ4ֵakh䑢L|nHN)lOwGvȓŚXL[TsTn pxW0@f#׿sMpKc*"=rx,) _J_>)WνmpptQl}?>bBos]2$>=W"]ثUxFx.&A!~ݙb/IqCh=L\SZ +uGn\E -as Pf^(2 PNSyYݐe}E\Gj}d]QckxT3zM9SePMHTCkXI"'4kתkOiv sD<1CA{\H6VoKooBЄJ[)c񰹹m r, 7t 3S(771?a/2v;jV㺞}(䍹u=oN`ݘwӚHjKuoZpPقv3]J TL@*z|^! ]EhFb5p HC谞$Ẕnj?jC2Hz&BB~diPa6ui?I(5~ޟq`n&z-:3`6dP) FS_ZoqT3aIcv:S&hVu06J+̭2Ua輢9.vX m8HA}3 E\h9Z\D N#:ʝxwNpiF X"/8X\<T+jL:O[H,hE({cV2e}0u8iը.Hu^~OFW-^4EՕLY<fR&CnFwJs#ĎJf}#9o 0 ΟnUJo/Ai+P7PѕG>fIkj c.ȥi- fRs$h rlT-c}~aP\%Q (jXƌ`NU19Kzp#n\}S{{>8v;m\xOVsdnoiԾCFowƥL/{wqF>@LH,}kneUR){*іi|@ j͹i~hs7ĸsƧwSD*<݋৳@ESe L;|j%-Ӱo:ZCoxLƎڧQ/2mHCf+`x4gu/>>^> E,O SЪxp}I'|^OVǣFѓ5|FȆ!>|cWo=,I'd}Б=Eߛ% ,!H!%,@!~f@j:E0΢ǟ5E+*.X@jmPArC W8 "fς?`(I!$ ۟7Kb=1kT(HwD # د36]2,'ꓔIDQ,}=gGݚG9p8UͼJ69XE/_ӭ=DZ)C8a[dp{ُ %S6$9jKKP-7%Q-st^D}OKU6j }!n+K:65<@]D25ߌ/ˑ@4ws &Sf^.Y+$Eguٰ0;N蟊 ԤS`:Uybb!]v. &M)})#RǡT.QA_h;dBR[23ܝ(`#,JJPb*;d)Q t'> Bt8|@MFͿnej'I=Ms)'6A'`<ߵ*'2(8ÿtQ *m @$ nqRiRY!܉MdVH|o˧HL!i([Г.A(2Ct79F(z V-u\|k!, D̀b@}LYZ$ط͝~< 1\K|r&=`ZtJR(fH@= ^@nw:6ܗOm @FrjRW0x\ѝt:mψ:{ʍZc"ZJ Y\@Sy O1ԘͶNHeGw}zUႷy b ioNo>>.98P$j=ȭ#tjW6%ZOPiiQ9z:k0YMyLKZ놛Aq-uqQ@8:xХhȁxFWP%iNE9ln=MS+.ado2Ri+.bܘk2|1o  'IwiA-a慨(iqdwUٌNdR5c2.4dx)> endobj 276 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 277 0 obj << /Length 5333 /Filter /FlateDecode >> stream vo9-Y|QckHf?nà H4+ M}K~\ \|Rϻ'O FQ$ۤξf1tң%fWbmķ# ﴍ]?TLRt? ךPse`^nfȐSjr̍mϲ5]bFS TF8J aA JMŠH@~v/Jt^Řa:亶 1FبөT|qDܬfnVlL?[ Y pWAjWtůD(|v+:mPc` ҃၅}djL7 g[)P'O:na礖Vc|:[P5cNwֿtS֯$H .53W<%t?IZDjq`cB yi [I 'Pu>:+dcEM٣wú/5,K=.])̄O?6'9m~Q%~KpUuW"P\>9Iw-bkZ}/x,M 8GKeb+5?LJ'eI=\6XZc>kp9eU'`O^*խ"u#% ew)Jz ?-?pv ˶H?KxT=n=L3iBڗ--S7 ,nj<:}3&{2`!.iBqշ<)ikJ?ig.e 4kՠ]=) xƬ3wbf;ly< ÐC#9A9)FX4{_,7AGf_X$D\buٜx BÈc1a8bv7qe`pv/9B/JyyP9 9#{椹v*rk~$] 'hSdC_j봨b17hhz`S$R; !~RyGk6 ۻƽE$Wo?/*0cs~,!ܭ s<ȔP}')a&s:UC RG>X5gQ+|h2ў@_Lgǡ7,]( "r{$MWe/%)Prӊ#1Q,̬y /ŬgB&PKٸ\UڇQRmOm쓂kPeը#Fd`mKw@2٣h1HWAGL[Q@(6߮sDS&j~m\ŲtĎ^g!⑪ѺږnIdc<| ީ'BfPR {W_Kl4*\t}@.XݷTk@98K/&LvtTWOK3",SY7%95d\p:m>N 1 _ʏoGE] +PWJ~VD^zvt in OA.A*ɏ@Δnō,tbMڕBt}6>o ՜c3<҄)MDr;QaA}^Veӕ-u(o7nCw5StQd_fNUZ'8 f l_#:I93z39%X\ {O5̈́5I2`p JH^ҡ$6 L1,t[z/n$K"ѠHpV4]DSI^xNk:&{\(o\ _WuuI Aws]nBEjE1GSèE.Ekp4p29=&sתI" 29ۭ~;DGlGҝ7!pCl;3hOaZY/zk夸 !5tR@5ݬkÔyc1һMVz,GRoc#=VH<.ohLSj{!w[/\CcՍ豤֝#xQH $jhWws9JG;BLw^cM&(ML7s*A]RX v |p8U&eT@Z[i@gAStCSlbN H4d'pcĞv&mŲLנ>J[:rWEuaGbOf"$>I8F? ߧS-'| "`׳2 jdRy\e "Z*XQʦV N% .:vVR^0T=B9qId d,03?|\(6½zObIsKS]7j^YZ(gOl{^ a#ҋs(_Ut=R?Q5*/7 y~^KBjc߰sзYqF5 h(ʖtl%`&UlCqnaOIR,%D31JVA)No:jvo ,S X͹jHN78{捦9Jspjx9ld֦VI3r"T/g1ߓ T <&XB,H7? TR\WiҽvNw]O_E@L#'IHZhSK IWbdMZrelEK :CaV"{}u4F)F/({TNnXvlZjЄL65mJg >I Wg F il+KptEI˻ޒm&WV$ꍃC+` GR`x+jM-^g_䙣reN[mC =X6uʏ6:먖˸hx-; ws?G%.3-.kF rQ?j:1ts?dR>Z-%%C44x ,Cm/{R=ʻP+"(h\G'̈́'e7Jc4gfw#Pnk 9=e{t-Ye>t%Tmǽ*ƁGJ؊fPo³fu_\!*M=}Yjjj#נ`ѽ[h: lIm 뿻@5)ny2gQ#%{-'zF- ƽ%i>h hN= ~{> endobj 279 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 280 0 obj << /Length 5995 /Filter /FlateDecode >> stream Zvm_"( cж<&,AOVWHwԌ8~zwc˼-$*N Nc<ս~:i9ObJd )T^RqC7 <Un ]c8JE17?C\C8x7G"(U7D3ichlT +>`YR'=Fh/5D2.C+.cw[4rX5y:p-÷_)[x@ Ihľ5幫U%A(xUɋN='8W<ӛ\pflDJ3otTa\2'j n`iz/jLLt ɕ</!=*ihɅ xH@>äLm°>̮aϹ^ ?:=! G1oYfR{F*Xs˩_q'1f+=ql~籓^Iiآr+=hN W-#̆P9hd"`ރh-aE\H^f@]L Ezjd5ql :d u{%= xp<Ӿ?4Bl~uLйr:ބ;_HR"k,? r+6'!PGa8 j9(K݊֕\R]NWYSM9(/g*9]1Xܤ=ާ$9_^dQ>zsy ¶_p8U4n6Lf?X"W?RT b/,a2B'3+8-M^nP;!x5"7Ye佸EkLEqKe8#e" EH^COCr pj5w_BF?5]'W9erpKiv֑ߡ?SXd_\9$A%o>ACO۬.Lfކ &! P%ؖ6 8pPe R-bxbVw'""^gٝ`ɂw0Y&$_DxaZ\WŽ ݂ }v#IiA/`4?14Q9[݁74&W>.T_4/U|ꝦxIDS.\>Q\먌q85[.x&›=i {0[Шtͪ9#*[坡x Kz~^i5 fH9e 1BGȨ֐ ^ٱLvnV_vRRoG-/,+㎴SM:G-ڄjO%YX}[zBIG ĥqC̼ 2$_PNyC;u9BYr_* S 7/AנF6=D{Z@^,MBjcPǢR&zV{ yyD5Ҹh'[gXߦ|8;R1 B ЊOF؆n%c؆ nv!kF o߅reNB>Obmnj CT0gKg@ uJh!wl3*ݕZtl])n\M=Pjsl)?#vMO98(0l2[["l^f*RhJ,,R X+w3. }eГ^ڬY[t8FNa,͇Xik3=Uo`HhLC^[AE^֓uC#?~W^xX*b|Ipz Tx !='"H{*e]3ٱq>+mGĐߝ6(c(/.ʥx]2$$0%=U [D{ևv35/٩yk}(oW5<Մ3UH)H2c4:g&$ΝiN`Q)^Ye(OH{d$mͩ"Zx4Z={#[&"1Q=rYx[=gQ`@[#NUڀSAKj;'kLX_4t>$B $V SWo@ؚS#(ԆBƙ+;8'Ա?QFlyQR;l4JמpWx5?WF_KZsĶpsZUR.V26>䲨򄒯G &׺P#HIw q.g4}.qBFh/7{ٸM pG'i9 Ud݋lvK 5?2\h:s0'T-6Kr#-jz\Yu>pju&g*CS >֞u{;bŷ^hx w( 8czt@#TA|4³7:&c\?AN 4^68LX@ׇ R B&)g[U\NrV蠠Z5(E Í$B,g} Is".wK? F0_ls O?YM5yyx_fP4Ӳsܗ3Ϲ읲%a!zp6E·djkj^ݧwvmOx;-<@ mZ3+,v`D, 3?/jYuLaNiuQ8,aÿ#;֠ }Ób0{D_=B !ߡ]vإ2 @Rv?bf*{{aDQNň~A8-Z&F'u""4T/`&.V.%^AjAǩ )1qy5Pw{:)@w^_{URL!?" 9ޘQmkpiRXzR#8.OQ#_ zR$\J8atcG0 !UNi "p~Y+*P&r ԡy.=[H%>'o<%N(%M}%B3Z2\Ƴ\^,[WCR\QvRE_%q̼;v~v+`UBCRηpm PĎNoUnH[-+e 0NMp]^-:R _f޷ 8^FΦJno|I$hxMFC'+c|HBD%u.'x+bY%++.)#+gQ+Jc*.\d&\gݶ+LbέB9U+PpmǑ"o೑(8P0 |LBr0>YVq%\fsfzR4rn#d)ieƐ];(UBqrH7L~4tJHuB x֒;ՓY @%K?lzkca @ZՠWۊ}PABED2TW{8 DMeAq[qK557R5+Qg$\R 9ZXKXV*,lMF8D 1)l e:y\!p?bSxCź\ˤ[="-z`bF]Pt:B)[rVb6,%(2  ze"?9%^!޺tLW45~1UoJy}aR3 8UGM{b,$hXBQZvt ptErjJ}< 'Qʘ"4sQM7iJgO ιͶrKtOT(w5( /LStH~m7ub1gY,hLc ޺6=|aM,? Oo¨7]Hiq ۱kwr=wQxՏ6 ۓ-{)KDž55^?jO4gbY$"sxv\|"덕Kt&n ~8nyŔ?Kͫf qۢK)>6 PhYFz0Qk^ pG?OfA[gu?^u޲h1~o%r%;ԍJԃ,tƋ*45&kkjb*q o͟jjSH&+_U"!j;:}-!#7~. 5߽{+bb^}) Wgps&Uo'UkI%1$sUOm;^4DW= xus[uz@SQ "5cg9]Kbj%TŶPm#FN:̩l\o# ac!4- @-Ҭu1h6ILW#6μLMi1h˖6z|}^M?3NwH7~9}1ՌS "F5 XոHl9ɺ?RČ._( endstream endobj 281 0 obj << /Type /Page /Parent 533 0 R /Resources 282 0 R /Contents 283 0 R /Thumb 513 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 282 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 283 0 obj << /Length 2507 /Filter /FlateDecode >> stream .0]U3q,V/M8v`+%k= Kxvzh{ Śͬy "Jm7I8ۿZyw9bplVabJc_X N3f9|U2XIp^nYS)}UnwfGI)^ ԰(J^J^Jm }=r#쀲wMMFGz+,-#b@pof"9# S(M>]x%v t3]zm)`߫sRbTF,Qei};4ƲBJ_:"uo1QԥO -y0-4|y`s,$LX)՗"+6[x1Lt50°מ$])un Mt!_kk:ot{S,TsuǓiZ"F3~hʑ_,'"ښ'(XqȽ\%l *R6 ^>BLF#=rB>ՕI]IL#6yAE 6]c g<]8$.X8 ?VYTYj(LL*-1j}}LK׾啉'Nt7P+c`EMKl$=1U7.MgR[iɇ^Xm {|гة,As{o͌@OWw*%ܩukgE' F-,Ijpa3Y'Ot19N`w(WF2 G ?_HC )m6+_-pdiԳ0Ku~om,'ԋVu sUGl]Thc=ਉ@BlkW[Zu}@%+݁pBzJS4>t?}~mh|4rSF'^OCdˉ|mD$V(jiF E Gmt<+Ʊp%ahQM`hx<8'uP8S/fŧ!tJ$гzo/U?RNI[YEd#aQ=N q endstream endobj 284 0 obj << /Type /Page /Parent 533 0 R /Resources 285 0 R /Contents 286 0 R /Thumb 515 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 285 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 286 0 obj << /Length 6697 /Filter /FlateDecode >> stream }WUWnB=?G4FL o?VfpM vvgLPPbIs*p_4$Q8(操-WZnUx;_xhP>jj ql,U$|HD0֞XpuNuya q.oEpVQ^uteARSVՂK=e EuɊ=5戣-sYOfa$QIAOb-vXO}i0$U'JZYq"4kEf%廪1wS >%<-|i _m[+|y]DضUYya%ҏfۀ72JkX{\vK\” r;l"'8 ,jdU>+>tf=rJ>,=C$ՙ".,yHD[SE,18X71XYJj7L=;zO&'XnoqQ"3N4@nEoM ]*qg8pG^"9FqdX당,9㝔C#f:cd2|[&B=㦐 CSHwXLDƈ?w6Twz@6^*r/Ը%9šk3?t;r&9J5wyW A盠g&Wwf)g1!l57 CgD^{lt:tu.Mﴖ9}0m ~UJ[lybVI8k$cuxBc!l?}攚},_yXq6e#lUܱWGF -==̝2Wsg)RiiN8K/ℎkF1X˰ ks"$,:o_%dbVMӚ42W/1G%".-8ؿ_z "Bi[Newc2_yF%mtٵ012J{3jn[*NF?j ==>\~)UWZYYCPAwaLKOg0<5'hnXZPƥV$+jZc ߲jQ>Oo3ᣘ[׳%i-"9?$)8ZX}5d i78?A8]HY҄1~7ۧ@;.9QɉE8kAvYk}z~hɑ ̠K7Hj՟ͣ8;DȐ-_vst)OO.݆6I8 ;Vw@@@pkh-y-{8ǞN~XyG+%D`5-#d.̪"LC>*&xM*$RY84בY,'HLh]PTT(Gp9[ {B˭lRZ]}[ {Ө 8vocWw} o<4UqT$_(UԞn mȺ^ վGT ?,-ۧ@CBhW8ͧ|u֝+N6]͐lìz4NfU^muiUPL[|z'WL)Mw f C 덨Y=g;h Q3靖ڠA67Sqa:QdRk) =@m>T5ϛRf:pam+Q??/ZJd9 GwySv2"㖣TTK ]=^° YzSQఖtԒT9k$yYώ)ʳhRL]+ ڔMP03/ha BPkW[L:=KH\QIN 3f5tRXvh~]Y6EGtItE6jc~N)e}64}O**S(n5{^Zc^?r9*hTHd$%Tה &m\88T\(FpH>s ?&<\> و2|20n?WR:d aLlH[4LeȭF(M룢mV\1È!@\Ra\[pΫ:gJjgބ#Y+o#i)M;Gj4V<\ ];]h^yFf C%#aEjF@=^|z VGOX>²~&D|_(<6.j(wGvBSD(4io%b#w0dډ:ʌ1H.' Ru[3 #w1]Ts?CʆIt%{8v}e5X/GShJ-V: mlћw!9討`Pͩ:+cT*صu-%j̗j0 uerHHܙ0E }=@\znM,H/@m-u$a߮CX{5Dh CmEK jr#ӓFhJ?&#53:)3Xx J3!GVFstznA&c(&^n ]> =|3L3'^-j-QO 1\C&?[~؂y(0<_4jl5Dn ;sUOP^,X@zGC5+HgV~[3]sGm#i zMm^H1z|(I1`d-> DiT"ܳ-ǕcPbu=+m#> -΅;Oѹ=M< >Q`wkHo7Z͛)tzw(!_05UCQaCɼ6mŲ lXw2TȨTDzS'D qq%Yy}/l蕴̓odyc2!Vú!n(sVgk wK=a'䡳dN>ƶ?-+4HENupyo [SҲ{oީ% TSSp0('F;Ǹnx:#&8cl(wt\w1%'+?V[z+\'7-sh:^} 3{[SX5a8[G#Pxy$4xۤ ֏WG Awqz`XYϘlæyGw5;6ΗmԪяb׌/)4yi#N5X*hK8^~!i*G-Bt2eLO ﮦ >mH31rw`z:6ɎXqҏ4emH4xTtТ1ơf_€ JBYp.*gdAWֵ ?< +AMb4'ۈw]kv?JKY%;dλ*&%?1e@R=sa[4وg8Y=>~-[lypzs{ZLH gSSV|a,0@Ė+0.F endstream endobj 287 0 obj << /Type /Page /Parent 533 0 R /Resources 288 0 R /Contents 289 0 R /Thumb 517 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 288 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 289 0 obj << /Length 4881 /Filter /FlateDecode >> stream @H颣8 xE~H=Y'?|ΫK-*逫+0^B>(o1yqRlۯh#Io9=^Z9[}J#+RUڂ~/W WS ^-м4j7hhF'lf iN!8 ED9i-CWĹZ9v-c)ˇ]JCF:O#@N~D#< 0c݃9?}Gm(1SEuV<@I@~Pݝ 'l^Pe#]iZZxkwݰreHeV__+E+ X`PƋ=4NO/ה}%UK} lt!:`Zܴb\f734Qf1A^?-m;/u diXSs!jKm%j%Q Ɛ]];QqgQmo5m=5;ՓAˀċ Ї `PHZsY_)A4pVU @վ=#ڕ:>[qȁ1¦ Ȕ!iQ mgȵ?}}(ߑ A  站rўEӒB 2!or1!m7_hU$`\څFPU+h|D$#QONU 㙩K \kk!/pA;O\UB}|WAN[)Sn&gJ*9#q‹B߷)#s7.,nQ,~/d6'@~ >^MmvɔޚS6=GL{k0DĮn^ ImА!pξ`驴څ (VZbC7fk,>"&Uڄ%xiUe?BDb!*A5it(e㶡ƼOAj> Tw.{$[!9`*};L~I~p,BmX45;K&_[vkc  2hҡ[+Uť yҖ%yzC9-%D)E# (Z_ô`v>=h XÔ$6xUL&A [m$`ݝ\Fz}<>o )j*<\vf9ߙoCr]9oB='M5uV{Kp3 mf9IR \J̌FHW.b l-Am vCLI@tU,"u_È9m`%&l;mj*I`eMegKپ,6߅*Y2dV,EBS8Sc Ad-~OZYإ &r9R!69K0pޫx&%$5Z3oF`D๸3;N,O)4D[k 936`uzN'F=Vxդ3ձ)9v+HWRK~^tԛ LN۲u]2Hs<"$LuF!˶/ؠ;-5]`+ĬI1Kq*[%a8V~";€QXICՠ~Y5@tjkZ8? . yV*p<\QzPcB =zo65UH"`"~%G X+t뗱QA3d[=͎Քf6 X R U0#E˯Kg!],1Y$9b5F9P9{HT d>ϗ⇛wMS?K)5*;yoG?WEL u,-d4U7h;+5{l;`DR/qҐŐJ'j|Dz"'{ +{Y}Hiu&9n!4>I5 WZUS:+yZLX1Zd# >F)E6J8tK )6%=P&lV̿&Du[ҼѻoLn8p'H@8kՉW!88%Zih8F*7:*[vS fCݣָ` :00өq%3+ҋ+Y]+(eRhxw*|lY/gӒ]8|w Qeا:ޥWX.GJbån>7mP47o ]I1~Gu(pQD}e9T }yWd&jl͓.k$}=?ZͰO+;_="DDlVRmKiD8gq)ZhQ ?i_%jB2m"\ ![lUx{Uj@sv5mͱ,Z%qw v5 J5K]^ ; ؜\QL>zt#X0h2On4k>zƞ fjF_я{1u&Om- ۛp`$;j"ʋ1gQ.Jp4pzʯNiA&KѳWl;,:R ce懧0 ޭ$YS4=D#8uE+$~IZćl&~', DH05QZcy܇HW又-h#\nkC_3rŏa#or-ʵUdU:L` J m.O+ֈ/7L`C@}nTnQx`! 9OGͷsE QT :X$l0#o{QUYhDW4I# x*%,E sJArc)c)f>1 {0Ћ?'"|\Ɠo9NHPE/г.o !'>uZku$TWAUU E>p_vP.M7|my7Dm7*{!|]I\ͭG(䏧l@TN slҹUTuIgKLXy9#҄*; UkI|i$N߸nNGhsȕVrcQ1JSUM'RuN TTÕDؠ? n|:4xdkoO:'.NڢWqcD^bMBy̒W~VnnfTߦlπ|A=l2󄐴V,_=ҵ턐^trǿeEAn&yWwjײ^E(Oq/|eVSicbT٦6Bta_  w? NoF%ˊqWT\r=S-+ N+l>!RhqFoђy@Q[zҬgW4zިּp\>])^M:vll2<_Yc]b[2ⲵtIa$֯.[HX2&7UX5tŨlA;Ҏged\^nͩXz[ -.KUER޾Otء_u'9 ՠXeޱ$ߪSʂ@bvKڝrTcNl569fxDҳׂӚgºP <㏦=%JؑP;(Q3ϠzJLl 9X׷,?af HhOե#2omX}7jhN!j R: o&3U K⒡;[ɘLR,Mt4G99Fby̲n('9_lk+ks2}d K"rVmW/RV9"4ݰI< ;9sp+̅ m$֥';X쁧VQ.`B$NiW$! endstream endobj 290 0 obj << /Type /Page /Parent 534 0 R /Resources 291 0 R /Contents 292 0 R /Thumb 519 0 R /MediaBox [ 0 0 595 842 ] /CropBox [ 0 0 595 842 ] /Rotate 0 >> endobj 291 0 obj << /ProcSet [ /PDF /Text ] /Font << /F2 293 0 R /TT2 545 0 R /TT4 541 0 R /TT6 542 0 R /TT8 549 0 R /TT10 294 0 R >> /ExtGState << /GS1 567 0 R >> /ColorSpace << /Cs6 547 0 R >> >> endobj 292 0 obj << /Length 4596 /Filter /FlateDecode >> stream n%bRaֈ{άXm!G #/m(^E3]š58Hz[#W81;-\w<(6]IrUt8vRF7-VƑ1te3tFl7`J.Vn | 1ʾ 4rLp@a :=mp?GQꧯ&^T _[`}B:h" 2M@$ TOkO/\prL7ZqiH˴<֧bHpuiӼ\ 3pANE9a o߶[*lu}0^s5lʮCӏ/z W]#1$^:FG [pCO]mM9;  Otb^d|m{Sڥ+_"i 򘗡bܘ vˡiD %ɯ=\ulMr abT:@%^k4`5ӷyerׯWow/፭$oVM.pƼE{ANO;#rKJ7Vh]BTzޫܳeX'5k2.x r2RRݶ3.R^ `22 W&KuoA6iJEgb-K? FУ\ҵXQu7vq}/~.^f%|尟XO3L0dm{;hi4=%[%NR.OC>s^apS(7Sr%i]=vۅ mg3 ݺK6*DSsL?~`jbSv?bZ8{8HPiXvޏ SC,dD媒FК_42|,[qux6T:XMM-)Z2$CO[D4j@;f+!-~ @بY^+kD8hQFǖi)d# +J ]-sc f8{ Y9n@hII 2$rIŘEpS-!P?xlIZpjofj?r[D<:8X=+-{ ޛlKl.PU.&V?:ṶTF}{3cVGΑ8 6?%OYm䠚8l3AJAnv"R2A7D4˵GGZvk 3|M-{ݐ:W[ajUc&ًMe|jL~>N,?KJ/Y.7(MPz[[rICQVRҨ :56ҷ$%@\Mt ]@yVNwJ m2FBmbrUV13GN[w*[設K!'Q>  n)±offdPRSTiv3/anӔS6nMWZtŒs(>Ⱥ[=Tcĭ|ѐ~hw3OMƱMɃ^ReHLYs*gڇQG=1\ 5dZOXΉNg{1y `5tBE90CCFTXuX%5 '0Aj#53~AkyaPaSi?VE@E(^Y(<ɬ뤮jXF[?!ؾqzEx;j_Q k]S̰s~{ %m;0 vNIm\K螥6C5zTh#i2ǧA| $$P~0+6 `+ȮNkx7y9%bמ) Yq4"\{BE f*1.`\3D.X$IweEHM{NJ}NJuZSF0ANSeLy{Ĺ8OdωȞFO,vz3jPR ?XWZKsl~-̋a(م_xDEo|Jsg9?A^,x_V=Aa:.r,IG%4Su塨Οs%"aXE |_pҎug1Hu& ͺŪu"|Cqh¡/n3pT:wؘ#fpmhƒeCwYRyWa]}g "hԹf9tdEp8E Onu hM괣zvk> b  .;OG J! XH3M֢M0xW `=qƍzg,J+5XњO B]iI^NoƌlB-FJ*L睳 YӰMa•TuEVN:dY0fgIg)FIĄR达GKҘ;¶,^"H rոn ^w;B><ߙȑ~yKc[ VVbq1]&hlDhGhIjwMĒ8t cq_?zF*>>J[MЭm%[;`\h֞#Ƭ$Eu:dvx[P˛Ҧa"AKnk(gUmv)t8a ~,~ۂq9Cr0'V@ m[L"~Vψ5 ׂbZZKr>Cu'=@T[0Sx w3`׾A_L*ZC;[?O]a䗩N1D};m =.3V;+P 53 d1A.^^6?iJYvy Rh1Bԅ=̠_0. Cdrmm Kk4U gpS!^T[U;GK+n4̔ijk I aocm': 췊=)0οӢ ᰙgAQ>v\q^oZͨkC( %Uq~KDŽl{S9([]J0H<زO~|P7bjdW`*,<^Tpŕ#WZg, .AB0Du'w|o4?m AҶ aJWzø7XKH581I" `${ëвv;_6Sa.IGXOUlg˦H }ZN.whkd#g|dPGF1g2g>okCL bKQPo:C;An Lܤ e endstream endobj 293 0 obj << /Type /Font /Subtype /Type1 /FirstChar 1 /LastChar 1 /Widths [ 787 ] /Encoding 311 0 R /BaseFont /TT486oI00 /FontDescriptor 299 0 R >> endobj 294 0 obj << /Type /Font /Subtype /TrueType /FirstChar 98 /LastChar 116 /Widths [ 1142 0 0 0 0 0 0 0 0 0 893 1129 0 0 0 0 0 0 1273 ] /Encoding /WinAnsiEncoding /BaseFont /PNIMCP+PhilipsCorpIdent /FontDescriptor 305 0 R >> endobj 295 0 obj << /Type /Font /Subtype /Type1 /FirstChar 1 /LastChar 2 /Widths [ 795 458 ] /Encoding 312 0 R /BaseFont /PNIOBL+TT48Eo00 /FontDescriptor 301 0 R >> endobj 296 0 obj << /Type /Font /Subtype /Type1 /FirstChar 1 /LastChar 1 /Widths [ 458 ] /Encoding 313 0 R /BaseFont /TT490o00 /FontDescriptor 303 0 R >> endobj 297 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 121 /Widths [ 250 0 0 0 0 0 0 180 333 333 0 0 250 0 0 278 500 500 500 500 500 500 500 500 500 500 0 0 0 0 564 0 0 722 667 667 722 611 0 722 722 333 0 0 611 889 722 722 556 722 667 556 611 722 722 944 0 0 0 0 0 0 0 0 0 444 500 444 500 444 333 500 500 278 0 500 278 778 500 500 500 500 333 389 278 500 500 0 500 500 ] /Encoding /WinAnsiEncoding /BaseFont /PNIPOM+TimesNewRoman /FontDescriptor 307 0 R >> endobj 298 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 121 /Widths [ 278 0 0 0 0 0 0 0 333 333 0 0 0 333 278 0 0 556 556 556 556 556 556 556 0 0 0 0 0 0 0 0 0 722 0 722 722 667 611 0 0 278 0 722 0 833 722 778 667 0 722 667 0 722 667 944 0 0 0 0 0 0 0 0 0 556 611 556 611 556 333 611 611 278 0 556 278 889 611 611 611 611 389 556 333 611 556 0 556 556 ] /Encoding /WinAnsiEncoding /BaseFont /PNJCPB+Arial,BoldItalic /FontDescriptor 309 0 R >> endobj 299 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 4 /FontBBox [ 0 -16 887 677 ] /FontName /TT486oI00 /ItalicAngle 0 /StemV 0 /FontFile3 300 0 R >> endobj 300 0 obj << /Length 413 /Subtype /Type1C >> stream NŽX{C1P˼ŐtB:Fl$k|]^:>eӫNEA:n)GOW+m'a(GLJQU#ȓDeu.3L'40:w7;Yӄh[ONjü\p 5^WFAxG}|"dij~0fFj Qhk,D4"1P@*$0=kh~X?Zק endstream endobj 301 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 4 /FontBBox [ 0 0 711 723 ] /FontName /PNIOBL+TT48Eo00 /ItalicAngle 0 /StemV 0 /CharSet ("u*) /FontFile3 302 0 R >> endobj 302 0 obj << /Filter /FlateDecode /Length 206 /Subtype /Type1C >> stream 8%C/"h 4؃(V3 YFzf]f֗$)/pmHT6SWǪpلaqrwL}%ѯA @bY-O"v [Wyԣi`>0lwG@)hm #? {샺-, 7i &",mRk endstream endobj 303 0 obj << /Type /FontDescriptor /Ascent 0 /CapHeight 0 /Descent 0 /Flags 4 /FontBBox [ 0 0 410 470 ] /FontName /TT490o00 /ItalicAngle 0 /StemV 0 /FontFile3 304 0 R >> endobj 304 0 obj << /Filter /FlateDecode /Length 148 /Subtype /Type1C >> stream TX骁-PYuw > endobj 306 0 obj << /Filter /FlateDecode /Length 3356 /Length1 6952 >> stream l _Tv{%Dp*̡>~MEwQyϣz: 2Q p e @@ϲ)v/1BX툛@f976"V,'=v\dzi!\s2#Ҁ]mHV} Ɣz0eٺBn*u ;x{Qxj/OԈ2j UtT_gJEy% [Q._ID{j.s4O@Ld̵BղbP \cxr;\HR? `(ךPE 5`" ԉF(ס*"rIyvX_~438˾|k=%62RF' tx lǨGh^.E#Qቊ 4YTv}ip3=:U"z1{=̛1&ӧ# F+Ȋ B84έHl_K*rA=>D"ý:2'l_2TUXI)id/au:P)>{/+bC* OPkoo5ӛ<,xPTbDfz{BSrKtSWTURx1AK8ڣ(Ȟ!j,V'e6J-\ktOici=;>ؗ-F)aώ_ÄWiqxt R9óyvNH> ;>8A]?8vaLIX G ݖ TϺ\, 0cωuЏɛq*qVpdQ BIk$FRn=XLDq̜#Nwͳa DPf-ɺ.Z_e<3be\18ZWjn :C[Q*J37m;L K80y6ʷd ;Mʇ<i*ۯld͢kPn]neDF#Vޭf,Xws Ꝇq 5Q#r Shް,gԊ͟_aK"FP)[K^+lYa`"]%{KR%0/* 5%XPaOrHpa22*yGo©Y*?bAW9"dh_>lÿլ1`ZL oLX[~pG_jV im$4fo\@[|R4bbq DuXgH#`YinNVRהzYhsxH^ńPox>n5:Οq`oH2վ((^?KA>Dغy3pY4px"=' :YRH/뮭reDl$Rs^)GMd0iChoFE Kxn)JAާPh3O+ȨF>ǜ\8嶠Go[rOm+9* wߗC8dד@xkyJ, {;v \]nc6vi(9؀. G鏊זJl0-[IoCtwudx~?+byZ'qXI뽒v)vq#BMr m_9BZ9=8Ix|2˵,C]ofcu[$i,ux){rWDލ WӞ.6C]YL :D b=䅋n][e6}zˁѿ\7yKn By A=Tuۅ]nX3q%`g>QYth)JԮ3Z Z"}8bϳ!vt+> QmnDI]z87ʯd`o_@B=Q1yh(u-ܱ5"%l33X,H=- 1o=[ksu Q)u)3 (8䃌m8r5$ h 36{J+/+PIL'u"=j:rom詖#3@^^rY2Tt)!`F*5OpGWy[nϤ]QMqĬ# oIpCݓɬCn{ꖪSfPim2S M3 >3$r @@cqTV=>ģ=oԩN3_ mev/WD1NK{Ônw#q3jtW'K3T)VW{66%E]˚BJaV:UWo]IZö~rN.+nlG`CM/c 'J4A޿ PN#!H%IFO}!qrmgVԅo~Zx I6ǰD endstream endobj 307 0 obj << /Type /FontDescriptor /Ascent 891 /CapHeight 0 /Descent -216 /Flags 34 /FontBBox [ -568 -307 2028 1007 ] /FontName /PNIPOM+TimesNewRoman /ItalicAngle 0 /StemV 0 /FontFile2 308 0 R >> endobj 308 0 obj << /Filter /FlateDecode /Length 28884 /Length1 45112 >> stream ܙ{|\[ݢ6=D'~tt,{`'^.Fz.?@Ll9ZX 'ٜdݓ ߬FEX#M@ꀘHv7cermEu]z8E_j\Zf|e%lZW}s;;7օ:obF̢6 їK<ɰv7ec-Líl9$dwӂ-Z0ކ_9^, q]x,]wdf!񟸃ɼO8:ډeݣm̽B2BXX*O^%@mV.6? @ ) `I#9Rr@r'3ֽIK|F90u5o[-R2')&B[YCK(L/hi%*҄}gvA]Υ=X ϰ t @v~O0F2ڭ8ÿ6)>[g f\[+ ´Ջ8dfM #N.j^#[~eUU(~I!2<pADwuY&VLDMӪC)0nxtؤH_qHBNLX<o1NNgpl2' 3墻T`sd Xm-*g%aM?';)DYL7C,e *C.(wgIŬ$*"jD9}~(/ ڹW}Xrݍ*rdJ̩sr ~߽Ξng妙EO06QA蔫U>;SHry V.<}=>:7@g|` JAWJ/ TAL5APIOatgH-ȅ;ǔgIᘢ$Ѕ~^>:MsZ0^1r0ӡ[@n)euN;랲z`,ٛ t.So9oGK̰4icB 4jyhGjQVZ-XLn~-#7=<\};wܪFG?rKyׄj~ ytC}Eq5/[Y7=}cw1!.Veͬaqh>p?,ബ y~zXW'8nK்PƼf`5^o;7# 3|eN9{"L)ⶡPtUSMWNgF.^`ka%w3S!M}SB K(#=ɩ\ҚB$cqW!lxX_S'i(nQbs%*N.=FI%G58Y50qI/ f^|KD.bW=q^~2Mr NTQFϩ!v׀ G& ?,Yӡq0ASw{>/MVg}ªdH| ȇ "^zH*+ÛMf* U"}'GH^ZTMFH <\YjtxuH2m=!'.,ʷvPf <; !PFoWY9>[plJOn^mQRo摌jFE=X ,Da<)E4v؃# u(vͬ@;L>ylmo)`EP#X-a+Tb1f7lX 7H.W,)uҺluzfP2]!TߕϿm F,1(Wpr°"^G(;}-O-LtdpdIrMDKiTr֚ó8TnKDuy ~z"v>Aձ՜?a^㌾*kM.oQ|ʧ͒@I=Åuִ^ x\(2=HV,Y>: *SUH 'rrW0ՈX@-gy)8~unee$M'=P{=μ`CrpL] }m..t ΁eͶ`x6:haʱ-/W={+&YkB.{1ه-&OAoWJ%^b8RTX{L@C'0VݕJ~.ZRf3A!D1XBe-H3+2Z;N.z•N^;הּBKRkdCg29 -1nqN(){@h˭BS/D= FGI#5#+_ W.8~jm&34FNQXك Kj>0 ף[`L|CKz&YjHM0\ ^,p?+r}k+%VU$ǚq4馔R$WLEW΋`` %6C>[&;#91(6n"\^=v-n(aAXNK#-&6YȖyXZ2JG9"[⠁P98F1btzJ>,b9!1 d3J6hAߡ .^iz8XYKӀ8IJN_ˣ_8~2nPWFWz.sa6kj,إ7*ɬGQ dn[r@؈"(i3sH9Pd{YAйkp_ YT-2w9MHj]&gG>u㢀u sJ jX영1υ) صi6. 9Y\uBh:i[8(Шkt!i]&|g',,@gd:n0F٠;[{L@)Q@^Nk~lB1<.6Uw2,_Ra<Ϋ,0`_8C_bwq6a]i.mgH`ڸX-ﵙbBTdecn!kw"]@u;\!lK.*Ғ%g4t" GH"eD6 `S1v5s^#FH#W᳣~ofh~d@j R(HϠ@c{LXT{B60ZcdSK@Mtʼn7շve4?ͳHNn'"`3$c`օ2QC^HYs0IQ2,S1Pc#9Jr^&4n تz ֥IZv$la<,f!kR(ii.)E,Dg9|5l)}&7sA Y7Bҹ ~d>U3 TPv"0Z18X ƚ٘L1 DQPF+]5f]JOFx vm݁ihM/4Ϥ8UhqkZ8=u(x;#J+y'VYf0k6Ö\Yk/i7#CRhJHcs<)!BEܿ:| 0Ȫ٧^^ ?ky%1_} L`dܮ}6 x. 9{q?Oچib_.7.yM|>9F'S~C%G`p "tu8{6,F1]lrO];-di}s]mWhY3&-rPsvQhwGʀo3[Lmiƀ.AdAtUϋK*QX5*zI/, h7K$ / Tg 2~!Dr8Dާ5}J>$q͙ط֣%5"mf}B4$35`/>5̲ :ˊ.(.w/㿸}B6n4WV֢'&*im##ɤU馴{U($^6LBU3BX&/:mJmǵ ߁!gHX%o@ ztGofMp1ZʼGcTW"3>ZqEG`c{[RCƖr[I 'Β.>[{ G0ki@B#Eܓ\]6wԁ5o;|pH *X>,M/ԩ(e>&Iì=kecpC@*ă:[ZdL!=F[_Qa2Z!Xt&())*ƭXU8Q=iE6ōrMYӶiѩ4`a)@92r CB,:^po<~Y SȠNaGThSc~H+=^4JD^ݼ,'q-=[oUJWTQPWjui;z_+h/V=VCNekx53O̘7Y"R=|60L*a|7Z_S}"͕0z > =cRnn+P}ti[mE_^ &o_ c.ϙ-ͳ9nn7]G/7m|mы\T﬒(Tm!J25iH?6i @'𿘁 jr08T4;F`)U6HȴN0mkw>1cp}JͶw5XSC 4"A /]9,a3ak Jt'k9=66jB5x6S{H5q ȗBx秅}Pe_seHc׉JyؿU#( 2.ZEl7\EfzPt[*6}vB[jE+wF'T@b{[|JT x:8#cȹWKĭ%✹MB k1{7q6/4VP_R!sKkJM1pEVԳK`9J @AW!i JA%4Ieҁ\H$&OOԒN_MEFd{C[lV7\d|Q=e x?s[},_C,Qu̺fcuHm< Q:!~1/Ī35+YH`yyr9pXV:^bdS·؆?G N˅ø4NBe>.lre&96}^۬%v&}6֏uA9̆yƛ5^ΏSY)ý*\0௱?|nlK)~ 9{5pi,b6a"q 9jrm$s_k'A:;*KDe_jn&{BK$K I\C0ڭ(jn_j Awrx{od@}ZPPymFn|dA W`r|l9mC*+$j۷="cԿۈv d:ܝ/y.И{3Tv3m_hh̠0&{ bp[H=+qgyQ, kc-1α5\@n%+;}/=kr=%)xt@ L\PsI'u;igcD5#6@0oz*1*ch_P#fgq58Q@zMrp JKJ^P I-r^0Gt;Nlj#H~A%+4IZ"+Rfл~g$v|[ ދ57cmQ-x<̗kC$mv C/|LީkHW,vLm tEȱ9&RC1G#Ljl=V2ړM FD)؋ Ӿm4򀶸t3b-#uOCc={RI /Itw](?ǂRGY;2YMDoPVt4 q|Uw[ruןϡڟRtm|^V6vv/ Tc٘lя#:Kth,Y"NELיlrJ5R;v$B_ɿ઻k A+֩ a%Y&B/Q$'8l}:r,E2C_%ZI1g).y ĩ{sMc45%DULJḇU@%l̅o(/[j[^5uWQdh~!fq(GΤD[OnJa_YTؘ"|P-@qR&U/ =BEA (oMy ۖT h9_9 <,d8ب5X MqXNiKt!M oly s(շtJcjkvxAD͵tB@@fAS2%ϜZWZtԟPus4-ϕ hU:/l9R#[pD.U7#Bms=՟+Op)J1@̓wu_e\{:Fe 5t oyj^*J4q}ǦP[֤+&Ha8s,4vћfeЭt{iLL^DcxdކW+Kr1x"rZH(ױ0'嵾L< iO4AIdX[}7g>_2KԳwgL7jH׿PBУ+hRn|cH@qdK${iG_eR9b[4*5oѳ݄V’^|\a(!W!K~)LΞnL3~oȟ(\d pG|.XB*in1~[8\Edl2 <8w0Gc\ּyJ-4Zc)%A1D <€F+ՀCzAgl[a{5i>iQHq$TUR矃8yFnl݂Rןnls)yi"wL3_oQ7F ߧov̄ |+A\N" 8?lҧc.+\j_6bdL8}șPaZ dW5UM28'G㺀-ZOAP9U±WkpH-ݐ/(Ö@}?*d@=/C!`6)K~ ۂҩ )"'khR)uՄQagQʾ[l<"d=88fPzb+"zlV?̏$/p}J#+h%Gj-t(s§_0Tok0kzB927%җkp1R`&^v`NW)t7\="Z^C)I@ (;U0#( HI5J$W޲?ʔ^XtNB+1*󹣜/&Z,=noS Yat5sV{ZYV̡L_ 2Nur% [pFS8g4f'uU-=V`e*ZeM犓jP jMN<8̆? WU27 G2n}ΈE}g>3m*! 4ӗ-%r ;CEYg _RI>~.e6[Դxc &(5 3zf6gI 7xSzy݆{\ʒ DeD?z4U!eҵs("[oٜ:2K9*ٱR |VMV:\|P55t25jhi: M@-ut0ڼ"daO5vq.FqV[o $)rd TWmA;9o\+1iwB̰P ~ⶢ+$߫L_+3}c s7C!*@AOf+ԓŭD'f /@TMPV2G2϶T,JpHCٍj#Ǹ3c\&ݍI*o]C1#:L85v}2AmɃ1z,nN@b[o7i?$- G98,oz`غoS-JzJ~^RO-ʌv?Կ{AS o4RgўO.rP^Kp2&0#+2GAyNaUI+m/GW2]+I7p;"*FHN.kB?L{qi6xSX!ZJpـ5Yl, p;UjIYp;ͬrׂ{w}BtfXI`F^ Ȏ$MUDUx-YlCHQO$CI}lStP_(yUnz*Nu{r"<ֳ;7zx35$xW$͟ "R.^: M\ i2Yб.ؐ =aEPI)ǰN zY-37:yBଁ +ɪ(~ ?: X! Nn>kA\:/bb_2qnH,|[]<q7vԘzI"j_=C^];UVp:K@6fxO]#_RlAo"Pi"( aHԠΘ<-\e쥸9`x'b6 N%'ҜoyGs*l<ȱQyƒ(@,OQ3?em&8v.%x3=1-^<r@A3 Q;m;haiCDl!WK+^Fb+'g7@x\Iu5yVq\W}xR&I<~UX)ԁ~A# s@U^\BufXGqM%5֎y)d,<T`iNSl](KmRv8y"unэ^~5Ω,Yޱ5'35FRݞx1kwl$M"u.vűzn3׎4% P&q]lN^d>*J^k[Yͧ5 PzՈ19|"(j2o=&z@rT%M!r'./l9!RPiStnt%4h+M⑛FX=-VtX oJ=l) tg{];ފ<=&|AdqU0)rȸEpî̵gIBsFI}  GP&Cnm(M uY%waO=.O-9x&wA M45 Eͫu[ACJƵ:Pz=zT@hF }衩؋Z "L7(ƴtwm`|mOUtUT KUmnzSO8i<෯@)7wDRV4j- -kS'Xް';*lS odo`1:;z(]gԈ|p $Y#YQOz[kMh@x2JhM?F̪ްX dIK!,6RTns6|7q} ꎓcn@2 Uz/T1C+tS)hp2R/&  $AOj&IG0% 6@llIH*b1]yZϲ)ϷwiM l"TV^gr"w5 .vy[[72Rcx"`$;pk_7X̟{cdS$lǝ`$'b`e@4;kD,wѰM/Ӊdem_Wgj 0,Rς^o 2UX#&M"* a2M{fyKK)'M6$2ڍ% l)odJ/AU/ 6:I=շ+bn j 3y7YW5==>u$2AjH`Pgw6d7P@zaa2{ט*Їl _XMBF_-`fq܌lj?kgBm"?19gLsd!09b-ǟ1 dA*Șc nQIq; *'C a}NQ2P6QgMb(.Eݞ*<=J|Sy2q}XQ*xqu5@g!EgZY)3aG84Aԇ=^| [K GYR3^Qe/ _H3ȓ[?Lq…P$V]NJ37b"g2]0ߺ$8UPÀgo+uY01*NGg1+3 VF>d.b, eNQ$ێ.tEGBz[u{ 'Խ6fM/.M)-Oѳݼ/,%D^SM* -+A>qEڔ akxfqs_&?L-NNAB)kz9UZs AYU I X2(mPMg/`%WlN橿5>OʱT^Н1T.GOěS]|6| Mh|ú5Cd:r>r^mXxhkE9g )A3 N{]\}8[71 )ʴA |@I/O(ղGrY4BuZF&1_n~R`k"4Ѩ'tљG\?]oicW)(ׂrD/)I%PEyتYCQ-܆*.]c[skNdMg¬% r"J(t mX?Kѕ*"!ȿYq:0(vFL31aa\JhS˜<0 ҷ axOMS6 K 7M} Z +ˇQw+~O1qzOJ WmsfVM+6$['|D_.eӄ"YEwQ4uqC5o)3`߷ĵ0oDok=,/px9$@S+g2FVNk%$-by v0oF-4sfX4l5GLUJ{y-!B0iC=ec,q5mjᢱm.4~z^(@K;Zg/ [f}@Y[?\̶MNv(nL{]ptv@+Q%͚~ht>y5P ʽ$fH.3$T !CDN.KD2_ث"&{SQuSCB(xp[戏5!Ό P(LHx0: Q_~0GX- hn8K|8d9l{ WiNssΦ.@\-11WlL3V-\ Ų47o*FXkds_bV;q^h-A~R55oy s^竹zfb=UBWkmUH%)E֚LÅ. T;.p4EygoF4v?ďbMIf5{d#*$ u5d d4޵.(JNDT Y^U"HuYKBHE#_й4V`|i@K Ӻ$`$JFS:]TSHXw ET'n .W' 7Xk0HE>ٜ'jvІ{2fG Sʂt>pdTc - 9P0O`z~!f0yTؗ~x`Bfp[yeJP!`{>CKYׇⵎbA_Ne&|l'˶WL` }*whgȯZ #Z )D%)Hϓ˵"3İru'ˊcZΪ$7e )D#0PMP}8ʜj8¿z|a^ۗ:)I(k k`̭%U&ʦP1UM뼭ʐHj4gE;,,ĐKTmЩ$]df&`o/m.{&BEM[1Fyvr+w Ļ ѾhmɩɕCriY:mz;yS4|\\oarG[y,%lS@OT""앰Fq+֫M|݈ͭ8i"hPw,IJҽg]m( k ^v8:T 6J+i0nOc?q΋T{buoZa;_t;:Ml Hz*&Ȃq ,%Gh-bS?,w]x aO$N>2dIі4d{M(]1WF,B(\M8<.'nӰokS{96~ Dm;Cmk5k$&ٙ M:Gy;gyn{rmRU ;̷ d!)#V7 [iHVؕ<>ր+ 1} [7E^$@ێs'?tB6CF HͶH%3f P4+Ԍ,@ӋUHQ e0)p,£)Մ%Ve5 ,.,X({rG{5vg aרy[z i3Ik+\#Zc4rcx) DE<zH U0& -I!v tM)vОYJz:\hPkw)&Q ڎ`?Yo:3ȦBRâ+6:ȡS";ngjl6$xe]rHA¶#sH"w l}[{'Igٱk_+"ڋzA;,*) !|KiVߴBͲfj؋ :u5Pf,DCbj8jpIL;/˰MԁG.NqkPn̚˙PҥQ{o=dqNA&>v0R7o ,FŐ %}o:J,؃q=D0K vv@Zm Mgc8qg @98fQY}|l0p;j/tŋf6MK鞻##r F3^Lngb+K_ uBl10e2{2ܩ]αp ٠Q.kdF^{w vx^h[UoQ m;GmsHg. )sk,׉x9$)&vU`(ye4.K~'6{ux[J)dm#G@I h qO|{Ä%R쭽`N Tj"ѰecRN]X1_13Qet~Z hG8ha~H4]1dd3 `GMQO~$=G~|? FIiY\ʃyM6AZn%1pJ'b&j'Y믶z8ӹghpbc0elJ@N Һ( Fҋ>L&?*Df~Aܨw}sM;)NW5V#w'q5gxyӫL!C' ,sfXqDνWI&$@ I®Tm,e/?=?z=XOP)* B2wO {f&`E*1ެ @g~ariގ^?DjثsYza;{=?cn:oqds-|6)tkkJSvOah)FKu+뼞m c#xs K}MLy}pW҈tu6@h󹾹譈?jΏjkON7h5m VDp Rg"UqWf.ܲօuk$J L˰^4ԯ`Z ?>}^7=$5(tN>DrbJ|=4M4ȇ3)O0q_F6;ΫL K!;lݚ)@ۀ3' Si^\7e bKkV 1zpddk,)+rkM2Ԩa.KVŀ_ 2'hHm!w;^eB1me0˸@.MNgFđh_uExXG|!g7U$)тi`]ʔص/|wsSx#$vH3DNP^h$ ⸉d 6PӟP[~4P9׫Ko@Ցt(Wv7%"ra`9JCIZ YqDos); vh[^!>Be+pnCt[d+ s:F΋e 0Ň Qü?u!73҂oCkCu^erN`Tˤ[Vʣ3h͋jT22,}vJыt{c28;0P,<Q 5 7IynbitC7߿k ~ u_ܻ.+EtA!6KmՑfSWLdCgv@:Q/s{oBٮ<{JP!$)z AKd;Z٪bZ&TT}!3|l@K' qL!A\'LGL)WGep9(f ;5\]qD7yGH]p9QlѢ:4q'=0j] D6R:iFl`TY -vOD>AƄm5A䙙Ao-BstV -L5Jǎ쩣VPWi 歆`c_AeX.)&)=}wI '#-s=ן*9we5:58LsgwE&pQW67[oz}*$ָxӏO8?GH15 .@>k.j7$U-4(,w\`9c7o(SK Z،+V'j-|/ES6\*p#c:rM*'{e=IsQﵦkn5 n*__綮ƞ˵Xͮۜ?e|ޔdKp=EoC~<~U0vDarv,"pӧ"~򜄽soE~^\̺ jG!G&x6pVO*Z|"Az< ڼw Na\{D']]O=:9ջE>H@,v,Wf3oB}s^BCET1n4>rF,iGzʫ/#%ZV!b!~xvY pdFHJl:9gAF7ޕY}!b6pe@?$ْ}w X;v:43JuЙ vO,IEU'#|jwh_ KX[-"bU U"t'^g9=%vZUwǣqDc-JP}\Ynxh%m I&Ŀm6-;ZT!'H 1<Q_aS%l})>̀WLxf/SEIW-hQzˠhDiì_M_Z~́8Qz`37N>>:;mO/Ut O)\Z(DCZn`͈3n o ~-0Xvpp\1#h]4=lWRD5|/"f xH/ FFacSZ1 MpV؍gMDY(/߽]eO&<%d g]T2c@܊9p)[nfA?2 ,=Z6}b\LIr{SԔզ #4k.vO.̃g/!LX3pL =P8ɀY[e~?W{gSRƁȘ0TeHMׂhqiIZH}/h\9y?;ruqv4eT2`x`?M͊ĨS3|% xMցL9 \m@.%#&bئ*iƋH%}KYL7`ɜ>א4_[KB>1(B2]EPD2n=nd1/`߱4FFhHpo7cG0Ze1I~t^>3 35rC|\@ݭܼ_RCE+- ظ5SɄe "=)Q]E W$Bv}ߦo7~A;<@IYIlY^ߢtd,Jf]:db,6n9f{ %asP/vץ'~h;*{^py6x^ZBgzŇ& Fn01|XWzCL; Џbտ,@rpU"DFlWKz6X X}wjfvi ѯ#~{05mSIcHͶ@p 7"~!xBŕZĥ3 ԟ1 X49 NX@U\Re -@k8r&pȝ9J4u3={݌+_"8_qSw =:G6\t; Ӿ]ql⌣{ gG<;:tkIЅ GfJ_D:]7 'Һcx^ē] w,;hgJBR"1n%΁2U~;ze kʌ{|C<9!why5ڥC> _! fYP uU,ZCp)WfP<=N߆Vx9埵gVtS B`LûY2 iߒYW3?.ԄgѨG_#\DžegVN  P{j 4rv}KEAHDRq $h.uRyWQMW)Ѡ[|=5k(0 L$9q@P̜k  NlyWEdIgk8aY堩Ԡ-W(e)ť E6s¾90 œa# ߇54PLH!"ʣBCjQ= 07ʝ!EavN3X-Mb%+yO_C>l-ߕAr"vsn_C=M^BSfN(GJun2< v]I JM jH]ZC>KMR*ȼtI'"Ck^M0F|K0gŪa}C>CqLe rYBRx}{wJĠ>&Kx2 oOi'ޣ =?L$'-OwijW_ Xw*ĵ}X(K|""6rU^qB C6 9 F(sO*CA3!6~1K6፬_@tX mx̫Uo路yl?#*BkFk _si~VcD#϶ށvKNJc{&T'#ՇplDx$ *5\Pt+6~6K!4bG;z8,˨^&W&B_-Ƈ8 z2Գ`Ay3p#)F5{17X{zMHR.^Jj{R y{&r>Wkywbhؾf5nl k55lRޚ2C>E@ +2}Ot+ eEB%D auN9TiBLv#f­m d\||o-\eM/x:8^Q~QH;>9oc(TYv]Qh5t(i+2P SveJ1mH[ޝ|FuuU-7_rKSI]`HjPIq?tdm(-뿽M Q9B;jP9INk~rT>(B/>P\CA7(0] q/#jQPm1n/RE8MQ{+`tNyZȑY7t黠GK֝zƼPb5 Z ؞LK:JxZiZ#v'y0dp45C䫂a>-'Lao_Yf9 359/d !LTL_zA̜@ "de ?z|7g!{G4\RyQ] ngetV^/L |*q`r8s#R@S Jy\?]21eWB.,S㓘 p=uIEjɑ<4zWb"Z:P7<=ĂXØIJYK>fj&(Ix!t5Xvք;V%S`#y/3uqd>9$}7 =8oda[7yU/ `F0*\J ĽR5*xɚ$p. zޤ DNi,`;s_;qtǸ!rUl"MlNOpiY4r/p?Y-Kh YWiq P0NH6F3$ʀ:Z5襛c//:"W~J[4 CteqP_{ )/ oH(Ʋ&|=BeB Q6WyBܾɳaC, 8>\]C[2 v(NT=u$ri?"mSQU7jCxOƔ5W_P!B7A W@P2bhJj>!jec]tq,8 qβ&[C^k,|j3ɨܩ&u v endstream endobj 309 0 obj << /Type /FontDescriptor /Ascent 905 /CapHeight 0 /Descent -211 /Flags 96 /FontBBox [ -560 -376 1157 1031 ] /FontName /PNJCPB+Arial,BoldItalic /ItalicAngle -15 /StemV 133 /FontFile2 310 0 R >> endobj 310 0 obj << /Filter /FlateDecode /Length 21124 /Length1 33104 >> stream r;\AܾV1In'I.Õ+߂ ,H'ڗ{ ZTh%5|EIH&^j1F3 hGaK)cKFEVY vzA-g kr \Yö,{D|'!2:& jtM?fAnv:Lۚ?ڜWӳp!4a!]}1vZvg5w繂S)TZp%$*>lV`Gjf'_fʯ! ̣Ct}\& z;g!k%$JD0[QfJs d~"0i׏h&F/[@jtyKXHo.+235dJKq[Y2L& l(57ca 3͐?f]|.:$e%3/RC뚹h<kXK}|sƲi$K1cL<=";›gvZ=/*s]l'ggd&IZTJɴp4-=nv)yٗxbZJT j-\* Odۑć贳Rub](uR`2o,rޖy,`v:-E eh:X%!{ / &^h'S§9x6N#+з^ѽ|~v|t(y<\Dؿ:%(.rI67*S-H?OKNtw ,+ DZmP҂4L!L-O-3^䜧fuOx1Y=ݦ ];%7l#vU9pvy7w0 ZO\q2_g.>|O-Z1wAz&8ZK'FXSG P脢J|y\bq X3[Wscs<tݷ*lLҭ/LBJ,{߹;(^LqE:S;*R8ͣծHº1hyQ sw\Gh ϰ/y 濲yّWANy̫J{^a QHjѯi$H{4Q c]FBP.Op5Ov$cbݙ>]B$*DCWmkT?; EēF:߽y?uw;R ?da.@8ɔFكէKaԶƬk?3N˝Ȩ_ i,^ n=~k*97Vx}Ы52KvUu"X. ُMXF 24,4;jzI]8ПZO،qyⳂ& gW_?~׎hko?Wg1n vZzԑI} #r8( \fp"hTѦؼ4vhFDz a35#Z(c{Qǔ3,#os戇2PH/ R#k4K_w#zņ {E?:08;'o.{Q 3&q">XkP _ch ]vr'ʊ`Ů/:dux&gUY]%u>Âjlg ġKX.6!7>)l@|-FB5. ٦Y-^|sȀ%G!BG$1+4+. Ļcm~Y&\2zpcTdfoV;fQIVZ#h4p b^`v:3I]]nY#~FK^5Ĉ  $XzErwG ^'/ṰcsX~$?IEV\֤AF]D+1_.%ppxu%@{|뇠X/#_`Bcj'Û(O)(],lgEۨ[𛛻SO@hpV";})NCl yuQ>Γ`:NJXj >gʔ!_x V=FH@_ްׁPXF;Rȥw} ^J{"擗qx"k92cj.>u.<Ѐkgv:*YZ]/~|7:@;`O = rUm l_a/, DG/G<ԒXHSu7Xo~q/\4V-Gh$_%b_x5x|Z'JZgXLEt1aDmPv BPv4߅*nQu=T吺9ׯLGKmO<[,GdkZ36@T2KP]z,BѦ2#w\'~7lLl h @_W̅{"ytJJE%cAI8.Zy JK6DXɇqٓ@$m7a?;A3ugMaAb"{fTxc,ztG%ő3‚WY GFF08޼e?z )I>:Kީ,t?UxU89aM$s[Jb@"X\vVS{$J 6;X0lm'دV>Tt[?$rJM.Kդ g e?NB~ۚonӳ(4Ԗ=ⶅ-r)qv26?4}ŌIx=!1EXl[JX\b@ؼ(\qY /m٪j hzFg\! Y1VEiuxNK,Ep$Y!ߋn|ޢ:0S"=l4^@z?U%w#"| 2'u#KCGK q4=AE6YQ?15R(2[gC}Hxf{WF̵J'x2 n,w;+ӒMH+ P ]\]TOK]{Ŭq1V_xǵ ?* :JOny]nA)Ё1xc_ u+#.BЀ;|an0AtW!Ͼ2a2i^ L7lT=hjI3Tt2/[~v̦伯ƙoaq"9)Y$*ƲB".c^Y ӳ 4dEFN7ZP{G0/~վTa}Jh2cY}CpajxwG&JPEm7v5z=1bP'$sۯ jN¶Kf}8Lc]M4Hiɡٲ;Hp @H ܍9Yu)rH^\$&Q%uC@P}8׍Ϸ9?  HZ{jwl FBJOYAv TgF>ʽK֝ ;q@EÊf-\4ԸHa]=XN1Q7u%0h;"LC ư*wELU3LN kgUˌC䏃,u5#jժJ9)VR1u@=nC>Av|)ѷIU#D ;dWͰĩ9:ʍCûil _]3 ޳!#EChьi:rFe}3*!.SX p:6œ&@ LN+3,fL䉹^uy0ڟcg ʦO?I0#{:NyLw_ v{SiGǃ%z! TfG44m^p a?r#4b_\јyO+—|%v'5CmwjJ`A H :HrJC)٥X dHo0*e"eE GEsXbzF+Ǧ6>ktznE tg C5Dzž|q:`|d+KG.ܗ;DUcFoC[nM0@Opaei.Bs1-/؞\-.6 S4ʐlM$n1&J^kbLpҝe.?Ո.+r[\.ZnU'U,.cÀo':tG\u|m&tEnL((@;tA-ώE6`FQyx^fD8{BD.4}Lw֘ݛ_i;SM;; P~Q{AL=G<=N~BAt;Z}m{3 O xxVa\Y"ݫ3բ ;3|Kt@Pd9,so0C6_ u>K]і}K@ ^-WH8S4dcI~q3] ڎHd6WSZE1H*)3۟gEe%#SȇhuӾÝ26PLg' ȺS}@~}+?kOdljIu :OjkWQ*I]OY!tf6;jFp]?7yH Ѫsnj"d:c)Tdڈ9LL` gg]&gF_ [#MP& gF!G-[.kݣ\O`_,ָ ꔙwѳ\o0AR N>&-a p7Qx4AVO1eqIIk*R 4֖h.2ASM<?)L,L>fJF"=,rI+,;îXEY &" -38>ttN"ήz-w^QMv.&%y_JNrk6#KFŭw|![U;ij|LH ˀi-=}h\,TfQ hQ$:&c:>@j wknne2Ir 09A%&B$ݲ+=psUXvqU;ޘé3>)]%O9W}KH\f͏[%C AP5WghL{d#LZGE|p0+r#q;tN]:M$ľܼR3NVy%S{^cRslOD<[2:FeBmGU9yAՁ)]tO{ʤ.n7=҈"n@_݉ eltp~d( wP G%OE'w[Ae}d5Jg3u6EXTCC(Y[hӔP8e.]UqE(= k+HD4h/ϔ+*oq ꃦ`. r[-8nzԘ|McnDz2Q^rq$B41T UfSHfb\(N, D0"l1#Z&ԇdƄ+fH83lD쯚[Nު4if#8ﺈ N~ohQ~ Y65\Aơ> M#]/1:]S^Tyڹ+0 D1W5"J9::9=e@;q `:~%%ّliP#/cw;Ѽ:\F]yӎ[cF5rTDorO A 5b ZzoM?Gi8_6J_R&~3K߫8GS4low9?zQQT>3'V˽mĶEPJ^T6?? E`ش_ F_v`[8|h>|k(oO)ob֐9?8]35 ŐvTӣrpR;j/Fjԡ:|6'յ<>)EWʹ.ȣ'1s.(V>dӄ{GD 餣頃 7\{ɓ;V^qL:FM4SQBQy6NK2%)~`3Qun0m ^gS!.}G^_9zPsCVnTR.SUAͭWxmpXQ%z^װ@drxMi(c;b~ǮYڔ U˃@'s9Ғ?;:d^Z?Wzz*&;bCkiYJ~oҔRg:hf* h%gi>[ܦZB὿T7Ⱥ^`)zĺ9%;o> Wx,(+4#+'{74kd_1<HJbB%FX-$7n#| {A\D;eފaԠYARHH^?'(:h[U_m(;>#cK?uh7T/7Nd%l/&cBԄN*uvNf[i-E47ҘPWְ:;mY2> fŠbF=N0}8[))xVyeng'uT@(cK "$̱S9G),YTOE>[͐},,K Rz=5?='( fޏݝŎޝE7ioԯ݅'^ȒNhR N{Rb>TXRBptIYxߍ$8ovS) d?9qa9Ac2{&B3+>$!3u)5 og5=-?VJdd&ϼqJ-|~[*j' 2Pn (|n&]9I`CЎ/%}R1MfA<Ά/3նY*LqF[x+}'dԷ^ڌc[V2Swm TĜvhVR}~ͥ{&.L/>|ע(/d<ߓQ5:p\~ XPyjl遈0#5F^v%6vT/E:(u)Dv-GY=GkR{@ 90כzGYȫnI$Mqc(嬬h$"oc '} fDκg=S$#QmԔd,-H|^2nue|~4#bBf̩4 Jôg*ZC\N\Bܯ7f>ᆪN/Qs|s.@ m~+3KӼ+$N\@ƨǟE!`/ ?xAq'܉<Bc MLI1Da[/n Mvmԝւ^jT=4`["s JeLyLyVEkAY$e5xȾ`Dѐf豰\{͔ gZ*oZuM"4ET_QC) ZKkD տ89a9ׁSq\%H $"+:>&Ck_Ybl,VBuMI ~Hۍ%RvRqʦcksҟev>W//m7O`(&W9~k<_D,."e# q%qWN &Bƌa..Y8I=4n{GHG$;!nWj'%>iF ^C !;nP)܀lt[H!̽"*ȜZЂObגX3D'\/ n#xg%^$fN<.Q$6 EF#'Bo*5JUC#*s.`v'II~gE˗ipX(gĂin;D[Y4rq]*U,^^4vU?BeȣM?%pi)8Ĵ">i&x5mك&.a`CCދuF` y'ς3`=?,ZqbF|%4*؅~N>DK驘aOaHx>8SI.82'Wq2ofS;:ؐFh[<N荧6V!phR3 4vŬ)LȧSp<{ i PJІTf*Sww9sL`w[ 26{ԈNi-$\'r-mZK A4'wИ!:m8`/]j saq3HBi]B{ZLwﶥq-\lDxɔCdBHT\_3榰6)\IY[w8>f OБQiTL!ߟ&r]iԙJ' ݹu| iZ$e+* y *%GUmMOUE3A,N&*9yCo6Ti6' TC(7 v Yѣf٫^" (c SǴ[ʑţ#1>E7%1{,C$@lF} *F<+9O'Ht,. ڼ܃HܩfGSb ΍C a=D )' 2F8.x"~ojg_ZGaB s!(V2_=J=}+0p ` ܛ8wBZd{=`?lIʕ gCS,MV4{qm@"9RUUJNQC!WTíZ~"G OH)Q2}[=vjC]_3['27(5u)L%}Ӣѡw&q_`΅6z($?L 3QCB CwnȫO gvY+JG~[fFof`3#zC 2'o"u֊K8иOn"Sտ=@J9cB8=*bW*$ͅƛI5ݢxfl?Mq.k 0\NJ˲n .7DKXqYABbTm6ݵ@TI|Sȍ0ф:lLP܁כh"#5x,1䵧8D!t>ov gqjtu9Zi9 +*51a1[f|2KkjGAl=:Y1l1erY{'b"EAyTz`ى5y-_bZĿ÷IAýg|eΒPNJ S; lE7 sGBY*PJHNهZiG}gksyj=ݏP*CH}OfMqya3=bI7͉n cN#T4_O =*Q8}Fsb8~"gEC$-I.CF&vn@j*L3GVHnAxAr;-EK|jUP0ΣE"Oq[F^GJ9FM .-ifΥpLjzI o-ݪXNQz_O>Y3HTQ4݃S o'N7dҳqӀu5?Dm<#Ka.De7U3Ⱥɜ8XDZuz*X=G:, ͕;\$Y<EN:Оl!T^Ԍ:|+7UN R7\*e67[a\?,v4MvI]x:Y2JJmlJ~OiHƹ*{*ɞV4/D} NLǿб䛆.=^f 8- ! jc2V$,8OtZ(IPeY(U1˔5<mRm Ɣ1D'mL5'cTɥOD4q:a*8Hg[V||C7h6'%O ]s%fzqk(E{>QUv$VpŃ9gy}S=C 0uŏ?FAѣ+[s'4 !~T]3?ӪWx_o/,Ԋdk50([?mmvXw3mEXj.8>Cⷯxte]w$ ``.CXqUK.du_v ϧߟdmC-pmh0YF E6F:`SK#bEsXP r!AӫApSp:_fjHoK,ya1* -D2TFINeaU|2) )Yܘ+!v0Zn`<SP;*_%oD5-(zO_rMD ,J o\QLӲ5*X7 ~;n*jOq64N1@,!LE@Ody$ 確$(F1-ΏA{gM&e:ʀy{ܴ_+9Юa x' pCobƏe#>sspWA4ǭhHwP\Lnp=i#/ fF~Le:/zF.g)/A\&CE}2emAb[, (YٝlPB=c@> 2ٙ33.^}}@+G_oq7 8i`ȆV'hYS ^tb6g3X'GTa3g{O7̅+FWos*Ї#ez?@5}Piu3 jmR70m, */z̝-I4P\]ƌᕈa D7ibc;>~ U:0 Ńr(mTתqkޓ * ٸ\qϬ"8eqS;;/E唠q/W)O9`Ј٦#ftI9gYRiژt :si8.kUoȘ\@b;^&% 04=)γUD x :0ڬͤf{+.rn]~˘cy6]6>$w?T(sU_(]^LC" Y={G-:Ytrn{SUxbg:cĀ&OabzFc:y4U5`#buUMW/,T9M sG9ֻ[._+8 u h0o&YՋCdTK2b{ >#[,/Q`Sn}u%$Q69l]pLUG%ҼN?kiH՜;IT'h9_l0,W(4 +vX`XqH'g! {tӅ߬^dewjęʺ&|h픙$F#++2j8nmE]`]9"^wU+P]D2 S턔[j)K&4A4z"?5g3 QFk mˆ*zƘf5yrb{Ca cMSZxaeK7H<3X  2F$.W⭩>ӢLTǚRA.\w2psTFTcbZA"QEs,gu0+vȰm1Ibo0]2)pKvp8BG5O~Q[hȬGƖ7o,%eZ֠<Wx~MX/w1T~o퓜d&:G1ړrE-bۧ{W8p"`c!Аn OM HM->qR,y1l Wƻl3'ף.24HZ|4N$t&IqpG$% 0@0zW dmwe1mk%#fR9K IٕP&"Y5X%N r)-? \ÛSQ 7ZV6'р|gH?>E]xלx.F L&*Ih- &(p[URem6e+ʅOp(lK)ab{yʸ1  Ec@ܙ0?>ρ |2>Sd^h}n)]|wԑ Xӆ)r -e3N_̓@YuA}2Ifa؈0t} pLSiLj@7 UB$3|CUM;1{z6ږvVln=)ߜ>PMI]FAd+bV-ϕ,Ї~ĥhRWh9 _dmh Id02{̇khp#Z9m[ HyTIf E:0nM?tb餧ik=l2JS(X53hmPq W iT*-b/fEd'_'P%qr&sW@͒J s^kw%HRSTU xzD1s-2 ]iF#) ۫j$4% 72E'eϤQh2JY!%QRU0$vLф8CsKEfK5߅ę?/Λv\KZG`h8틂6أ "UZxj7˥!(r_!'ۡtӞnYk\;0haqqBQ+mJs-Fڦk'g )v)|֛0Q`H9,tk<([|'(Sd5MтoTR[+)b46q#ߘO*[mhά#ameδ&~&V J NT{ ^Pgy@`S8tTQL) V:I8J_}|UdVKT ~ù:2V $W׃R4$^C0& x>ҪY٪U4` 3#=6Tli|IRr I,8:%KaKnly̚dlhܬ $k^Wׂ^ ht<-A_ų_ U| (2p䒺(:[ ?Y}؁D{>B3nM{-\Ǻeg՟+Zς?b k% rncXQj{ͻ=!BR mry=[Z`rO?}s,5fVqu 3Zp A),,H' z"aZkjD׼'؞r-~9 yq)ab%ƦuL]FӢRLNAIH/8n уޡώ_!ŚC"jK?fkl d5cEz%RJ[ZiD눧FY =?q7-ZCx`7Eadwd{Q1\ma Lw_Jk]VI"1~¶ A~xƁY?HUlB:WڿTUy=?509gN~Rě:~q1#ʝ,^1ܡgbPQl(]'F9Y~CT3%:Ly7ޚP0 f Щ,^*ʛPt2K#WYA)W|MjUQe)6 X_w+k$ϢAu:QR}^TR&̬o3lX:>gu-6c+NuDBižVP$ _劼>e/t;XU5{sF0,#\3&ADC?a]FuMR"/1rGv{4]ilW`<[[;| (!Hɹ)a3Cq 9lRlpxw3dT Tn.()~ 1a#i4($nB:ꇶtNťgtbY8VP8FUSu$$̋^"SYĻX1ҜƮ_7/!H3`w: Jz ǻ#>s@cSCP.gfLyBFĖĘp?97m˺+$ͽPVC%ҞZ jCщ!To d&p v ˼H2*^wgMݿ25d܎c \2Q|I;}Us;g.㣟XtJϵ_7 NJX`*:ѨOo-jD:LmI恇f%j٬<+0.숂#SA#u4,:ŀ `{7UB)Cԍ@KN!I8}_2niuIvISt[ w͌Za?!iLgS&|҉߷`ئT+r-6̔R{o4GEpɞ_hG2hOij= 2SI)SZ;RMZ~:DO; |c`,b+B*w )\VEl֗$LhIɤ@T\=T'KoF+a*@Ҝ}jmnC-Qo9`L=&eJt,fA#J6_ߦ=dF%uC=m||U;5]\խoˬJuISRިds[F # U;ӁѠe O: TS$JNh/Z<\Ifi֞9ǫ fq)+ C(;%ܤ- ʑZbEo7ҥ&WZ袯U;j$YU6_TVh5cq9ϞƿZ3qxRo`H$ܾQƣKi%Dל30}O 岬t7ٙ]+a;s+E.' /Ry.fQݐ: oH,ڠ6)+~%oN}Fy$w`O8)'t@3ѿi[3~7-E+8!ɥq~59p%&8#U7Sxv$oAk5OC}^9HuٶOP7CA“>>Xq^m/=VS55NQI] a;'3:$raHzeTPw,(BeoKM@U'BUlMbb; A-ämZZNǿ3GvO\G–R ry$tEzы"-=p-cz2 @\]dd6KM $GZSAlM cDosJq4V!l(Q^>J[{!_[G9Nh.ɼ,8B7ß/5 L'N?@M@_ - ;ѽe[,(K6ca$Sj(HHwzWca=w=:c̐[P&12qp+, b I\sv-.y'!`&9Ӂd+Uz5dNe(G%SzP3kwcrfUX*+vŒn.9S܂~5f7Y}y3qqC,1wR_'<ތn ` endstream endobj 311 0 obj << /Type /Encoding /Differences [ 1 /Gd3 ] >> endobj 312 0 obj << /Type /Encoding /Differences [ 1 /Gd8 /G9f ] >> endobj 313 0 obj << /Type /Encoding /Differences [ 1 /Gb7 ] >> endobj 314 0 obj << /Count 64 /First 315 0 R /Last 316 0 R >> endobj 315 0 obj << /Title (62yY\\) /Dest [ 73 0 R /FitB ] /Parent 314 0 R /Next 327 0 R /First 372 0 R /Last 373 0 R /Count 7 >> endobj 316 0 obj << /Title (8cV) /Dest [ 233 0 R /FitB ] /Parent 314 0 R /Prev 317 0 R >> endobj 317 0 obj << /Title (yɘE;wp99oW-) /Dest [ 230 0 R /FitB ] /Parent 314 0 R /Prev 318 0 R /Next 316 0 R >> endobj 318 0 obj << /Title (`ɪ) /Dest [ 227 0 R /FitB ] /Parent 314 0 R /Prev 319 0 R /Next 317 0 R /First 320 0 R /Last 321 0 R /Count 4 >> endobj 319 0 obj << /Title (&SZ1~ZS}> endobj 320 0 obj << /Title (qvsQ}~OT\rPg{) /Dest [ 227 0 R /FitB ] /Parent 318 0 R /Next 321 0 R /First 322 0 R /Last 323 0 R /Count 2 >> endobj 321 0 obj << /Title (ǜ`8p!{wQk> ?RzQo[ȴ!) /Dest [ 227 0 R /FitB ] /Parent 318 0 R /Prev 320 0 R >> endobj 322 0 obj << /Title (ݣʨ,ot;I;?O?J:8`ѫ 5;\rG ) /Dest [ 227 0 R /FitB ] /Parent 320 0 R /Next 323 0 R >> endobj 323 0 obj << /Title ([c2rMEQX=) /Dest [ 227 0 R /FitB ] /Parent 320 0 R /Prev 322 0 R >> endobj 324 0 obj << /Title (@3/pOU/0^qm) /Dest [ 218 0 R /FitB ] /Parent 314 0 R /Prev 325 0 R /Next 319 0 R /First 326 0 R /Last 326 0 R /Count 1 >> endobj 325 0 obj << /Title (;DAlL) /Dest [ 100 0 R /FitB ] /Parent 314 0 R /Prev 327 0 R /Next 324 0 R /First 328 0 R /Last 329 0 R /Count 34 >> endobj 326 0 obj << /Title (I鵳*) /Dest [ 218 0 R /FitB ] /Parent 324 0 R >> endobj 327 0 obj << /Title (z}I;:@U) /Dest [ 82 0 R /FitB ] /Parent 314 0 R /Prev 315 0 R /Next 325 0 R /First 362 0 R /Last 363 0 R /Count 10 >> endobj 328 0 obj << /Title (D˻TM !+Ǿj[4h") /Dest [ 100 0 R /FitB ] /Parent 325 0 R /Next 330 0 R /First 345 0 R /Last 346 0 R /Count 17 >> endobj 329 0 obj << /Title (-]fVȭ{:rPp?J) /Dest [ 200 0 R /FitB ] /Parent 325 0 R /Prev 330 0 R /First 331 0 R /Last 332 0 R /Count 6 >> endobj 330 0 obj << /Title (NϦ# ݿ6~\\RN$ǻE) /Dest [ 182 0 R /FitB ] /Parent 325 0 R /Prev 328 0 R /Next 329 0 R /First 337 0 R /Last 338 0 R /Count 8 >> endobj 331 0 obj << /Title (Es|jmG$#KL) /Dest [ 200 0 R /FitB ] /Parent 329 0 R /Next 333 0 R >> endobj 332 0 obj << /Title (ֆ,Zĝ<X36) /Dest [ 213 0 R /FitB ] /Parent 329 0 R /Prev 333 0 R >> endobj 333 0 obj << /Title ({:ԸȽi/ADCcH:) /Dest [ 205 0 R /FitB ] /Parent 329 0 R /Prev 331 0 R /Next 332 0 R /First 334 0 R /Last 335 0 R /Count 3 >> endobj 334 0 obj << /Title (G7aP0D%y\(vv) /Dest [ 205 0 R /FitB ] /Parent 333 0 R /Next 336 0 R >> endobj 335 0 obj << /Title (n9oIF$iʗ׺$P^vhtO7) /Dest [ 213 0 R /FitB ] /Parent 333 0 R /Prev 336 0 R >> endobj 336 0 obj << /Title (~V,7y5NIE@ ) /Dest [ 205 0 R /FitB ] /Parent 333 0 R /Prev 334 0 R /Next 335 0 R >> endobj 337 0 obj << /Title (Enir\r`qm7G3) /Dest [ 182 0 R /FitB ] /Parent 330 0 R /Next 339 0 R /First 341 0 R /Last 342 0 R /Count 4 >> endobj 338 0 obj << /Title ("\(Ȓ2 ߋ [<"K4) /Dest [ 196 0 R /FitB ] /Parent 330 0 R /Prev 339 0 R >> endobj 339 0 obj << /Title (\n7r[}ﯳ39뱴^~8) /Dest [ 185 0 R /FitB ] /Parent 330 0 R /Prev 337 0 R /Next 338 0 R /First 340 0 R /Last 340 0 R /Count 1 >> endobj 340 0 obj << /Title (-%֯B<8cV) /Dest [ 185 0 R /FitB ] /Parent 339 0 R >> endobj 341 0 obj << /Title () /Dest [ 182 0 R /FitB ] /Parent 337 0 R /Next 344 0 R >> endobj 342 0 obj << /Title (2C~KIYry$Mbp{ ǀ) /Dest [ 182 0 R /FitB ] /Parent 337 0 R /Prev 343 0 R >> endobj 343 0 obj << /Title (-m Ϲvwu\rm) /Dest [ 182 0 R /FitB ] /Parent 337 0 R /Prev 344 0 R /Next 342 0 R >> endobj 344 0 obj << /Title (;]^S1\\:\)9) /Dest [ 182 0 R /FitB ] /Parent 337 0 R /Prev 341 0 R /Next 343 0 R >> endobj 345 0 obj << /Title (Y і͸@7dIs@]`ZBF) /Dest [ 106 0 R /FitB ] /Parent 328 0 R /Next 347 0 R /First 358 0 R /Last 359 0 R /Count 4 >> endobj 346 0 obj << /Title (* @m|c<*wjm]}\\) /Dest [ 172 0 R /FitB ] /Parent 328 0 R /Prev 347 0 R /First 348 0 R /Last 349 0 R /Count 3 >> endobj 347 0 obj << /Title (|ǧ\\wRPPa!&^qs) /Dest [ 109 0 R /FitB ] /Parent 328 0 R /Prev 345 0 R /Next 346 0 R /First 351 0 R /Last 352 0 R /Count 7 >> endobj 348 0 obj << /Title (>,Vږ:0P7\)!!"3t) /Dest [ 172 0 R /FitB ] /Parent 346 0 R /Next 350 0 R >> endobj 349 0 obj << /Title (]? xJ2~N-O;+}6"0) /Dest [ 177 0 R /FitB ] /Parent 346 0 R /Prev 350 0 R >> endobj 350 0 obj << /Title (JM]4J\r?۸9\)4) /Dest [ 177 0 R /FitB ] /Parent 346 0 R /Prev 348 0 R /Next 349 0 R >> endobj 351 0 obj << /Title (uUkKyHVHmF) /Dest [ 109 0 R /FitB ] /Parent 347 0 R /Next 357 0 R >> endobj 352 0 obj << /Title ('\ňhG+'8fh) /Dest [ 166 0 R /FitB ] /Parent 347 0 R /Prev 353 0 R >> endobj 353 0 obj << /Title (ׅX~D^FO) /Dest [ 154 0 R /FitB ] /Parent 347 0 R /Prev 354 0 R /Next 352 0 R >> endobj 354 0 obj << /Title ([wRl*> endobj 355 0 obj << /Title (- _?7CWH*F) /Dest [ 142 0 R /FitB ] /Parent 347 0 R /Prev 356 0 R /Next 354 0 R >> endobj 356 0 obj << /Title (sg*W5g+\)4sC) /Dest [ 128 0 R /FitB ] /Parent 347 0 R /Prev 357 0 R /Next 355 0 R >> endobj 357 0 obj << /Title (Tϐ_3[> endobj 358 0 obj << /Title (hJpW) /Dest [ 106 0 R /FitB ] /Parent 345 0 R /Next 361 0 R >> endobj 359 0 obj << /Title (4SH,\\Ch]lZL%$,7Sx) /Dest [ 106 0 R /FitB ] /Parent 345 0 R /Prev 360 0 R >> endobj 360 0 obj << /Title (Pb6E!S !h,Q) /Dest [ 106 0 R /FitB ] /Parent 345 0 R /Prev 361 0 R /Next 359 0 R >> endobj 361 0 obj << /Title (P\)|> endobj 362 0 obj << /Title (Jqh/bbڏlnv) /Dest [ 85 0 R /FitB ] /Parent 327 0 R /Next 364 0 R >> endobj 363 0 obj << /Title (eȔYIm\r$_Z \n) /Dest [ 94 0 R /FitB ] /Parent 327 0 R /Prev 364 0 R >> endobj 364 0 obj << /Title (/ +[@}KfshFj+ KN&Jc) /Dest [ 91 0 R /FitB ] /Parent 327 0 R /Prev 362 0 R /Next 363 0 R /First 365 0 R /Last 366 0 R /Count 7 >> endobj 365 0 obj << /Title (@<YIr) /Dest [ 91 0 R /FitB ] /Parent 364 0 R /Next 367 0 R /First 368 0 R /Last 369 0 R /Count 4 >> endobj 366 0 obj << /Title (^$) /Dest [ 94 0 R /FitB ] /Parent 364 0 R /Prev 367 0 R >> endobj 367 0 obj << /Title (γ-Acrct) /Dest [ 94 0 R /FitB ] /Parent 364 0 R /Prev 365 0 R /Next 366 0 R >> endobj 368 0 obj << /Title (nvxm=};) /Dest [ 91 0 R /FitB ] /Parent 365 0 R /Next 371 0 R >> endobj 369 0 obj << /Title (9WyR) /Dest [ 91 0 R /FitB ] /Parent 365 0 R /Prev 370 0 R >> endobj 370 0 obj << /Title (l.1´j!Tm#ZJ) /Dest [ 91 0 R /FitB ] /Parent 365 0 R /Prev 371 0 R /Next 369 0 R >> endobj 371 0 obj << /Title (؁7rt Jt·џ?=) /Dest [ 91 0 R /FitB ] /Parent 365 0 R /Prev 368 0 R /Next 370 0 R >> endobj 372 0 obj << /Title (`ʦPV>> endobj 373 0 obj << /Title (ʄ\n\\ C\(ڝfV[EE[\\Y@Uv) /Dest [ 79 0 R /FitB ] /Parent 315 0 R /Prev 374 0 R >> endobj 374 0 obj << /Title (8N]z./Q\)d?Ez ,) /Dest [ 76 0 R /FitB ] /Parent 315 0 R /Prev 375 0 R /Next 373 0 R >> endobj 375 0 obj << /Title (da+M/$) /Dest [ 73 0 R /FitB ] /Parent 315 0 R /Prev 376 0 R /Next 374 0 R >> endobj 376 0 obj << /Title (ߝĦOcPEڔUqkEހ@\\A) /Dest [ 73 0 R /FitB ] /Parent 315 0 R /Prev 377 0 R /Next 375 0 R >> endobj 377 0 obj << /Title (S#e&!ӛ4Mه) /Dest [ 73 0 R /FitB ] /Parent 315 0 R /Prev 378 0 R /Next 376 0 R >> endobj 378 0 obj << /Title (g4q8ʦn) /Dest [ 73 0 R /FitB ] /Parent 315 0 R /Prev 372 0 R /Next 377 0 R >> endobj 379 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 380 0 R >> stream N"\xDsxvKkm+-# "9΄}5V](l$ztgR,Νq~QDчePdRiu%Sf$]6zɜeE"UGU!A̞eODȨ|t8}6h0zo5C+*Nr*H@,^tͿ}`?P!o d endstream endobj 380 0 obj 196 endobj 381 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 382 0 R >> stream 3/zOWWaPğ)0Rۇ4Ne0呍~}RZY[9wѫ@I7v=n@3&yxILhGpA/lpE{uC; endstream endobj 382 0 obj 127 endobj 383 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 384 0 R >> stream c.+-BbR}nVh8ZUPڎ?IYWˍ!}@p zϐ/T` GblһmVB*g!GW7͋mZ0RwU?9hbT*[+*^Zڡ@XbA*6ӰO$g3-ӵ& tE 14pơ}.+Wqr"e?I)Z# uPF&{XEXfKdZU?wT:_qJ߇$< R+]HeLiI,~=Lct*Ex} _D (LƘwjmOF}C eK]&>b'bʎ~2^k̼[T_ endstream endobj 384 0 obj 569 endobj 385 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 386 0 R >> stream ]m4Bԅ➗ghAt1cP LJ79LrvEïV ͮx2b,vj$$KFE5_X#BP},onY!E̝~5}C-$Ikv 78eL7w=o>z0Kl8}[ϤiQT-dDn0Q endstream endobj 386 0 obj 219 endobj 387 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 388 0 R >> stream =>Jje|K2eYcgiF1!lH\T(#jd =@J2HrCh\wE٥j˭2%f9ìfs_SԴp ֙S`c1$N,4gxSŧFob=Ci2Z7.n4t*i,-'=/EHtbߠP-|Pǔ.3_+d`uV,6WP8G?!T0Ȓ,[=oO!_΀,(ƺ+ O4FL/]AȾB&#&#JMC(HVCONZAUo eZeSa Iyf+3E<]Kqi}-Te> stream OEa=lE4w;S=1|s>1 uSV2h ^GIFCU\+LExxcu;6wѷO"u2rU]b Zz0dw&;lIWw.ܢc(;+ՙޟ /܋~0jt%~ ĥ䊨5}gs!xa脎mnO:xqGTq:aE"SP 0uY 1pUB K~&sV27!W,MhLJFZ&#`{*͆F7,6Շcq<&/BU0ų7Bc{dG;\z妷۴QpejB8Z$ Up~wZ]y&NJŗ~@mI5"BOWyiyJ3.GlUx^K̈́J ~nvq} فb endstream endobj 390 0 obj 526 endobj 391 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 392 0 R >> stream J݈i[ }DR+F:Λ!Z i4J: xR S9'IHg3F<8#$sV $ǂw.]W&S}Ft{W1 'ub&,6I&! @ z^J3)M]-;]9>۝yO!3pq!p:ov eKS$zЇl*xW/;:8qQ,ʎE?AsW%#(+ ? I ]!^T1_qdʎډL endstream endobj 392 0 obj 300 endobj 393 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 394 0 R >> stream Q]9s!o6mܵ栘Z)Qbe Ey $ iDoȹ9u4n1m`h{ueQ֜/NV{'9_^H┇ME3sPޫ[ [1o/{&W2tEwZ+ki}h%i 1ૄ#[ Q-ƭmU:ٶR)3B=Q?k a/ۈu`n?2U +Kmm20XnSNI p`,料{7kTơ8 |oO9f&2Vǰ]m弦pֈS Ql6juF? X,]в \,.e7{4zkz_Fçrjk-݆_C4PX endstream endobj 394 0 obj 426 endobj 395 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 396 0 R >> stream Rxm6%n7d99~/O]kG:^HW7ru]w [:x72yxGGś"i7'nL _P""FPm:%# (:ؽ`dMd4"޼Ӗў9.2@y;^.ilv p b-qEXҙ:(ד n%h5ᖨa4E dRH^\x&OZPC D'iJx|,(dٶ&үE(QݖVNQ[xv1noWcE+mh~B~. 8L⩗] a#n:хIAi󥔳*zLD;YD1HFi0]Tx[ޮ?E+oZn0&>oqGwǠQ|RO)D-믄zj WecQwaf+J\FPB!["I)6ew>9eD U2)<Z'#nCB_Dx|d endstream endobj 396 0 obj 588 endobj 397 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 398 0 R >> stream p?=E`K{Py~uK!@ZnZgb)7p䏈G0T>A 뾅 @eɇeϮi95!Jh+Q)-3#ɑ"C҉nM#KN} Vim|TgL5eovk3o|S*\W_,`p&*-XШ̅{ųP9WIpM 7aOE7:aɰ{==\UA،Ex2vPœMb;f L^۞yHy*RuR/]=@KƼk4e}Hq~g<)% izrc˻ӣ#[ hmY28$՘6MESR;{ZaCgޒ vƵ>h>X_Tͪr hr]p/D8tCqH1L݉tכ!0_()4Go)˞kd#@?Q.Ng;Cu(Ğ&KRev~Nks62uVO?om=#Z4nfsyY@jZI7g/7NΐbԻSJ/p(bh5> stream UNc߬JY([o[9Nb[1bPO3zl?xυrl;x8R4MZC%Ɛ'7[Sz,,GFE7B6m=GI~{F ɽs!lRו?Siw(o,TkzjQr[bԩ|M:q}hb*F>.QIxzIƹ IFt8bP`LH d_xk2(]BCV*MCG,2ϑwv/q5. tLBЛH A$̴0Ib㬬_$g䮠ʮS.7 ;Sf\qFPj >[ N%L z+ux.B]`Aoa l\W>_ N $5+ 7mHラ.:;AIj`[RKջDMW]u0KW=OIy?Y#5Zà ApꚒ λKG870#G1d:UMC:2{$ endstream endobj 400 0 obj 572 endobj 401 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 402 0 R >> stream GNy!b;PȲj!*[LX *wGʈ1+YzϜGV*@Y<6![7Iv$^r%HYf*t8"SXv}.㗶 ]HN8`Udz #ӘgU'*JX%6lf6f62S"C%aZiOѬ,u\(dM.@]\T|G5/Zq^ɼiP ;-1;S&v53 0]Kju-?<',Djze,4B!*|*dJSη|rtuKG=}gpH|`6mn}xP@{(x18˧^]S}T;-+VtFvFA;߫2[BY:_Ё)2f).ĘrGU/e".!.jXDy *㊂0n۹{ endstream endobj 402 0 obj 562 endobj 403 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 404 0 R >> stream ?d\vkETU +tf&GzXisM"CһWh":ͻkcoyiS3IzD<9@'Seբ Rpi[& hfV9SK]J6D癈4d+bU<en*(,Y7)qWV&Og°& endstream endobj 404 0 obj 204 endobj 405 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 406 0 R >> stream Ɵۻ өj Ec`rɩ ÃFJ.wĒBX/{gG.Y9/c{"@zA{k1UdY%Z VtbE`l yp<ճE9a PxeQW2G*Jbi'w_wQhf+3Ue%nғ0K(̸pW WXb@u% FܿT}]E"N E%1Oۭ^Z'lyF!>A1Y9HxϔZ"V}FnH4*Iſw9%6`%7Y.osP/Bl3X4}b J&+5bGAW2X1 *˛:R?كAEt" ы"  ~04T7prbƲ"W> stream ?DlAMG Ц8^`D8ȧӽSrpb k9Fro8Y;4<}X{ 9[ ߙTUt&jjd_UՎwO _߼Ku8degytڗWK .*t2ht'35¥YFVvLs8'W ޫn> ŏxZ7VI> stream t+{D@<Xj|U!'e)Hk{݂4O!%Jӑk2U}֜`3/Q6ʖ񻊜TnE<6cɇ?fj\a1jx:3)j7f|q;xĂ>XoWاjf)fJk Z@ FۻϽm!6aG>LYdXedzڵ>TAB"nHaP#X1M8IA/Vpp 9lxcL:Xk)'/A͙,NZ"DPHѳf,km endstream endobj 410 0 obj 333 endobj 411 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 412 0 R >> stream ˘_|{oF@* 3:<mRqjH5_zpPh3Nf4.]mEl=? |j"Ԣ>V_ǣ[98PUJ݇f‡JY"*6sgmu}WD 4Y}zV hZ)J6lp&?r )=.mb/B|7*UOEQA6uЈ:.0Ճ'8]H>Q5Z,j@dDxVamR^[lJ SjvzB}Pgq/=>Ee~6發$[RqlβUR|yE&qXnD] endstream endobj 412 0 obj 380 endobj 413 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 414 0 R >> stream Z~1t^ g#BrL۵t4І@Enjxud[AG3z{vcOQc]1̑)UR+ڱRC$\{KMYS0F6qupYlS:(q2Q;1VOR|gE$w*6O}U.N_7= vLio0s\t$]VI{ Nj,8z|K6mH)%*&c%ZVfeo[.O Ё@ Xg*9V=*-"lQA))U( hk>O_JmnU[ot߶ <8*194Nc[ 4fF1#h?t-BHg1M cHgv[:iA5tmJ!3j[̰v?n1i!spPO TNy4 /Rq03> stream 993t*hny8=2/gv70/85aQ\LῙ;*MN?R 7}8W.PRH$Չg<.+ 78fc!> K:;DB?]p\ th X;MoVs}slf|f\rptFXwO dayAI+Ú+% IXQO6]La)HYּUh-u/:kG621a<[X7RaY|fN{Yr*Sg.2*|QB%rm,@="9t$C*AճX9h6TQ 3זxW endstream endobj 416 0 obj 522 endobj 417 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 418 0 R >> stream {'I:L]7g:^8@Qru#zmI˭FKy2H_L[K{a(x[N!27?oO0%v|s8Bht07u-ޭ޵jHu, %9*oq{E4[c'511֯`mxU1X}"f 7?^Zur ;*vG#'z8&W's[¥``|0+G%9_&sszF"Q(EpdXb>0D!R \p}+GNd!ol':|VKZ<ei endstream endobj 418 0 obj 462 endobj 419 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 420 0 R >> stream XqkFƊ hUB 9~J5LUkók%ap΁=>Pwv5ns DuRZc KŚFOZrGe8c)c詶xZJQ4+\.c<>. aJuo4Z+L t^DP#cZvԼJ~@BUc}ʀM݃ \F,b뼞nuvPb endstream endobj 420 0 obj 228 endobj 421 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 422 0 R >> stream 94f[Rm~ Tvq<Ɏ4u J'9\Evnx37栔J$Cٛ =э.Il 0YN Q?w\eB/1Uiӊkf/+x-zF`}0BaPg\*'! JQ60Q{CѬ<W9vMvʇg#Wu&`dDnG,֔'w$F_Q{* ֘ˍ4 sNk{u\ȑ ͉{ @wu5VU9 N3XT-q n@Q"g`nhZ] .Bp@cmxt4r 8TVzh$MTت5.E/||z- Qׂ~U,=Q^qkEvj#Մn/_Wٞ jԷ+@_L<8ך Fedi &6Aёf)CC endstream endobj 422 0 obj 533 endobj 423 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 424 0 R >> stream PS s+e |(^Dwƻ:gդ>&SIzyjܩR.tSj/?y˻˕t<h;!VZvkAy-Oj/΁a]b8eh7ro_Jkq! P<V*EjkIo:u!ꁏ{\ r7J$Ҳ<'Wimwbb㗣!j>^zj;@VAq3 A,]9 !S'< ZXz_sHmŚOuI¸i$.&W[_KpZpi0lqlI7YmnDe z<Jϝ?0PUU{æ; ‰>oJQs$Yc)!q^^G{O-w0hmByORYmZPD1{% endstream endobj 424 0 obj 478 endobj 425 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 426 0 R >> stream dKo <> PzwSl%A'qq"֜e>.L|0ZCB;/ԩfИܘ>=K0U"5@Cl>jB`8H&e-oj'OhA2Ity :됎u=kO>)iqN-hR4.4ΡD+@4y2>'zqł_M @׃}DT4>rʂRkT_e,=E|ߥj`:mk bEИTZӕL5}YUgHqeq?iŦEJdU/R79R#Ցr%tDYB<#I cόdkk2E|BRn^=(;H_ R BFy4pk#QR+ߔ280Rwn." endstream endobj 426 0 obj 456 endobj 427 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 428 0 R >> stream 5=P{=&lK9#.B- Z>8,tēT] o_i^ڹɘ,/(;"[pVl +nk)i@7 β?>I9YA~32`j;}=読Ae*h &e"č)ܘi/_SxSGS(v":fNcyA(ٯ8A!rUfN΁eMEAOJ/fTA(A:opyISFڣ3+T@ ,ڂ[I(z]Pu6[=dl(L6D*2@5:!ph 6N@ik ͘IW 53Q W? endstream endobj 428 0 obj 427 endobj 429 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 430 0 R >> stream /Š C s2S[P *"l$&mZ#4n*}/w,kp-ڼƳSѦ(6h lƖPP:!нޘnT3V f0˴gv,AAȅ12$*mڛ9GP|ycSg#۳Ls-~a]s n'kYH\ׁbZ{ i#%{dz)cTYҕƔC"z'b z-܂x0E `&ю|$fqN!x>8=YQ r}KI|a@bϕw-B5e endstream endobj 430 0 obj 529 endobj 431 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 432 0 R >> stream aDloi5%Kp(٠. 1: ;ǃ lJs6~?xV+ҟ<]BfҔ{;KU&O -f`֟p4k^ >y5!+j#9a#jʷ6 Q]Js"I^h0I хUzz;H " endstream endobj 432 0 obj 405 endobj 433 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 434 0 R >> stream ;1׏ڍRGcjN|[s9 Q2Z3G,{%7m" p5(t(%O?bM(2Cf|s ."BsIxBZ nj;#e`I7l#An`L3l"ڲ7¬/@y9(L[tk1/!<4# endstream endobj 434 0 obj 203 endobj 435 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 436 0 R >> stream hc B~DS7PI 8ѫ8b uHɒ[(,Ѳd3<#o#`oǬ\SePS$R1^#4XQuq.fSh~oYCO>a*j}*lSë؞lnS=Qxд9Eeխ>m67S"y,ݽAl +><^Z <|7Z֟bM׆YɓvVט:1Wa =Cn4M>ϟ:=E[fG -o~tV":VYΛhèa4$(qd]p> stream eT@4{&qw\m:scmHJw d<ǝÛ_U.o8Tw]rI#P8v|/\6|>oS{qMlrMC=*qz.`7K:wAJn}tT z"<[ XbW)؄5R(穱2=8ރoJu~V,06jR ( E |ͷ Μ[EE>!q"dq0kn4!z2_Gּ%;y*:/ÒFaS}a̢*: ޾&KrpDRw"EP?irCFy g:0Ǵ2?KX ޲㮡gL{6(}-oQ \}oS ,#| *`/\;ZI HŠ $.Ρ]D39]n.) +EOvG+m,kha RNJNv(/ endstream endobj 438 0 obj 533 endobj 439 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 440 0 R >> stream I^0bBWCz_.ǩJ7oc S3[ 6ODG%W^1{IEȥ:Im S%]gxaU.e0P= QVR"S $,ҘKj׭> stream 4ع@3/PX\;M}tAè2|is)\Ff-$<ߐ!$q=jacT? ȤFh6(,@zCaWdQD>ήu L\d /9> stream B5(MW .S0<\E٣wu]<'A)X&r],TvGlD@> -➍7"mQ+3'quh\k@ttLJnKthtATl$@~SЊ0Ǭ3F֛K犄30YKn|ψTL:qVϦb)czRi'Ykt`ۼ!Jt1ܽ]Fk endstream endobj 444 0 obj 235 endobj 445 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 446 0 R >> stream 3Y~O'8V4c(6aժ_Nx{`3`s!7H0U>Fm!.2\"02COEvhooS.=:y]<]prY 40{ڜFCxK#e_:[w,-TRIbf$ Tn3C#C 9<8 ’mi*jh4+N)$XqZQ#r~ V媡r.m+5QXmCІƷmQ<̜f=٠ ,ѿ)kQ洶?~F:'2{"zm,e/r=cVVn3D&1YzY3m^FliIؽ/#4}pb'?]tПt5t=$([ F5v/ endstream endobj 446 0 obj 469 endobj 447 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 448 0 R >> stream e|JY f [T "]}y |_{9tX/Js ׫@NDoHQ;I#,о75(l&K,"(Dž%||ͦ]!/Z0UrF" %vpf*m``Sۛ9^ hΖs $Sc!ɽVV(2ݪ1)ٞ49 3=NBS&Xaq{t,~4^<ɨG%Z=j[L\!5BM?LF# .y qgߊ endstream endobj 448 0 obj 315 endobj 449 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 450 0 R >> stream {K=ֈx8PKT\=K=_QiJI2AG .T!%g(e˝-1i yCI\/DAywصFzd2y'y8ر Y@XSO4ʦ&Qp.Di'λ#s4d}į&6cDwkE1U.=x>%ͮ[wzgg[0A\P՛SMS4u g{@y|\W>~N,|9Vmn&=Ojl֕2c @L٩H%ݺhl> stream [ngPlwn'jL虗Da!.hu 3xVmbpO. <]8A0[0^ HS[IUq0n5hdb/V .+Ȱ\Y;s ֔,2r DmL ̇LZj g]yۈ :Z{y}ta@YU P=覸¥纬@y[U^E* V>~z"Zu:tOKZ˓P *(> stream %K0H-7ehj BR ÞL`@9>&<V:jWf cKu.\ko"LdD& >Ԛ'-gќT\ pm=L1acX.^F AbpouqL Tͮ-oҮ(r .jS[T}zQӻ3˩xD*Ca(ЭϛU2 endstream endobj 454 0 obj 463 endobj 455 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 456 0 R >> stream ʶ~۳(}˳J>/ C܀x=8a{G$Ec'8!Bas@/?SլF?C_ ]3+-]6>q"T7ctCݹN 7F¶ 7J@O!ݽF<ޭ; 6Bz)TumYֳNsnzQWA]4ɪ=9Rs&fOWԓ;%7{q1,~mSܷ%X+/ɇY]Gq/m_A:HJA[7#0D-7> stream "G!@FN'e9%<w}|BD "V۽#o _E󞶩.CP-#nK2}4Q=s&2LVD%'?wڟ5a*ƋuA-OBbvM z pzK5SC,N:U'uN6,K9$Rcn$“K-Ci)<4 (rg*z\ɕ?LM?A<=FRL]AR6߶ z{.ǝ2e75ZEeHcD8/ Tm<=ō6=v gul:d)@?e좍*5As-RvS?. nT2;oR+e83x̃7dƘլf;|XN3ћWA͚6E"&L endstream endobj 458 0 obj 448 endobj 459 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 460 0 R >> stream bRp?]&8(lUUR X#ddyweC͊h-aw1#e.-9yE^ݞӣe.\CUb1-[4 ?h_ v217KϗT%Exe&zkS2#h)_Կ$ j?02Bwkxe:f斮 endstream endobj 460 0 obj 389 endobj 461 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 462 0 R >> stream xRhQZ?S#^[}=&+AUǬ@9PN%dz vs K>5-9]PM endstream endobj 462 0 obj 275 endobj 463 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 464 0 R >> stream F_ެl&CCOɡ[B :.%JxސX@Jt{ }$[.Wt`8c9Vgu?0NfPSu_:EQztͨ@MQkP w+$ X5% ֖Tغpjs$;쎭9*̦P&;acSNJaTdZ* E6,%@&%{! Ow&u 3B[`An”YsL617x64j&3iyY+(:U w;9B#*mTWwXs%Sw OA8 710MPxEÃx4cngԿx 0C2p 1&FeXN;EN|ef%,om tn$8;v>I})x ]ۜ!$ b>~Vc:6p=`@2: ̱jd\3xc cc:EdP)AD endstream endobj 464 0 obj 545 endobj 465 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 466 0 R >> stream #f@(|T;bk$T_L:f>DKX 5華d/8KF:wvhn*LQȍEfPre] 'aK LZ=3qpF\ 'r&;7XRj`sT|1]>=b:@Y/ Pp*tT&۵_ xRdrC MEE e06 qaȗNވpzf (JA$C߸ϏcGxONt y☡>b']2$ bt!f߾hh r]h"?` o;5z7wHP_Z1b`6HxG&AKܷV ꈔ屜4K9S/ Ef!>W_TKdhȞqN`[zIs[T6w1ty 'yW*fH\jpӶW#6'cĤn&_"|DaVjZt+dM' }Ll,!G endstream endobj 466 0 obj 575 endobj 467 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 468 0 R >> stream 2>$ m=WĨv*aKd ==h,̠<u6\Gs CE%;7Ju^?A $1 yϡCe~wT2謔ݺkɎ'$qVya5A2&4]OK2~iܛWY^+D endstream endobj 468 0 obj 379 endobj 469 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 470 0 R >> stream G:ߝEĈIl"hYC* V RYWphёxYk]o R+忂ަtHn.V%<'3ykOb7n%.e$yeOԒ ܛ|ľ2zP+lK錵}Ax-C(iՖ $@z2)3|{`Ps&^HKL *>xi@y^Yv_يdhK;9Mj{c e uRUsZ+p'ef [Mw pDo ' N̲7r|1E. @Q٬РE1Utgf2߿N. =Vyp!N\oqn6#Ek\H endstream endobj 470 0 obj 411 endobj 471 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 472 0 R >> stream WqmDG#7Ew2T4Z*D6iIӿ#dIQi*NqH-~%AX_f챔\J=A9(=W :wO ZD_0}Ǧ&Lh5+^XEnipsCf2}/mRGP^Iͭ={%(w iZ.6B.nG{!=Cm]ƽh@ Ş&$X  endstream endobj 472 0 obj 233 endobj 473 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 474 0 R >> stream m=(۲-Uct9 t}6"/DD7ҳXB6)^H=x]'n~f(i 1ʄSG;iIW{GEa9| Reo^k>?x7_^`le‧R!4NTP)C03y?[Ap/Q5m]O=-YrC@ZY/w'76 ݊k(IL4{HꝬvlm#a˭er0FkB\{ktS/rG TkWÐ/JWd 0+}s ?xH(rc2(7=ִܳ;*= X#k.4b&@9ۣ>w]|!ը ^];B8缴KjC\<| "IBU 2mpGCFOpn>?r&Qcj#JAr-`$?"آC`V铛_Gw a}-GPv >;o]'߬ endstream endobj 474 0 obj 548 endobj 475 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 476 0 R >> stream PbiL#?Fn%CJє8q}Ȩܔ |k,'V9|WIR]^i%/ j>H)lC^!n%f@C:G,?/Ak}i U٣PDE]DEi_8%I,C&9.W ^?S LILt]'sLs.r-xt(W.Fg5^:QWw(Xa@u-pdPx8~MbY dZM{\ahJ6೥qeNZ Uwm|Bu2 endstream endobj 476 0 obj 461 endobj 477 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 478 0 R >> stream *<zԥr$lnW&Au&*nSJ̿oe9V70P''ac?Y)5zS_6Km4͚+Jč9QB_bު- a}^վ/OE&0K3UeIFm}4V;Hi_233)] 7䁆, OȣA.F;O;+x^U*l/`e<m hKْ5^RJ [AY:.eE]%69iࡽr'V7(Yut/c>٢'+<6rOȾ 5yDǣ U\q;:I+ A}٩r%V`fIJ!;# VsG+~l'GL!ar­W#,1f> stream uL뭬1a"@ʟ;O\7Ĉ,.Ҩiy3PwRK=j0> stream "j9;o;-=͘iR0FzfM}4ݳ\$lvٳ#;͞:Q*^v:+_zd,I[Ww6~P!n<9&g3/:y@ Ӡv^s>RO6nh>,k> stream |qPnܸ;⋨*e8ƒA,Xcu$/Pe0a;}Gid?pv 3\̪RAEmL%ҁAh E`gA8 'LDB8Ʌ_/)1ǎ}^XFIm\`_9"LupL~-֜j:㷆z^;#ea!)M2`plDW'/8ŭ8ӈ/VtϷٽ-uO]rGv~f_'H/*t#>[9hU@} HY,E5Ey fm}]y4"er3 endstream endobj 484 0 obj 488 endobj 485 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 486 0 R >> stream 0L6' c./tP9_kŃU dbihty[b1k䶞Xqp~%=j`{B~MHڌ`9Sd"}3J tN47ںÝ䧆@.SA| * ؛TfXp >+s78w6̹+(5 9fM/N.kj'1zڌk 3IC˨rbMC'NvȗtBˣB5g(}uZtTG ɏHA&䣬[YE&2^'Vm;~.ֈۛnZS2u Q!/,K5ٓLa27Cӛ75W>CA.@Q^:UahAg~$mN1(wV4k,x ;,f'vSt8 endstream endobj 486 0 obj 432 endobj 487 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 488 0 R >> stream pbyhQaYzjdpb46+U1M{|}q25L_XѠ3~:N67/*Dh)[6|ʮ=J> stream Q:=P uTB\G{%< 峊}H,lb\e++bZp-Hi1`<*~y%g.y]Ph}`uH*_x{ ؜볹Gaev97U&(^h.l@wr  i-@@ctl9Ez$t>q5čĹw,ӐY# R2D;Jkad!U{UFJ.ɯl$9#l5Ob,$H$Ugn'W\-pZ < :5F{vbk \xZxz|jqd6{Wp !WRuGX48 j \vd({hjcsm>b-zyJ>Ȳ 4qDU{³ 4,']@2a-lkn5Q endstream endobj 490 0 obj 513 endobj 491 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 492 0 R >> stream ?p}2V{޶JN$&u>i?_ةЅ$UCj:ޱ #NrSƉ}k¾8! D[rd!/91\PAm /:*(16 N$RIXD}$OAE|n0Nw!$]֭a(L7Ú7'qAi2f .F#œ_yb0JޖrGrDFu\&Ϙϱ7YlN^Od9Sq=&؍ |Ga*Uo\6^5]> M3j˹Kc': }9 M읠}>9SwWޢ2 endstream endobj 492 0 obj 345 endobj 493 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 494 0 R >> stream s! C/OPP 8^u:ᨒ&PY?!كftCE웎4 Vm~LTͦ(\/Pn!J4J8B Q؉2agj~PA*=M/ڡ[q'v é$ݺ`JDWÄ\ _xA endstream endobj 494 0 obj 345 endobj 495 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 496 0 R >> stream Q+ϮVY ȑ( F4ja5@Vh>4 9VQSd|d+0@jNEILEB'*'fC`+Hf<ְf29p0Q@E8;#"xy'1cQNDggyヾC:K1^ZF;=lZbD2iRm>2e>27צq9;\=155&`;s5n s"N埦QBՊIHN93vh#w!F򮇂 xXL.!V >c;aъ`L2sΣ`ʂͫB{3.vy>AZG|ң\Ι x4Δ qȩ:sI͕=PF(׫(Іak\yYm *x@1S?ߊ4OB" ~Do*Γc(>1Zn$D9leN endstream endobj 496 0 obj 523 endobj 497 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 498 0 R >> stream UR&whoJz@],= D|5pJ ]4WR- f , XbUxer:.tE÷gsъ~ܳ-^2ֿLj1{7,oT~nrv0aBgӍ2ב]9O}{hKkc ؍Vy]EM8(7$F3?uCj \kcMW_Ԋl<8ι_:Im|s^pݕ, [Ul,U(y!#~ivg)2+F5o"]i4@ 9MŏG5CdNΛ͍T@l%B7? endstream endobj 498 0 obj 491 endobj 499 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 500 0 R >> stream yAvzG lP̜Q?o,82'r3,ZWe?:RԊuE8p>}SV^4bw77*Ahh2z9X>ɦ}59&]Ԩo9** ui M)z C^ 0$`IR&>(dLj4x{ Ðr(pݥJrNӷ+;pft],E:?T20U!+·ԁvͷ٦,9jb O x7i8]; 8ƺ,トz\d endstream endobj 500 0 obj 506 endobj 501 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 502 0 R >> stream CGxu8pw22Dtv .Z3<Ǩ)178Zg6Ij-cLczb&DU'&SM~6@EUVٵqFGQ`sv7cum" ef S 7 JNħ*d<.q/Dž~ԢJ~=Ğkvd+vmlĠEd[ Y,zG)v8 TӋ `&#+?X~=f[Sm endstream endobj 502 0 obj 294 endobj 503 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 504 0 R >> stream DB3jc9@`5TMiļjV*Q+#`^$ ڪKU&Ɏ4xsϕѽ` >!.HAE[|sս.rLfIJ l:O,бcϪqA3bQцXNƮܠE!.j=ql,0GA5y4j&6Zૌѧ#A$r bsK[m ?riwc_ԺT q{27H endstream endobj 504 0 obj 549 endobj 505 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 506 0 R >> stream ( =G3Kܨ,?gD: BxwjC]-gXוTk!7ժ(%aHdєDeb9}̕I_¥H61\+|)nzCHwˤ {s53L/+ڟ0z}t!DYtL` 5v|2tnDK_v0$hC?ļcs*N}9mk68!P$V!UrE}>ੇR2R w$.P:VN)!tpGw teՅ4ܨ @F0b6@HD#ꙧ\ѐH;TlK="<@Af9/)Jh~_qqsbȝqv\_ endstream endobj 506 0 obj 441 endobj 507 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 508 0 R >> stream ,vS 8R"Rą#vcGG;3`45~W-n9`Jk$<]ɾѳqHf(V2*gd4euͯl RiRoJJKB^ZnH *fkiLRPAA&O048,Ixo5핚n&.Jq!<`>xzqݟeh CwWaaCT&]X.,9^[jA@,\iv}PbK[ 6޻coR@g /޷8sǟrRwN`r6X"|6!t1g o U+7N˨1+Hnzā Wqܢ5w;oѷ >+jM 3HmU\17R2ϓ} endstream endobj 508 0 obj 427 endobj 509 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 510 0 R >> stream .]d8ǒe*N X!2Ksz bJX-!-l]h$C#Ku*q/jw{Nʅ+`鸌ͬ'q(HΞNBu?CJ6\-Mnk#CyRH07l:1 0|.B]H,XT횲)& 5:ETwӏSwn+fsQ.A  a oy)畕g04r2*9Q&M]qn.ceF 0}R,nnr6>Qi0731nͯﲤ$Bi[N5G5w^^G1@ƁgGEN)R:vGgc]jA_$V%(sɦYퟫ?tE[F~[-W+g/= endstream endobj 510 0 obj 486 endobj 511 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 512 0 R >> stream Կb: GH׵`@+4 i,hQw,wi {Q6eSvŬ"g:Y'`Ǐ0LV} YY,/^l֕ʅ #H%H ︚c|eP*>btYD|exvR&'sܘDP&pZT.nn\Az:t'y dLy&ϋ%2]z4N/I;t@ QAk g&n> stream F 1L ^L5p/2C# C_ [S5jyW3Z&?TB% OFJ*J|ujs_U ske\=5$72R4b75M8Y]yjR֣i@-YO4urzQl0jX~%> (e_]t@dW ?/WZn5CpR&d1\_kqBd+-R endstream endobj 514 0 obj 279 endobj 515 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 516 0 R >> stream BUwts 86 %YPHa%9F(d` mN4!sNsFv?UG*9G^͒1]Sꐭ&sl֯jr牢(vR|[~0:iIZ9:ziёN bs1򲿄UNk<ڱj.㛳: 23uR 3#^hKLR4jh)# ڌ@m:uCprAy[! r r)E`{ Z71z;jd;z y1׺ "|&+3 endstream endobj 516 0 obj 349 endobj 517 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 518 0 R >> stream ΢z&x$嵀>#1^_#wWQ7)!rX\o8 4q:H qZww2Yn;͸2}STϞGbaIw@_ rW]_A zI~߇֎mK9)Xz{ՙ±Yd*guGӝ}zZRe+%*Ș[>p2;by! "|/@j V_p;p3ZRG}FʰC0QnL)GdJ*aܩOj )Eg1ә/ˁ@ӗϤ;*(Nu1!9lk-ufricZ`1 @ ydEgCWiz?z wǐj;H@8NaiB/8o+h*ٹot72 endstream endobj 518 0 obj 473 endobj 519 0 obj << /Filter [ /ASCII85Decode /FlateDecode ] /Width 74 /Height 105 /ColorSpace 523 0 R /BitsPerComponent 8 /Length 520 0 R >> stream EC&bW21`B۩3 ׮k|iN,ruQ B1 F0%nuk{7I!jb\#Y7m!WHQ>Qcd6kfM E=њ{Nw%#[ jF5S> stream ~ pp JJ|%_ƷVو XT6vEA|ǧb> endobj 525 0 obj << /Nums [ 0 524 0 R ] >> endobj 526 0 obj << /Type /Pages /Kids [ 539 0 R 1 0 R 4 0 R 61 0 R 73 0 R 76 0 R 79 0 R 82 0 R 85 0 R 88 0 R ] /Count 10 /Parent 527 0 R >> endobj 527 0 obj << /Type /Pages /Kids [ 526 0 R 528 0 R 529 0 R 530 0 R 531 0 R 532 0 R 533 0 R 534 0 R ] /Count 71 >> endobj 528 0 obj << /Type /Pages /Kids [ 91 0 R 94 0 R 97 0 R 100 0 R 103 0 R 106 0 R 109 0 R 114 0 R 119 0 R 122 0 R ] /Count 10 /Parent 527 0 R >> endobj 529 0 obj << /Type /Pages /Kids [ 125 0 R 128 0 R 133 0 R 136 0 R 139 0 R 142 0 R 145 0 R 148 0 R 151 0 R 154 0 R ] /Count 10 /Parent 527 0 R >> endobj 530 0 obj << /Type /Pages /Kids [ 157 0 R 160 0 R 163 0 R 166 0 R 169 0 R 172 0 R 177 0 R 182 0 R 185 0 R 190 0 R ] /Count 10 /Parent 527 0 R >> endobj 531 0 obj << /Type /Pages /Kids [ 193 0 R 196 0 R 200 0 R 205 0 R 210 0 R 213 0 R 218 0 R 221 0 R 224 0 R 227 0 R ] /Count 10 /Parent 527 0 R >> endobj 532 0 obj << /Type /Pages /Kids [ 230 0 R 233 0 R 236 0 R 239 0 R 242 0 R 245 0 R 248 0 R 251 0 R 254 0 R 257 0 R ] /Count 10 /Parent 527 0 R >> endobj 533 0 obj << /Type /Pages /Kids [ 260 0 R 263 0 R 266 0 R 269 0 R 272 0 R 275 0 R 278 0 R 281 0 R 284 0 R 287 0 R ] /Count 10 /Parent 527 0 R >> endobj 534 0 obj << /Type /Pages /Kids [ 290 0 R ] /Count 1 /Parent 527 0 R >> endobj 535 0 obj << /CreationDate (+{2F) /Producer (LooBt^%^yV\\\(ĈpZ) /ModDate (+{2J|NpS) >> endobj xref 0 536 0000000000 65535 f 0000107843 00000 n 0000108011 00000 n 0000108187 00000 n 0000108730 00000 n 0000109291 00000 n 0000109448 00000 n 0000109606 00000 n 0000109764 00000 n 0000109922 00000 n 0000110080 00000 n 0000110239 00000 n 0000110398 00000 n 0000110557 00000 n 0000110716 00000 n 0000110874 00000 n 0000111033 00000 n 0000111192 00000 n 0000111351 00000 n 0000111510 00000 n 0000111669 00000 n 0000111828 00000 n 0000111987 00000 n 0000112146 00000 n 0000112305 00000 n 0000112464 00000 n 0000112624 00000 n 0000112784 00000 n 0000112944 00000 n 0000113104 00000 n 0000113264 00000 n 0000113424 00000 n 0000113584 00000 n 0000113744 00000 n 0000113904 00000 n 0000114064 00000 n 0000114224 00000 n 0000114384 00000 n 0000114544 00000 n 0000114704 00000 n 0000114864 00000 n 0000115024 00000 n 0000115184 00000 n 0000115344 00000 n 0000115504 00000 n 0000115664 00000 n 0000115824 00000 n 0000115984 00000 n 0000116144 00000 n 0000116304 00000 n 0000116464 00000 n 0000116624 00000 n 0000116784 00000 n 0000116944 00000 n 0000117104 00000 n 0000117264 00000 n 0000117424 00000 n 0000117583 00000 n 0000117741 00000 n 0000117899 00000 n 0000118089 00000 n 0000121284 00000 n 0000121531 00000 n 0000121690 00000 n 0000121850 00000 n 0000122009 00000 n 0000122168 00000 n 0000122328 00000 n 0000122488 00000 n 0000122648 00000 n 0000122808 00000 n 0000122967 00000 n 0000123157 00000 n 0000124429 00000 n 0000124600 00000 n 0000124816 00000 n 0000127915 00000 n 0000128086 00000 n 0000128288 00000 n 0000131139 00000 n 0000131310 00000 n 0000131512 00000 n 0000133362 00000 n 0000133533 00000 n 0000133749 00000 n 0000135971 00000 n 0000136142 00000 n 0000136347 00000 n 0000139459 00000 n 0000139630 00000 n 0000139835 00000 n 0000143689 00000 n 0000143860 00000 n 0000144050 00000 n 0000147262 00000 n 0000147433 00000 n 0000147623 00000 n 0000150651 00000 n 0000150822 00000 n 0000150999 00000 n 0000151991 00000 n 0000152165 00000 n 0000152382 00000 n 0000156339 00000 n 0000156513 00000 n 0000156717 00000 n 0000158560 00000 n 0000158734 00000 n 0000158925 00000 n 0000160841 00000 n 0000161044 00000 n 0000161204 00000 n 0000161364 00000 n 0000161583 00000 n 0000164374 00000 n 0000164577 00000 n 0000164737 00000 n 0000164897 00000 n 0000165116 00000 n 0000170238 00000 n 0000170412 00000 n 0000170616 00000 n 0000175530 00000 n 0000175704 00000 n 0000175908 00000 n 0000180369 00000 n 0000180543 00000 n 0000180747 00000 n 0000182119 00000 n 0000182322 00000 n 0000182482 00000 n 0000182642 00000 n 0000182861 00000 n 0000187757 00000 n 0000187931 00000 n 0000188135 00000 n 0000193154 00000 n 0000193328 00000 n 0000193532 00000 n 0000198970 00000 n 0000199144 00000 n 0000199348 00000 n 0000203738 00000 n 0000203912 00000 n 0000204131 00000 n 0000208764 00000 n 0000208938 00000 n 0000209142 00000 n 0000214597 00000 n 0000214771 00000 n 0000214962 00000 n 0000216419 00000 n 0000216593 00000 n 0000216812 00000 n 0000219831 00000 n 0000220005 00000 n 0000220224 00000 n 0000224719 00000 n 0000224893 00000 n 0000225097 00000 n 0000228350 00000 n 0000228524 00000 n 0000228728 00000 n 0000231894 00000 n 0000232068 00000 n 0000232272 00000 n 0000233639 00000 n 0000233813 00000 n 0000234032 00000 n 0000237224 00000 n 0000237398 00000 n 0000237617 00000 n 0000239834 00000 n 0000240037 00000 n 0000240197 00000 n 0000240357 00000 n 0000240576 00000 n 0000243160 00000 n 0000243363 00000 n 0000243523 00000 n 0000243683 00000 n 0000243902 00000 n 0000247273 00000 n 0000247447 00000 n 0000247651 00000 n 0000250877 00000 n 0000251080 00000 n 0000251240 00000 n 0000251400 00000 n 0000251619 00000 n 0000254417 00000 n 0000254591 00000 n 0000254810 00000 n 0000258967 00000 n 0000259141 00000 n 0000259345 00000 n 0000263694 00000 n 0000263889 00000 n 0000264049 00000 n 0000264253 00000 n 0000266426 00000 n 0000266629 00000 n 0000266790 00000 n 0000266951 00000 n 0000267155 00000 n 0000271293 00000 n 0000271496 00000 n 0000271657 00000 n 0000271818 00000 n 0000272037 00000 n 0000275465 00000 n 0000275639 00000 n 0000275830 00000 n 0000282794 00000 n 0000282997 00000 n 0000283158 00000 n 0000283319 00000 n 0000283525 00000 n 0000287588 00000 n 0000287762 00000 n 0000287966 00000 n 0000289122 00000 n 0000289296 00000 n 0000289515 00000 n 0000294838 00000 n 0000295012 00000 n 0000295216 00000 n 0000303631 00000 n 0000303805 00000 n 0000304034 00000 n 0000307021 00000 n 0000307195 00000 n 0000307399 00000 n 0000308753 00000 n 0000308927 00000 n 0000309131 00000 n 0000310192 00000 n 0000310366 00000 n 0000310570 00000 n 0000316049 00000 n 0000316223 00000 n 0000316427 00000 n 0000321914 00000 n 0000322088 00000 n 0000322292 00000 n 0000327945 00000 n 0000328119 00000 n 0000328323 00000 n 0000334198 00000 n 0000334372 00000 n 0000334563 00000 n 0000338879 00000 n 0000339053 00000 n 0000339257 00000 n 0000345982 00000 n 0000346156 00000 n 0000346360 00000 n 0000351789 00000 n 0000351963 00000 n 0000352167 00000 n 0000356906 00000 n 0000357080 00000 n 0000357284 00000 n 0000362616 00000 n 0000362790 00000 n 0000362994 00000 n 0000365384 00000 n 0000365558 00000 n 0000365762 00000 n 0000370893 00000 n 0000371067 00000 n 0000371271 00000 n 0000376605 00000 n 0000376779 00000 n 0000376983 00000 n 0000382614 00000 n 0000382788 00000 n 0000382992 00000 n 0000388402 00000 n 0000388576 00000 n 0000388780 00000 n 0000394852 00000 n 0000395026 00000 n 0000395217 00000 n 0000397801 00000 n 0000397975 00000 n 0000398179 00000 n 0000404953 00000 n 0000405127 00000 n 0000405331 00000 n 0000410289 00000 n 0000410463 00000 n 0000410667 00000 n 0000415340 00000 n 0000415505 00000 n 0000415744 00000 n 0000415919 00000 n 0000416083 00000 n 0000416574 00000 n 0000417050 00000 n 0000417242 00000 n 0000417727 00000 n 0000417943 00000 n 0000418242 00000 n 0000418431 00000 n 0000418672 00000 n 0000418887 00000 n 0000422334 00000 n 0000422549 00000 n 0000451526 00000 n 0000451748 00000 n 0000472965 00000 n 0000473032 00000 n 0000473104 00000 n 0000473171 00000 n 0000473238 00000 n 0000473383 00000 n 0000473482 00000 n 0000473623 00000 n 0000473784 00000 n 0000473932 00000 n 0000474095 00000 n 0000474234 00000 n 0000474383 00000 n 0000474504 00000 n 0000474674 00000 n 0000474840 00000 n 0000474929 00000 n 0000475097 00000 n 0000475268 00000 n 0000475420 00000 n 0000475604 00000 n 0000475717 00000 n 0000475838 00000 n 0000476021 00000 n 0000476135 00000 n 0000476256 00000 n 0000476391 00000 n 0000476559 00000 n 0000476681 00000 n 0000476859 00000 n 0000476966 00000 n 0000477065 00000 n 0000477195 00000 n 0000477322 00000 n 0000477453 00000 n 0000477620 00000 n 0000477783 00000 n 0000477961 00000 n 0000478091 00000 n 0000478216 00000 n 0000478356 00000 n 0000478485 00000 n 0000478600 00000 n 0000478723 00000 n 0000478868 00000 n 0000478998 00000 n 0000479135 00000 n 0000479276 00000 n 0000479375 00000 n 0000479506 00000 n 0000479632 00000 n 0000479763 00000 n 0000479883 00000 n 0000480011 00000 n 0000480203 00000 n 0000480352 00000 n 0000480448 00000 n 0000480569 00000 n 0000480677 00000 n 0000480780 00000 n 0000480908 00000 n 0000481036 00000 n 0000481157 00000 n 0000481287 00000 n 0000481422 00000 n 0000481538 00000 n 0000481686 00000 n 0000481814 00000 n 0000481937 00000 n 0000482295 00000 n 0000482317 00000 n 0000482606 00000 n 0000482628 00000 n 0000483359 00000 n 0000483381 00000 n 0000483762 00000 n 0000483784 00000 n 0000484481 00000 n 0000484503 00000 n 0000485191 00000 n 0000485213 00000 n 0000485675 00000 n 0000485697 00000 n 0000486285 00000 n 0000486307 00000 n 0000487057 00000 n 0000487079 00000 n 0000487998 00000 n 0000488020 00000 n 0000488754 00000 n 0000488776 00000 n 0000489500 00000 n 0000489522 00000 n 0000489888 00000 n 0000489910 00000 n 0000490622 00000 n 0000490644 00000 n 0000491077 00000 n 0000491099 00000 n 0000491594 00000 n 0000491616 00000 n 0000492158 00000 n 0000492180 00000 n 0000492902 00000 n 0000492924 00000 n 0000493608 00000 n 0000493630 00000 n 0000494254 00000 n 0000494276 00000 n 0000494666 00000 n 0000494688 00000 n 0000495383 00000 n 0000495405 00000 n 0000496045 00000 n 0000496067 00000 n 0000496685 00000 n 0000496707 00000 n 0000497296 00000 n 0000497318 00000 n 0000498009 00000 n 0000498031 00000 n 0000498598 00000 n 0000498620 00000 n 0000498985 00000 n 0000499007 00000 n 0000499569 00000 n 0000499591 00000 n 0000500286 00000 n 0000500308 00000 n 0000500959 00000 n 0000500981 00000 n 0000501494 00000 n 0000501516 00000 n 0000501913 00000 n 0000501935 00000 n 0000502566 00000 n 0000502588 00000 n 0000503065 00000 n 0000503087 00000 n 0000503583 00000 n 0000503605 00000 n 0000504213 00000 n 0000504235 00000 n 0000504860 00000 n 0000504882 00000 n 0000505430 00000 n 0000505452 00000 n 0000506062 00000 n 0000506084 00000 n 0000506635 00000 n 0000506657 00000 n 0000507094 00000 n 0000507116 00000 n 0000507823 00000 n 0000507845 00000 n 0000508582 00000 n 0000508604 00000 n 0000509145 00000 n 0000509167 00000 n 0000509740 00000 n 0000509762 00000 n 0000510157 00000 n 0000510179 00000 n 0000510889 00000 n 0000510911 00000 n 0000511534 00000 n 0000511556 00000 n 0000512240 00000 n 0000512262 00000 n 0000512683 00000 n 0000512705 00000 n 0000513085 00000 n 0000513107 00000 n 0000513757 00000 n 0000513779 00000 n 0000514373 00000 n 0000514395 00000 n 0000515035 00000 n 0000515057 00000 n 0000515732 00000 n 0000515754 00000 n 0000516261 00000 n 0000516283 00000 n 0000516790 00000 n 0000516812 00000 n 0000517497 00000 n 0000517519 00000 n 0000518172 00000 n 0000518194 00000 n 0000518862 00000 n 0000518884 00000 n 0000519340 00000 n 0000519362 00000 n 0000520073 00000 n 0000520095 00000 n 0000520698 00000 n 0000520720 00000 n 0000521309 00000 n 0000521331 00000 n 0000521979 00000 n 0000522001 00000 n 0000522632 00000 n 0000522654 00000 n 0000523095 00000 n 0000523117 00000 n 0000523628 00000 n 0000523650 00000 n 0000524285 00000 n 0000524307 00000 n 0000524892 00000 n 0000524914 00000 n 0000524936 00000 n 0000525514 00000 n 0000525569 00000 n 0000525601 00000 n 0000525647 00000 n 0000525795 00000 n 0000525920 00000 n 0000526076 00000 n 0000526235 00000 n 0000526394 00000 n 0000526553 00000 n 0000526712 00000 n 0000526871 00000 n 0000526956 00000 n trailer << /Size 536 /ID[<4ef7e63bfd6b8619637083eb9af585c7><4ef7e63bfd6b8619637083eb9af585c7>] >> startxref 173 %%EOF minc-tools-2.3.00+dfsg/conversion/dcm2mnc/acr_element_defs.h0000644000175000000620000002347112574624760022771 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_element_defs.h @DESCRIPTION: Element definitions for DICOM @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ /* Define standard UID's */ #define FAVORITE_ABSTRACT_SYNTAX ACR_MR_IMAGE_STORAGE_UID #define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" #define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" #define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" #define ACR_APPLICATION_CONTEXT_UID "1.2.840.10008.3.1.1.1" #define ACR_IMAGE_GID 0x7fe0 #define ACR_IMAGE_EID 0x0010 /* Define acr-nema constants */ #define ACR_MODALITY_MR "MR" #define ACR_MODALITY_PT "PT" /* Defined values for pixel representation (0028,0103) */ #define ACR_PIXEL_REP_UNSIGNED 0 /* Unsigned magnitude */ #define ACR_PIXEL_REP_SIGNED 1 /* 2's complement signed */ /* Element id's for DICOM */ GLOBAL_ELEMENT(ACR_Affected_SOP_class_UID , 0x0000, 0x0002, UI); GLOBAL_ELEMENT(ACR_Command , 0x0000, 0x0100, US); GLOBAL_ELEMENT(ACR_Message_id , 0x0000, 0x0110, US); GLOBAL_ELEMENT(ACR_Message_id_brt , 0x0000, 0x0120, US); GLOBAL_ELEMENT(ACR_Priority , 0x0000, 0x0700, US); GLOBAL_ELEMENT(ACR_Dataset_type , 0x0000, 0x0800, US); GLOBAL_ELEMENT(ACR_Status , 0x0000, 0x0900, US); GLOBAL_ELEMENT(ACR_Affected_SOP_instance_UID , 0x0000, 0x1000, UI); GLOBAL_ELEMENT(ACR_Move_originator_AE_title , 0x0000, 0x1031, AE); GLOBAL_ELEMENT(ACR_Image_type , 0x0008, 0x0008, CS); GLOBAL_ELEMENT(ACR_Instance_creation_date, 0x0008, 0x0013, DA); GLOBAL_ELEMENT(ACR_Instance_creation_time, 0x0008, 0x0013, TM); GLOBAL_ELEMENT(ACR_SOP_Class_UID , 0x0008, 0x0016, UI); GLOBAL_ELEMENT(ACR_Study_date , 0x0008, 0x0020, DA); GLOBAL_ELEMENT(ACR_Series_date , 0x0008, 0x0021, DA); GLOBAL_ELEMENT(ACR_Acquisition_date , 0x0008, 0x0022, DA); GLOBAL_ELEMENT(ACR_Acquisition_datetime , 0x0008, 0x002a, DT); GLOBAL_ELEMENT(ACR_Study_time , 0x0008, 0x0030, TM); GLOBAL_ELEMENT(ACR_Series_time , 0x0008, 0x0031, TM); GLOBAL_ELEMENT(ACR_Acquisition_time , 0x0008, 0x0032, TM); GLOBAL_ELEMENT(ACR_Image_time , 0x0008, 0x0033, TM); GLOBAL_ELEMENT(ACR_Modality , 0x0008, 0x0060, CS); GLOBAL_ELEMENT(ACR_Manufacturer , 0x0008, 0x0070, LO); GLOBAL_ELEMENT(ACR_Institution_id , 0x0008, 0x0080, LO); GLOBAL_ELEMENT(ACR_Referring_physician , 0x0008, 0x0090, PN); GLOBAL_ELEMENT(ACR_Station_id , 0x0008, 0x1010, SH); GLOBAL_ELEMENT(ACR_Procedure_description , 0x0008, 0x1030, LO); GLOBAL_ELEMENT(ACR_Series_description , 0x0008, 0x103E, LO); GLOBAL_ELEMENT(ACR_Performing_physician , 0x0008, 0x1050, PN); GLOBAL_ELEMENT(ACR_Operators_name , 0x0008, 0x1070, PN); GLOBAL_ELEMENT(ACR_Manufacturer_model , 0x0008, 0x1090, LO); /* See C.8.12.3.1.6 for full list of possible values, but for DTI * images this field _should_ equal "DIFFUSION" */ GLOBAL_ELEMENT(ACR_Acquisition_contrast , 0x0008, 0x9209, CS); GLOBAL_ELEMENT(ACR_Patient_name , 0x0010, 0x0010, PN); GLOBAL_ELEMENT(ACR_Patient_identification, 0x0010, 0x0020, LO); GLOBAL_ELEMENT(ACR_Patient_birth_date , 0x0010, 0x0030, DA); GLOBAL_ELEMENT(ACR_Patient_sex , 0x0010, 0x0040, CS); GLOBAL_ELEMENT(ACR_Patient_age , 0x0010, 0x1010, AS); GLOBAL_ELEMENT(ACR_Patient_weight , 0x0010, 0x1030, DS); GLOBAL_ELEMENT(ACR_Scanning_sequence , 0x0018, 0x0020, CS); GLOBAL_ELEMENT(ACR_Sequence_variant , 0x0018, 0x0021, CS); GLOBAL_ELEMENT(ACR_MR_acquisition_type , 0x0018, 0x0023, CS); GLOBAL_ELEMENT(ACR_Sequence_name , 0x0018, 0x0024, CS); GLOBAL_ELEMENT(ACR_Slice_thickness , 0x0018, 0x0050, DS); GLOBAL_ELEMENT(ACR_Repetition_time , 0x0018, 0x0080, DS); GLOBAL_ELEMENT(ACR_Echo_time , 0x0018, 0x0081, DS); GLOBAL_ELEMENT(ACR_Inversion_time , 0x0018, 0x0082, DS); GLOBAL_ELEMENT(ACR_Nr_of_averages , 0x0018, 0x0083, DS); GLOBAL_ELEMENT(ACR_Imaging_frequency , 0x0018, 0x0084, DS); GLOBAL_ELEMENT(ACR_Imaged_nucleus , 0x0018, 0x0085, SH); GLOBAL_ELEMENT(ACR_Echo_number , 0x0018, 0x0086, IS); GLOBAL_ELEMENT(ACR_Magnetic_field_strength,0x0018, 0x0087, DS); GLOBAL_ELEMENT(ACR_Spacing_between_slices, 0x0018, 0x0088, DS); GLOBAL_ELEMENT(ACR_Number_of_phase_encoding_steps, 0x0018, 0x0089, IS); GLOBAL_ELEMENT(ACR_Echo_train_length , 0x0018, 0x0091, IS); GLOBAL_ELEMENT(ACR_Percent_sampling , 0x0018, 0x0093, DS); GLOBAL_ELEMENT(ACR_Percent_phase_field_of_view, 0x0018, 0x0094, DS); GLOBAL_ELEMENT(ACR_Pixel_bandwidth , 0x0018, 0x0095, DS); GLOBAL_ELEMENT(ACR_Device_serial_number , 0x0018, 0x1000, LO); GLOBAL_ELEMENT(ACR_Software_versions , 0x0018, 0x1020, LO); GLOBAL_ELEMENT(ACR_Protocol_name , 0x0018, 0x1030, LO); GLOBAL_ELEMENT(ACR_Trigger_time , 0x0018, 0x1060, DS); GLOBAL_ELEMENT(ACR_Cardiac_number_of_images , 0x0018, 0x1090, IS); /*added ilana*/ GLOBAL_ELEMENT(ACR_Calibration_date , 0x0018, 0x1200, DA); GLOBAL_ELEMENT(ACR_Calibration_time , 0x0018, 0x1201, TM); /*added ilana*/ GLOBAL_ELEMENT(ACR_Actual_frame_duration , 0x0018, 0x1242, IS); GLOBAL_ELEMENT(ACR_Receive_coil_name , 0x0018, 0x1250, SH); GLOBAL_ELEMENT(ACR_Transmit_coil_name , 0x0018, 0x1251, SH); GLOBAL_ELEMENT(ACR_Acquisition_matrix , 0x0018, 0x1310, US); GLOBAL_ELEMENT(ACR_Phase_encoding_direction, 0x0018, 0x1312, CS); GLOBAL_ELEMENT(ACR_Flip_angle , 0x0018, 0x1314, DS); GLOBAL_ELEMENT(ACR_SAR , 0x0018, 0x1316, DS); GLOBAL_ELEMENT(ACR_Acq_comments , 0x0018, 0x4000, LT); GLOBAL_ELEMENT(ACR_Patient_position , 0x0018, 0x5100, CS); /* Supplement 49 stuff, section C.8.12.5.9 pp 94-95. */ GLOBAL_ELEMENT(ACR_Diffusion_directionality, 0x0018, 0x9075, CS); GLOBAL_ELEMENT(ACR_Diffusion_gradient_direction_seq, 0x0018, 0x9076, SQ); GLOBAL_ELEMENT(ACR_Diffusion_b_value , 0x0018, 0x9087, FD); GLOBAL_ELEMENT(ACR_Diffusion_gradient_orientation, 0x0018, 0x9089, FD); GLOBAL_ELEMENT(ACR_MR_Diffusion_seq , 0x0018, 0x9117, SQ); GLOBAL_ELEMENT(ACR_Diffusion_anisotropy_type, 0x0018, 0x9147, CS); GLOBAL_ELEMENT(ACR_Series_instance_UID , 0x0020, 0x000E, UI); GLOBAL_ELEMENT(ACR_Study , 0x0020, 0x0010, SH); GLOBAL_ELEMENT(ACR_Series , 0x0020, 0x0011, IS); GLOBAL_ELEMENT(ACR_Acquisition , 0x0020, 0x0012, IS); GLOBAL_ELEMENT(ACR_Image , 0x0020, 0x0013, IS); GLOBAL_ELEMENT(ACR_Image_position_patient_old, 0x0020, 0x0030, DS); GLOBAL_ELEMENT(ACR_Image_position_patient, 0x0020, 0x0032, DS); GLOBAL_ELEMENT(ACR_Image_orientation_patient_old, 0x0020, 0x0035, DS); GLOBAL_ELEMENT(ACR_Image_orientation_patient, 0x0020, 0x0037, DS); GLOBAL_ELEMENT(ACR_Temporal_position_identifier, 0x0020, 0x0100, IS); GLOBAL_ELEMENT(ACR_Number_of_temporal_positions, 0x0020, 0x0105, IS); GLOBAL_ELEMENT(ACR_Acquisitions_in_series, 0x0020, 0x1001, IS); GLOBAL_ELEMENT(ACR_Images_in_acquisition, 0x0020, 0x1002, IS); GLOBAL_ELEMENT(ACR_Slice_location, 0x0020, 0x1041, DS); GLOBAL_ELEMENT(ACR_Image_comments, 0x0020, 0x4000, DS); GLOBAL_ELEMENT(ACR_Number_of_frames , 0x0028, 0x0008, IS); GLOBAL_ELEMENT(ACR_Frame_increment_ptr , 0x0028, 0x0009, AT); GLOBAL_ELEMENT(ACR_Rows , 0x0028, 0x0010, US); GLOBAL_ELEMENT(ACR_Columns , 0x0028, 0x0011, US); GLOBAL_ELEMENT(ACR_Pixel_size , 0x0028, 0x0030, DS); GLOBAL_ELEMENT(ACR_Bits_allocated , 0x0028, 0x0100, US); GLOBAL_ELEMENT(ACR_Bits_stored , 0x0028, 0x0101, US); GLOBAL_ELEMENT(ACR_High_bit , 0x0028, 0x0102, US); GLOBAL_ELEMENT(ACR_Pixel_representation , 0x0028, 0x0103, US); GLOBAL_ELEMENT(ACR_Smallest_pixel_value , 0x0028, 0x0106, US); GLOBAL_ELEMENT(ACR_Largest_pixel_value , 0x0028, 0x0107, US); GLOBAL_ELEMENT(ACR_Image_location , 0x0028, 0x0200, US); GLOBAL_ELEMENT(ACR_Window_centre , 0x0028, 0x1050, DS); GLOBAL_ELEMENT(ACR_Window_width , 0x0028, 0x1051, DS); GLOBAL_ELEMENT(ACR_Rescale_intercept , 0x0028, 0x1052, DS); GLOBAL_ELEMENT(ACR_Rescale_slope , 0x0028, 0x1053, DS); GLOBAL_ELEMENT(ACR_Detector_information_seq, 0x0054, 0x0022, SQ); GLOBAL_ELEMENT(ACR_Number_of_slices , 0x0054, 0x0081, US); GLOBAL_ELEMENT(ACR_Number_of_time_slices , 0x0054, 0x0101, US); GLOBAL_ELEMENT(ACR_Units , 0x0054, 0x1001, CS); GLOBAL_ELEMENT(ACR_Frame_reference_time , 0x0054, 0x1300, DS); GLOBAL_ELEMENT(ACR_PET_Image_index , 0x0054, 0x1330, US); GLOBAL_ELEMENT(ACR_Shared_func_groups_seq, 0x5200, 0x9229, SQ); GLOBAL_ELEMENT(ACR_Plane_orientation_seq , 0x0020, 0x9116, SQ); GLOBAL_ELEMENT(ACR_Pixel_measurement_seq , 0x0020, 0x9118, SQ); GLOBAL_ELEMENT(ACR_Perframe_func_groups_seq, 0x5200, 0x9230, SQ); GLOBAL_ELEMENT(ACR_Plane_position_seq , 0x0020, 0x9113, SQ); GLOBAL_ELEMENT(ACR_Pixel_data , ACR_IMAGE_GID, ACR_IMAGE_EID, OW); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dicom_read.h0000644000175000000620000000134312574624760021572 0ustar stevestaffextern void get_dicom_image_data(Acr_Group group_list, Image_Data *image); extern void parse_dicom_groups(Acr_Group group_list, Data_Object_Info *di_ptr); extern void get_file_info(Acr_Group group_list, File_Info *file_info, General_Info *general_info, const char *file_name); #define DICOM_POSITION_LOCAL 2 #define DICOM_POSITION_GLOBAL 1 #define DICOM_POSITION_NONE 0 extern int dicom_read_position(Acr_Group group_list, int n, double position[3]); extern int dicom_read_orientation(Acr_Group group_list, double orientation[6]); extern int dicom_read_pixel_size(Acr_Group group_list, double pixel_size[2]); extern void convert_dicom_coordinate(double coord[]); extern int is_numaris3(Acr_Group group_list); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/minc_file.h0000644000175000000620000000107212574624760021430 0ustar stevestaffextern int create_minc_file(const char *minc_file, int clobber, General_Info *general_info, const char *file_prefix, const char **output_file_name, Loop_Type loop_type); extern void setup_minc_variables(int mincid, General_Info *general_info, Loop_Type loop_type); extern void save_minc_image(int icvid, General_Info *general_info, File_Info *file_info, Image_Data *image); extern void close_minc_file(int icvid); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/siemens_to_dicom.h0000644000175000000620000000020312574624760023016 0ustar stevestaffextern Acr_Group siemens_to_dicom(const char *filename, int max_group); extern void update_coordinate_info(Acr_Group group_list); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dcm2mnc.h0000644000175000000620000002166212574624760021035 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dcm2mnc.h @DESCRIPTION: Header file that includes things needed for dicomserver. @METHOD : @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dcm2mnc.h,v $ * Revision 1.15 2007-05-30 15:17:34 ilana * fix so that diffusion images all written into 1 4d volume, gradient directions and bvalues are written to mincheader, some fixes for TIM diffusion images * * Revision 1.14 2005/11/11 18:42:54 bert * Latest fixes to dcm2mnc * * Revision 1.13 2005/08/26 21:25:54 bert * Latest changes ported from 1.0 branch * * Revision 1.10.2.4 2005/08/18 18:17:36 bert * Add -usecoordinates option * * Revision 1.10.2.3 2005/06/09 20:46:11 bert * Fairly major fixes to use integers for all arguments, add filename_format and dirname_format to global options, always include math.h, and defined OPTS_NO_RESCALE for testing converter without ICV * * Revision 1.10.2.2 2005/05/16 19:45:23 bert * Add config.h, sys/types.h, other config.h conditionals for float.h, int32_t and int16_t. Also fix definitions of Name and command_line in the globals structure. * * Revision 1.10.2.1 2005/05/12 21:16:47 bert * Initial checkin * * Revision 1.10 2005/04/29 23:09:36 bert * Add support for -stdin option to read file list from standard input * * Revision 1.9 2005/04/18 16:38:09 bert * Add comments and defines for both DICOM and IMA file detection * * Revision 1.8 2005/04/05 21:50:11 bert * Add comment * * Revision 1.7 2005/03/18 19:10:39 bert * Scan coordinate and location information for validity before relying on it * * Revision 1.6 2005/03/13 19:34:41 bert * Add pms_element_defs.h to the header * * Revision 1.5 2005/03/03 20:10:14 bert * Consider patient_id and patient_name when sorting into series * * Revision 1.4 2005/03/03 18:59:15 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.3 2005/03/02 18:23:33 bert * Added mosaic sequence and bitwise options * * Revision 1.2 2005/02/23 18:28:11 bert * Minor updates * * Revision 1.1 2005/02/17 16:38:09 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.5 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.4 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.3 2000/12/14 21:17:34 rhoge * cleanup of log messages * * Revision 1.2 2000/12/14 21:15:58 rhoge * added ACQ and MEAS constants as flags for type of (non-standard) * dynamic scan looping * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * added num_slices_nominal to Data_Object_Info * (for support of acquisition loop scans) * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #if HAVE_SYS_TYPES_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "acr_element_defs.h" #include "spi_element_defs.h" #include "ext_element_defs.h" #include "pms_element_defs.h" /* Philips Medical Systems */ #include "gems_element_defs.h" /* GE Medical Systems */ #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif /* Constants for "standard" DICOM files (herein referred to as * CD/Export files). For more details of this, see section 7.1 "DICOM * FILE META INFORMATION" of the DICOM specification section 3.10. */ #define DICM_MAGIC_SIZE 4 #define DICM_MAGIC_OFFS 0x0080 #define DICM_MAGIC_STR "DICM" /* Constants for Siemens IMA file format detection. These are somewhat * arbitrary - I test for IMA format by looking for the manufacturer string * SIEMENS at offset 0x0060 */ #define IMA_MAGIC_SIZE 7 #define IMA_MAGIC_OFFS 0x0060 #define IMA_MAGIC_STR "SIEMENS" /* Test to see if two floating-point numbers are very close in value. */ extern int fcmp(double x, double y, double delta); #define NEARLY_EQUAL(x, y) (fcmp(x, y, 1e-6)) #if !HAVE_INT16_T typedef short int16_t; #endif #if !HAVE_INT32_T typedef int int32_t; #endif typedef char string_t[511+1]; #define STRING_T_LEN (sizeof(string_t) - 1) /* Define logging constants */ #define NO_LOGGING 0 #define LO_LOGGING 1 #define HI_LOGGING 2 /* added by rhoge for ACQ and MEAS loop handling */ typedef enum { NONE = 0 , ACQ , MEAS } Loop_Type; /* supported file types */ typedef enum { UNDEF, IMA, N3DCM, N4DCM } File_Type; /* Type for carrying around object information */ typedef struct { int file_index; /* input file index */ char *file_name; /* input file name */ double study_id; /* yyyymmdd.hhmmss */ int study_date; int study_time; int scanner_serialno; int acq_id; int rec_num; int image_type; int num_echoes; int echo_number; int num_dyn_scans; int dyn_scan_number; int global_image_number; int num_slices_nominal; int slice_number; int acq_rows; int acq_cols; int rec_rows; int rec_cols; int num_mosaic_rows; int num_mosaic_cols; int num_slices_in_file; string_t sequence_name; string_t protocol_name; string_t patient_name; string_t patient_id; double slice_location; int coord_found; } Data_Object_Info; #include "dicom_to_minc.h" #include "dicom_read.h" #include "minc_file.h" #include "progress.h" #include "siemens_to_dicom.h" #include "string_to_filename.h" typedef enum { MOSAIC_SEQ_UNKNOWN, MOSAIC_SEQ_INTERLEAVED, MOSAIC_SEQ_ASCENDING, MOSAIC_SEQ_DESCENDING } mosaic_seq_t; /* Globals */ struct globals { char *minc_history; /* Global for minc history */ char *pname; /* program name */ File_Type file_type; /* type of input files */ int Debug; /* Debug on/off */ int Anon; /* "Anonymize" the output */ int List; int useMinMax; /* TRUE if need to use pixel min/max values */ int splitEcho; /* TRUE if echos in separate files */ int splitDynScan; /* TRUE if dynamic scans in separate files */ int clobber; char * command_line; int opts; mosaic_seq_t mosaic_seq; int use_stdin; int use_filelist; /*use file with list of filenames ilana*/ char * filename_format; char * dirname_format; int prefer_coords; /* In event of slice thickness conflict, use the coordinate information rather than the slice thickness or spacing. */ int min_acq_num; /* Minimum acquisition number (0020,0012). */ int max_acq_num; /* Maximum acquisition number. */ int min_img_num; /* Minimum image number (0020,0013). */ int max_img_num; /* Maximum image number. */ int abort_on_error; /* Abort on parse errors. */ }; /* Values for options flags */ #define OPTS_NO_MOSAIC 0x00000001 /* Don't parse mosaic information. */ #define OPTS_KEEP_COORD 0x00000002 /* Don't flip DICOM coordinates */ #define OPTS_NO_LOCATION 0x00000004 /* Never rely on slice location */ #define OPTS_NO_RESCALE 0x00000008 /* Do NOT rescale pixel data in any way! */ extern struct globals G; #define CHKMEM(x) \ if ((x) == NULL) \ (fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__), \ exit(-1)) minc-tools-2.3.00+dfsg/conversion/dcm2mnc/minc_file.c0000644000175000000620000015245712574624760021441 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_file.c @DESCRIPTION: Code to do minc file handling. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: minc_file.c,v $ * Revision 1.19 2010-11-23 23:30:50 claude * dcm2mnc: fixed seg fault bug (Claude) and added b-matrix (Ilana) * * Revision 1.18 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 1.17 2008/01/17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.16 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.15 2008/01/11 07:17:07 stever * Remove unused variables. * * Revision 1.14 2007/12/18 15:19:48 jharlap * reverted acq.comments from its new image_comments name to maintain backwards compatibility. also restored all the dicom elements which ilana decided to remove, with the exception of 0-length elements which make HDF spit out lots of warnings * * Revision 1.13 2007/06/08 20:28:57 ilana * added several fields to mincheader (dicom elements and found in ASCONV header) * * Revision 1.12 2006/04/09 15:34:32 bert * Add ability to save DTI parameters using Jennifer Campbell's convention * * Revision 1.11 2005/11/04 22:26:16 bert * Combined cloned code into a single check_regular() function * * Revision 1.10 2005/08/26 21:25:54 bert * Latest changes ported from 1.0 branch * * Revision 1.6.2.7 2005/08/18 18:17:55 bert * Fix up one warning message * * Revision 1.6.2.6 2005/07/22 20:02:45 bert * 1) Save start value for time coordinate. 2) Don't append fractional seconds to time in filename * * Revision 1.6.2.5 2005/06/20 21:59:33 bert * Add strfminc() to allow arbitrary output file naming, implement OPTS_NO_RESCALE debug option, fix rounding * * Revision 1.6.2.4 2005/06/02 18:20:06 bert * Fix generation and scaling of files with signed data * * Revision 1.6.2.3 2005/05/16 19:55:26 bert * Fix usage of G.Name * * Revision 1.6.2.2 2005/05/13 21:40:15 bert * properly initialize variable, also use _pet instead of _mri suffix for PET modality * * Revision 1.6.2.1 2005/05/12 21:16:48 bert * Initial checkin * * Revision 1.6 2005/04/29 23:09:06 bert * Write sample-width information to file for irregular time dimensions * * Revision 1.5 2005/04/20 23:15:06 bert * Don't save attributes that are no longer set * * Revision 1.4 2005/04/18 16:21:42 bert * Add debugging information for intensity scaling * * Revision 1.3 2005/03/13 19:34:21 bert * Minor change to avoid core dump with strange files * * Revision 1.2 2005/03/03 18:59:15 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.1 2005/02/17 16:38:10 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.12 2002/04/29 15:24:53 rhoge * removed (mode_t) cast in minc_file - would not build on SGI's * * Revision 1.11 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.10 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.9 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.8 2002/03/19 22:10:16 rhoge * removed time sorting for N4DCM mosaics - time is random for mosaics * * Revision 1.7 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.6 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.5 2001/02/26 22:22:37 rhoge * added scanner serial number to minc file naming * * Revision 1.4 2001/02/26 13:38:22 rhoge * made `existing directory' warning conditional on logging * * Revision 1.3 2000/12/15 01:04:46 rhoge * make sure acquisition_id (series no) is 6 digit hhmmss string for meas loop * * Revision 1.2 2000/12/14 21:19:22 rhoge * added code to compute time spacing if measurement loop dynamic * scanning has been detected * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 "dcm2mnc.h" #include /* Define mri dimension names */ static char *mri_dim_names[] = { NULL, "echo_time", MItime, "phase_number", "chemical_shift", NULL}; /* * verify that a list of coordinates is "regular", that is, that the * spacing between each adjacent pair does not vary by more than a * small amount relative to an average stepsize. */ int check_regular(double step, double coordinates[], int length) { int index; double diff; if (step == 0.0) { step = 1.0; /* avoid division by zero */ } for (index = 1; index < length; index++) { /* Calculate the difference between two adjacent locations, * less the average step value. */ diff = (coordinates[index] - coordinates[index - 1]) - step; if (diff < 0.0) { diff = -diff; } diff /= step; if (diff > COORDINATE_EPSILON) { return FALSE; } } return TRUE; } int strfminc(char *str_ptr, int str_max, const char *fmt_ptr, General_Info *gi_ptr) { char *tmp_ptr; int str_len = 0; char scan_label[MRI_NDIMS][20]; Mri_Index imri; static char *scan_prefix[MRI_NDIMS] = {"sl", "e", "d", "p", "cs"}; char tmp_str[1024]; /* Get strings for echo number, etc. */ for (imri = 0; imri < MRI_NDIMS; imri++) { if ((gi_ptr->cur_size[imri] < gi_ptr->max_size[imri]) && (gi_ptr->cur_size[imri] == 1)) { sprintf(scan_label[imri], "%s%d", scan_prefix[imri], gi_ptr->default_index[imri]); } else { strcpy(scan_label[imri], ""); } } while (*fmt_ptr != '\0') { tmp_ptr = NULL; if (*fmt_ptr == '%') { fmt_ptr++; switch (*fmt_ptr) { case 'N': /* Subject name */ string_to_filename(gi_ptr->patient.name, tmp_str, sizeof(tmp_str)); if (tmp_str[0] == '\0') { tmp_ptr = "no_name"; } else { tmp_ptr = tmp_str; } break; case 'D': string_to_filename(gi_ptr->patient.reg_date, tmp_str, sizeof(tmp_str)); tmp_ptr = tmp_str; break; case 'S': string_to_filename(gi_ptr->study.study_id, tmp_str, sizeof(tmp_str)); tmp_ptr = tmp_str; break; case 'T': strcpy(tmp_str, gi_ptr->patient.reg_time); tmp_ptr = tmp_str; while (*tmp_ptr != '\0') { if (!isdigit(*tmp_ptr)) { *tmp_ptr = '\0'; break; } tmp_ptr++; } tmp_ptr = tmp_str; break; case 'A': string_to_filename(gi_ptr->study.acquisition_id, tmp_str, sizeof(tmp_str)); tmp_ptr = tmp_str; break; case 's': tmp_ptr = scan_label[SLICE]; break; case 'e': tmp_ptr = scan_label[ECHO]; break; case 't': tmp_ptr = scan_label[TIME]; break; case 'p': tmp_ptr = scan_label[PHASE]; break; case 'c': tmp_ptr = scan_label[CHEM_SHIFT]; break; case 'm': if (!strcmp(gi_ptr->study.modality, MI_MRI)) { tmp_ptr = "_mri"; } else if (!strcmp(gi_ptr->study.modality, MI_PET)) { tmp_ptr = "_pet"; } else { tmp_ptr = ""; } break; default: break; } } if (tmp_ptr == NULL) { if (str_len < str_max) { *str_ptr++ = *fmt_ptr++; str_len++; } } else { fmt_ptr++; while (str_len < str_max && *tmp_ptr != '\0') { *str_ptr++ = *tmp_ptr++; str_len++; } } } *str_ptr++ = '\0'; return (str_len); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_minc_file @INPUT : minc_file - name of file to create. If NULL, a name is generated internally. clobber - if TRUE, any existing file will be overwritten. general_info - information for creating the file. file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : id of image conversion variable (MI_ERROR in case of error). @DESCRIPTION: Routine to create the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : rhoge - modified to create directory for session -------------------------------------------------------------------------- */ int create_minc_file(const char *minc_file, int clobber, General_Info *general_info, const char *file_prefix, const char **output_file_name, Loop_Type loop_type) { char temp_name[1024]; const char *filename; int minc_clobber; int mincid, icvid; static char full_path[1024]; /* Turn off fatal errors */ ncopts = NCOPTS_DEFAULT; /* Create the file name if needed */ if (minc_file != NULL) { filename = (const char *) minc_file; } else { /* rhoge: add session directory to prefix */ strcpy(full_path, file_prefix); if (G.dirname_format == NULL) { G.dirname_format = "%N_%D_%T"; } strfminc(temp_name, sizeof(temp_name), G.dirname_format, general_info); strcat(full_path, temp_name); if (strlen(full_path) != 0) { if (mkdir(full_path, 0777) && G.Debug) { printf("Directory %s exists...\n", full_path); } strcat(full_path, "/"); } /* if measurement loop, make sure that acquisition_id is * a 6 digit (hhmmss) string with leading zero if needed */ if (loop_type == MEAS) { sprintf(general_info->study.acquisition_id, "%06d", general_info->acq_id); } /* Create file name */ if (G.filename_format == NULL) { G.filename_format = "%N_%D_%T_%A%s%e%t%p%c%m"; } strfminc(temp_name, sizeof(temp_name), G.filename_format, general_info); strcat(full_path, temp_name); strcat(full_path, ".mnc"); /* Always append the extension */ filename = full_path; if (G.Debug) { printf("MINC file name: %s\n", filename); printf("Patient name: %s\n", general_info->patient.name); printf("Study ID: %s\n", general_info->study.study_id); printf("Acquisition ID: %s\n", general_info->study.acquisition_id); printf("Registration date: %s\n", general_info->patient.reg_date); printf("Registration time: %s\n", general_info->patient.reg_time); printf("Rows %d columns %d slices %d/%d\n", general_info->nrows, general_info->ncolumns, general_info->cur_size[SLICE], general_info->max_size[SLICE]); if (general_info->max_size[TIME] != 1) { printf("Time axis length: %d/%d\n", general_info->cur_size[TIME], general_info->max_size[TIME]); } } } /* Set output file name */ if (output_file_name != NULL) { *output_file_name = filename; } /* Set the clobber value */ if (clobber) minc_clobber = NC_CLOBBER; else minc_clobber = NC_NOCLOBBER; /* Create the file */ mincid = micreate(filename, minc_clobber); if (mincid == MI_ERROR) { return MI_ERROR; } /* Set up variables */ setup_minc_variables(mincid, general_info, loop_type); /* Put the file in data mode */ ncsetfill(mincid, NC_NOFILL); if (ncendef(mincid) == MI_ERROR) { return MI_ERROR; } /* Create the icv */ icvid = miicv_create(); /* Set the type and range */ miicv_setint(icvid, MI_ICV_TYPE, NC_SHORT); if (general_info->is_signed) miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); else miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); miicv_setdbl(icvid, MI_ICV_VALID_MIN, general_info->pixel_min); miicv_setdbl(icvid, MI_ICV_VALID_MAX, general_info->pixel_max); /* Attach the icv */ miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_set_spacing @INPUT : mincid varid imri gi_ptr @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: This function checks the given MRI dimension (most typically the TIME dimension) to see if it has a "regular" structure. If so, the MINC file is updated accordingly. If not, the function creates a "xxxx-width" variable corresponding to the dimension which will contain the width information from this dimension. NOTE: At present only the time-width variable is defined by MINC. @METHOD : @GLOBALS : CALLS : @CREATED : April 27, 2005 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ static void minc_set_spacing(int mincid, int varid, Mri_Index imri, General_Info *gi_ptr) { double sum = 0.0; /* Sum of differences for computing average */ double avg; /* Average */ double step; /* Step size from widths */ int regular; /* TRUE if dimension is regular */ int index; /* Loop/array index */ long length; /* Length of this dimension (> 1) */ regular = TRUE; length = gi_ptr->cur_size[imri]; if (length <= 1) { fprintf(stderr, "minc_set_spacing called with length <= 1\n"); exit(-1); } /* First, see if the widths were set, and if so, if they are consistent. */ for (index = 1; index < length; index++) { if (gi_ptr->widths[imri][0] != gi_ptr->widths[imri][index]) { regular = FALSE; break; } } /* OK, now set the step value according to the widths, if possible. */ if (regular) { step = gi_ptr->widths[imri][0]; /* Now calculate the average value for the coordinate spacing. */ for (index = 1; index < length; index++) { sum += gi_ptr->coordinates[imri][index] - gi_ptr->coordinates[imri][index-1]; } /* The previous loop sums "length - 1" numbers, so that is the * correct divisor in this calculation. It used to be "length" * (bert). */ avg = sum / (length - 1); /* compute mean */ if (step != 0.0 && avg != step) { printf("WARNING: Sample width (%g) not equal to average delta (%g)\n", step, avg); } step = avg; /* Use the average anyway. */ /* Check for uniformity of spacing */ regular = check_regular(step, gi_ptr->coordinates[imri], length); } else { /* We have widths provided for us, so use them to calculate the * average step size. */ for (index = 0; index < length; index++) { sum += gi_ptr->widths[imri][index]; } step = sum / length; } /* * Write the step value. According to the MINC specifications, it is * always valid to store a step value even for irregular dimensions. * The step should always equal the average spacing of the dimension. */ miattputdbl(mincid, varid, MIstep, step); miattputdbl(mincid, varid, MIstart, gi_ptr->coordinates[imri][0]); if (regular) { miattputstr(mincid, varid, MIspacing, MI_REGULAR); } else { miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); /* Create the -width variable. At present, this * is only a valid operation if the dimension in question is the * time dimension. MINC does not define a width variable for any of * the other, non-standard dimensions. So for now this code is * very much a special case. */ if (imri == TIME) { int dimid; dimid = ncdimid(mincid, MItime); if (dimid >= 0) { micreate_std_variable(mincid, MItime_width, NC_DOUBLE, 1, &dimid); } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_variables @INPUT : mincid general_info @OUTPUT : general_info @RETURNS : (nothing) @DESCRIPTION: Routine to setup minc variables. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void setup_minc_variables(int mincid, General_Info *general_info, Loop_Type loop_type) { Mri_Index imri; Volume_Index ivol; World_Index iworld; int ndims; int dim[MAX_VAR_DIMS]; long dimsize; char *dimname; int varid, imgid, dicomvar; double valid_range[2]; char name[MAX_NC_NAME]; Acr_Group cur_group; Acr_Element cur_element; int length; char *data; nc_type datatype; int is_char; int ich; int is_flattened; /* Define the spatial dimension names */ static char *spatial_dimnames[WORLD_NDIMS] = {MIxspace, MIyspace, MIzspace}; /* Create the dimensions from slowest to fastest */ ndims=0; /* Create the non-spatial dimensions (from slowest to fastest) */ for (imri=MRI_NDIMS-1; (int) imri > SLICE; imri--) { /* for the TIME dimension, check if we have acquisition-loop dynamic scan OR a `corrected' dynamic scan */ if ( (imri==TIME) && ((loop_type!=NONE) || (general_info->acq.num_dyn_scans>1)) ) { /* for Siemens scans using the signal averaging loop for multiple time points we use the TR as the time step */ dimsize = general_info->cur_size[TIME]; if (general_info->cur_size[TIME] > 1) { dimname = mri_dim_names[TIME]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); miattputstr(mincid, varid, MIspacing, MI_REGULAR); miattputstr(mincid, varid, MIunits, "s"); if (loop_type == MEAS) { /* if Meas loop, time step is not equal to TR, and frames should have time values (rhoge) */ minc_set_spacing(mincid, varid, TIME, general_info); } else { /* assume ACQ loop and use TR for time step */ miattputdbl(mincid, varid, MIstep, general_info->acq.rep_time); } miattputdbl(mincid, varid, MIstart,0); general_info->image_index[TIME] = ndims; ndims++; } } else { /* NORMAL CODE */ dimsize = general_info->cur_size[imri]; if (general_info->cur_size[imri] > 1) { dimname = mri_dim_names[imri]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (imri == TIME) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); miattputstr(mincid, varid, MIunits, "s"); minc_set_spacing(mincid, varid, TIME, general_info); } else if (imri == ECHO) { varid = ncvardef(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); miattputstr(mincid, varid, MIvartype, MI_DIMENSION); miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); miattputstr(mincid, varid, MIunits, "s"); } general_info->image_index[imri] = ndims; ndims++; } } } /* Next the spatial dimensions */ for (ivol = 0; ivol < VOL_NDIMS; ivol++) { switch (ivol) { case VSLICE: dimsize = general_info->cur_size[SLICE]; iworld = general_info->slice_world; break; case VROW: dimsize = general_info->nrows; iworld = general_info->row_world; break; case VCOLUMN: dimsize = general_info->ncolumns; iworld = general_info->column_world; break; default: fprintf(stderr, "Should not happen!!"); exit(-1); } dimname = spatial_dimnames[iworld]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (ivol == VSLICE) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); /* Check for regular slices */ if (check_regular(general_info->step[general_info->slice_world], general_info->coordinates[SLICE], general_info->cur_size[SLICE])) { miattputstr(mincid, varid, MIspacing, MI_REGULAR); } } else { varid = micreate_std_variable(mincid, dimname, NC_LONG, 0, NULL); } miattputdbl(mincid, varid, MIstep, general_info->step[iworld]); miattputdbl(mincid, varid, MIstart, general_info->start[iworld]); miattputstr(mincid, varid, MIspacetype, MI_NATIVE); ncattput(mincid, varid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, general_info->dircos[iworld]); if (ivol == VSLICE) { general_info->image_index[SLICE] = ndims; } ndims++; } /* Set up image variable */ imgid = micreate_std_variable(mincid, MIimage, general_info->datatype, ndims, dim); if (general_info->is_signed) miattputstr(mincid, imgid, MIsigntype, MI_SIGNED); else miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); valid_range[0] = general_info->pixel_min; valid_range[1] = general_info->pixel_max; ncattput(mincid, imgid, MIvalid_range, NC_DOUBLE, 2, valid_range); miattputstr(mincid, imgid, MIcomplete, MI_FALSE); /* Create image max and min variables */ varid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); if (strlen(general_info->units) > 0) miattputstr(mincid, varid, MIunits, general_info->units); varid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); if (strlen(general_info->units) > 0) miattputstr(mincid, varid, MIunits, general_info->units); /* Create the patient variable */ varid = micreate_group_variable(mincid, MIpatient); if (strlen(general_info->patient.name) > 0) { if (G.Anon) { miattputstr(mincid, varid, MIfull_name, "anonymous"); } else { miattputstr(mincid, varid, MIfull_name, general_info->patient.name); } } if (strlen(general_info->patient.identification) > 0) miattputstr(mincid, varid, MIidentification, general_info->patient.identification); if (strlen(general_info->patient.birth_date) > 0) miattputstr(mincid, varid, MIbirthdate, general_info->patient.birth_date); if (strlen(general_info->patient.age) > 0) miattputstr(mincid, varid, MIage, general_info->patient.age); if (strlen(general_info->patient.sex) > 0) miattputstr(mincid, varid, MIsex, general_info->patient.sex); if (general_info->patient.weight != -DBL_MAX) miattputdbl(mincid, varid, MIweight, general_info->patient.weight); if (strlen(general_info->patient.position) > 0) miattputstr(mincid, varid, "position", general_info->patient.position); /* Create the study variable */ varid = micreate_group_variable(mincid, MIstudy); /* rhoge: fixed date/time to reflect study */ if (strlen(general_info->patient.reg_date) > 0) miattputstr(mincid, varid, "start_date", general_info->patient.reg_date); if (strlen(general_info->patient.reg_time) > 0) miattputstr(mincid, varid, MIstart_time, general_info->patient.reg_time); if (strlen(general_info->study.modality) > 0) miattputstr(mincid, varid, MImodality, general_info->study.modality); if (strlen(general_info->study.manufacturer) > 0) miattputstr(mincid, varid, MImanufacturer, general_info->study.manufacturer); if (strlen(general_info->study.model) > 0) miattputstr(mincid, varid, MIdevice_model, general_info->study.model); if (general_info->study.field_value != -DBL_MAX) miattputdbl(mincid, varid, "field_value", general_info->study.field_value); if (strlen(general_info->study.software_version) > 0) miattputstr(mincid, varid, "software_version", general_info->study.software_version); if (strlen(general_info->study.serial_no) > 0) miattputstr(mincid, varid, "serial_no", general_info->study.serial_no); if (strlen(general_info->study.calibration_date) > 0) miattputstr(mincid, varid, "calibration_date", general_info->study.calibration_date); if (strlen(general_info->study.calibration_time) > 0) miattputstr(mincid, varid, "calibration_time", general_info->study.calibration_time); if (strlen(general_info->study.institution) > 0) miattputstr(mincid, varid, MIinstitution, general_info->study.institution); if (strlen(general_info->study.station_id) > 0) miattputstr(mincid, varid, MIstation_id, general_info->study.station_id); if (strlen(general_info->study.referring_physician) > 0) miattputstr(mincid, varid, MIreferring_physician, general_info->study.referring_physician); if (strlen(general_info->study.performing_physician) > 0) miattputstr(mincid, varid, "performing_physician", general_info->study.referring_physician); if (strlen(general_info->study.operator) > 0) miattputstr(mincid, varid, MIoperator, general_info->study.operator); if (strlen(general_info->study.procedure) > 0) miattputstr(mincid, varid, MIprocedure, general_info->study.procedure); if (strlen(general_info->study.study_id) > 0) miattputstr(mincid, varid, MIstudy_id, general_info->study.study_id); /* Create acquisition variable */ varid = micreate_group_variable(mincid, MIacquisition); if (strlen(general_info->study.acquisition_id) > 0) miattputstr(mincid, varid, "acquisition_id", general_info->study.acquisition_id); if (strlen(general_info->study.start_time) > 0) miattputstr(mincid, varid, MIstart_time, general_info->study.start_time); /*added some more study info*/ if (strlen(general_info->acq.series_time) > 0) miattputstr(mincid, varid, "series_time", general_info->acq.series_time); if (strlen(general_info->acq.acquisition_time) > 0) /*should use this instead of the Study time*/ miattputstr(mincid, varid, "acquisition_time", general_info->acq.acquisition_time); if (strlen(general_info->acq.image_time) > 0) miattputstr(mincid, varid, "image_time", general_info->acq.image_time); if (strlen(general_info->acq.scan_seq) > 0) miattputstr(mincid, varid, MIscanning_sequence, general_info->acq.scan_seq); if (strlen(general_info->acq.series_description) > 0) /*add Series Description*/ miattputstr(mincid, varid, "series_description", general_info->acq.series_description); if (strlen(general_info->acq.protocol_name) > 0) miattputstr(mincid, varid, MIprotocol, general_info->acq.protocol_name); if (strlen(general_info->acq.receive_coil) > 0) miattputstr(mincid, varid, "receive_coil", general_info->acq.receive_coil); if (strlen(general_info->acq.transmit_coil) > 0) miattputstr(mincid, varid, "transmit_coil", general_info->acq.transmit_coil); if (general_info->acq.rep_time != -DBL_MAX) miattputdbl(mincid, varid, MIrepetition_time, general_info->acq.rep_time); if ((general_info->acq.echo_time != -DBL_MAX) && (general_info->cur_size[ECHO] <= 1)) miattputdbl(mincid, varid, MIecho_time, general_info->acq.echo_time); if (general_info->acq.echo_number != -DBL_MAX) miattputdbl(mincid, varid, "echo_number", general_info->acq.echo_number); if (general_info->acq.echo_train_length != -DBL_MAX) /*add echo train length ilana*/ miattputdbl(mincid, varid, "echo_train_length", general_info->acq.echo_train_length); if (general_info->acq.inv_time != -DBL_MAX) miattputdbl(mincid, varid, MIinversion_time, general_info->acq.inv_time); if (general_info->acq.flip_angle != -DBL_MAX) miattputdbl(mincid, varid, "flip_angle", general_info->acq.flip_angle); if (general_info->acq.slice_thickness != -DBL_MAX) miattputdbl(mincid, varid, "slice_thickness", general_info->acq.slice_thickness); if (general_info->acq.num_slices != -DBL_MAX) miattputdbl(mincid, varid, "num_slices", general_info->acq.num_slices); if (strlen(general_info->acq.slice_order) > 0) /* add slice ordering info*/ miattputstr(mincid, varid, "slice_order", general_info->acq.slice_order); if (general_info->acq.b_value != -DBL_MAX) miattputdbl(mincid, varid, "b_value", general_info->acq.b_value); if (general_info->acq.delay_in_TR != -DBL_MAX) /*add delay in TR*/ miattputdbl(mincid, varid, "delay_in_TR", general_info->acq.delay_in_TR); /* add number of dynamic scans (rhoge) */ /* this will be relevant if we are receiving siemens scans that have been `cleaned up' (and hence have the correct number of dynamic scans inserted) */ if (general_info->acq.num_dyn_scans != -DBL_MAX) miattputdbl(mincid, varid, "num_dyn_scans", general_info->acq.num_dyn_scans); if (general_info->acq.num_avg != -DBL_MAX) miattputdbl(mincid, varid, MInum_averages, general_info->acq.num_avg); if (general_info->acq.imaging_freq != -DBL_MAX) miattputdbl(mincid, varid, MIimaging_frequency, general_info->acq.imaging_freq); if (strlen(general_info->acq.imaged_nucl) > 0) miattputstr(mincid, varid, MIimaged_nucleus, general_info->acq.imaged_nucl); if (general_info->acq.win_center != -DBL_MAX) miattputdbl(mincid, varid, "window_center", general_info->acq.win_center); if (general_info->acq.win_width != -DBL_MAX) miattputdbl(mincid, varid, "window_width", general_info->acq.win_width); if (general_info->acq.num_phase_enc_steps != -DBL_MAX) miattputdbl(mincid, varid, "num_phase_enc_steps", general_info->acq.num_phase_enc_steps); if (general_info->acq.percent_sampling != -DBL_MAX) miattputdbl(mincid, varid, "percent_sampling", general_info->acq.percent_sampling); if (general_info->acq.percent_phase_fov != -DBL_MAX) miattputdbl(mincid, varid, "percent_phase_fov", general_info->acq.percent_phase_fov); if (general_info->acq.pixel_bandwidth != -DBL_MAX) miattputdbl(mincid, varid, "pixel_bandwidth", general_info->acq.pixel_bandwidth); if (strlen(general_info->acq.phase_enc_dir) > 0) miattputstr(mincid, varid, "phase_enc_dir", general_info->acq.phase_enc_dir); if (general_info->acq.sar != -DBL_MAX) miattputdbl(mincid, varid, "SAR", general_info->acq.sar); if (strlen(general_info->acq.mr_acq_type) > 0) miattputstr(mincid, varid, "mr_acq_type", general_info->acq.mr_acq_type); if (strlen(general_info->acq.image_type) > 0) miattputstr(mincid, varid, "image_type", general_info->acq.image_type); if (strlen(general_info->acq.comments) > 0) miattputstr(mincid, varid, MIcomments, general_info->acq.comments); // this is Siemens Numaris 4 specific! if (strlen(general_info->acq.MrProt) > 0) miattputstr(mincid, varid, "MrProt_dump", general_info->acq.MrProt); /* Add DTI stuff if needed */ if (general_info->acq.dti) { int length = general_info->cur_size[TIME]; double *tmp_ptr = calloc(length, sizeof(double)); ncattput(mincid, varid, "bvalues", NC_DOUBLE, length, tmp_ptr); ncattput(mincid, varid, "direction_x", NC_DOUBLE, length, tmp_ptr); ncattput(mincid, varid, "direction_y", NC_DOUBLE, length, tmp_ptr); ncattput(mincid, varid, "direction_z", NC_DOUBLE, length, tmp_ptr); free( tmp_ptr ); /*This means that ANY DTI sequence will have a b_matrix in the mincheader. For Siemens software versions before VB (and probably other vendors), the field does not exist but we still choose to set the b_matrix field in the mincheader to all 0s. We do this because b=0 files do not have the b_matrix 0019x1027 field, and we must create one. I.e. determining whether the field exists or not in a particular file is not enough to reject it for the series.*/ int num_elements=6;/*should always have 6 elements per direction (i.e. direction=TIME variable)*/ double *tmp_ptr2 = calloc(length*num_elements,sizeof(double)); ncattput(mincid, varid, "b_matrix", NC_DOUBLE, num_elements*length, tmp_ptr2); } /* Create the dicom info variable */ varid = ncvardef(mincid, "dicominfo", NC_LONG, 0, NULL); miattputstr(mincid, varid, MIvartype, MI_GROUP); miattputstr(mincid, varid, MIvarid, "MNI DICOM information variable"); miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); if (strlen(general_info->image_type_string) > 0) miattputstr(mincid, varid, "image_type", general_info->image_type_string); miattputdbl(mincid, varid, "window_min", general_info->window_min); miattputdbl(mincid, varid, "window_max", general_info->window_max); /* Put group info in header */ /* A lot of these dicom groups dump lots of junk into the mincheader, and most of the information here has already been included in the minc fields, we just should add those that are missing and might be of use. Removing the fields starting with "dicom_0x... for now ilana*/ /* contrary to ilanas view - this *is* useful info! */ cur_group = general_info->group_list; dicomvar = ncvardef(mincid, DICOM_ROOT_VAR, NC_LONG, 0, NULL); miattputstr(mincid, dicomvar, MIvartype, MI_GROUP); miattputstr(mincid, dicomvar, MIvarid, "MNI DICOM variable"); miadd_child(mincid, ncvarid(mincid, MIrootvariable), dicomvar); while (cur_group != NULL) { /* Create variable for group */ sprintf(name, "dicom_0x%04x", acr_get_group_group(cur_group)); varid = ncvardef(mincid, name, NC_LONG, 0, NULL); miattputstr(mincid, varid, MIvartype, MI_GROUP); miattputstr(mincid, varid, MIvarid, "MNI DICOM variable"); miadd_child(mincid, dicomvar, varid); /* Loop through elements of group */ cur_element = acr_get_group_element_list(cur_group); while (cur_element != NULL) { sprintf(name, "el_0x%04x", acr_get_element_element(cur_element)); is_char = TRUE; length = acr_get_element_length(cur_element); data = acr_get_element_data(cur_element); if (data == NULL) { length = 0; } /* If the element is a sequence, we need to flatten out the * data so that we store the actual DICOM data rather than * our internal representation of it. We allocate a * data buffer to hold the output, because we can't safely * modify the memory returned by acr_get_element_data(). */ if (acr_element_is_sequence(cur_element) && (data = malloc(length)) != NULL) { /* Create a file to store the output. */ FILE *fp = tmpfile(); Acr_File *afp = acr_file_initialize(fp, 0, acr_stdio_write); Acr_Element item; for (item = (Acr_Element) acr_get_element_data(cur_element); item != NULL; item = acr_get_element_next(item)) { acr_output_element(afp, item); } acr_file_flush(afp); /* Now seek back to the beginning of the temporary file, * and read in the data we wrote. */ fseek(fp, 0, SEEK_SET); fread(data, 1, length, fp); acr_file_free(afp); fclose(fp); /* Set flag so that we will free() the pointer later on. */ is_flattened = 1; } else { is_flattened = 0; } for (ich=0; ich < length; ich++) { if (!isprint((int) data[ich])) { is_char = FALSE; break; } } if (is_char) datatype = NC_CHAR; else datatype = NC_BYTE; /* Do not insert 0-length elements as it makes HDF complain */ if(length > 0) ncattput(mincid, varid, name, datatype, length, data); cur_element = acr_get_element_next(cur_element); /* If we had to "flatten" the element because it is a sequence, * remove the resources used to accomplish this. */ if (is_flattened) { free(data); } } cur_group = acr_get_group_next(cur_group); } /* Create the history attribute */ if (G.minc_history != NULL) { miattputstr(mincid, NC_GLOBAL, MIhistory, G.minc_history); } return; } /* Insert a scalar 'value' at 'position' along the given vector attribute * of preallocated 'length'. */ void put_att_dbl(int mincid, int varid, char *attname, int length, int position, double value) { double *val_ptr = malloc(sizeof(double) * length); if (val_ptr != NULL) { ncattget(mincid, varid, attname, val_ptr); val_ptr[position] = value; ncattput(mincid, varid, attname, NC_DOUBLE, length, val_ptr); free( val_ptr ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : save_minc_image @INPUT : icvid general_info file_info image @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to save the image in the minc file @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void save_minc_image(int icvid, General_Info *gi_ptr, File_Info *fi_ptr, Image_Data *image) { int mincid; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int file_index, array_index; int idim; Mri_Index imri; char *dimname; int pvalue, pmax, pmin; double dvalue, maximum, minimum, scale, offset; long ipix, imagepix; /* Get the minc file id */ miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Create start and count variables */ idim = 0; for (imri=MRI_NDIMS-1; (int) imri >= 0; imri--) { if (gi_ptr->image_index[imri] >= 0) { file_index = gi_ptr->image_index[imri]; if (gi_ptr->cur_size[imri] > 1) { array_index = search_list(fi_ptr->index[imri], gi_ptr->indices[imri], gi_ptr->cur_size[imri], gi_ptr->search_start[imri]); if (array_index < 0) array_index = 0; gi_ptr->search_start[imri] = array_index; } else { array_index = 0; } start[file_index] = array_index; count[file_index] = 1; idim++; } } start[idim] = 0; start[idim+1] = 0; count[idim] = gi_ptr->nrows; count[idim+1] = gi_ptr->ncolumns; /* Write out slice position */ switch (gi_ptr->slice_world) { case XCOORD: dimname = MIxspace; break; case YCOORD: dimname = MIyspace; break; case ZCOORD: dimname = MIzspace; break; default: dimname = MIzspace; } mivarput1(mincid, ncvarid(mincid, dimname), &start[gi_ptr->image_index[SLICE]], NC_DOUBLE, NULL, &fi_ptr->coordinate[SLICE]); /* Write out time of slice, if needed */ if (gi_ptr->cur_size[TIME] > 1) { mivarput1(mincid, ncvarid(mincid, mri_dim_names[TIME]), &start[gi_ptr->image_index[TIME]], NC_DOUBLE, NULL, &fi_ptr->coordinate[TIME]); if (gi_ptr->acq.dti) { put_att_dbl(mincid, ncvarid(mincid, MIacquisition), "bvalues", gi_ptr->cur_size[TIME], start[gi_ptr->image_index[TIME]], fi_ptr->b_value); put_att_dbl(mincid, ncvarid(mincid, MIacquisition), "direction_x", gi_ptr->cur_size[TIME], start[gi_ptr->image_index[TIME]], fi_ptr->grad_direction[XCOORD]); put_att_dbl(mincid, ncvarid(mincid, MIacquisition), "direction_y", gi_ptr->cur_size[TIME], start[gi_ptr->image_index[TIME]], fi_ptr->grad_direction[YCOORD]); put_att_dbl(mincid, ncvarid(mincid, MIacquisition), "direction_z", gi_ptr->cur_size[TIME], start[gi_ptr->image_index[TIME]], fi_ptr->grad_direction[ZCOORD]); int i; int num_elements=6; for(i=0;icur_size[TIME], start[gi_ptr->image_index[TIME]]*num_elements+i, fi_ptr->b_matrix[i]); } } /* If width information is present, save it to the appropriate * location in the time-width variable. */ if (fi_ptr->width[TIME] != 0.0) { int ncopts_prev; int varid; /* Since it is possible for width information to be present * in circumstances where we do not want to save it, the * time-width variable may not even exist when we get here. * In order to avoid a nasty and unnecessary error message * we have to disable netCDF errors here. */ ncopts_prev = ncopts; ncopts = 0; varid = ncvarid(mincid, MItime_width); /* Get the variable id */ ncopts = ncopts_prev; /* If the variable was created, update it as needed. */ if (varid >= 0) { mivarput1(mincid, varid, &start[gi_ptr->image_index[TIME]], NC_DOUBLE, NULL, &fi_ptr->width[TIME]); } } } /* Write out echo time of slice, if needed */ if (gi_ptr->cur_size[ECHO] > 1) { mivarput1(mincid, ncvarid(mincid, mri_dim_names[ECHO]), &start[gi_ptr->image_index[ECHO]], NC_DOUBLE, NULL, &fi_ptr->coordinate[ECHO]); } /* Search image for max and min. This needs to be done such * that we interpret signed data correctly, so there are separate * loops for signed and unsigned data. * * If the data is signed, we need to search for the smallest and * lowest 2's complement 16-bit values. These will range from (at * most) -32768 to 32767. For unsigned values, we know that the * range will be from 0 to 65535. Since pmin, pmax, and pvalue * are declared to be 'int', they will always be able to represent * these values on 32-bit or 64-bit architectures. * * First, calculate the total number of voxels in this image. */ imagepix = gi_ptr->nrows * gi_ptr->ncolumns; pmax = INT_MIN; /* Initialize to smallest possible int */ pmin = INT_MAX; /* Initialize to largest possible int */ if (gi_ptr->is_signed) { short *ssh_ptr = (short *) image->data; /* Cast to signed data */ for (ipix = 0; ipix < imagepix; ipix++) { pvalue = ssh_ptr[ipix]; if (pvalue > pmax) pmax = pvalue; if (pvalue < pmin) pmin = pvalue; } } else { unsigned short *ush_ptr = (unsigned short *) image->data; for (ipix = 0; ipix < imagepix; ipix++) { pvalue = ush_ptr[ipix]; if (pvalue > pmax) pmax = pvalue; if (pvalue < pmin) pmin = pvalue; } } /* Calculate the 'scale' and 'offset' (slope and intercept) we * must use to scale the data. */ if (pmax > pmin) { scale = (gi_ptr->pixel_max - gi_ptr->pixel_min) / ((double) pmax - (double) pmin); } else { scale = 0.0; } offset = gi_ptr->pixel_min - scale * (double) pmin; /* debugging info for slice intensity scaling */ if (G.Debug >= HI_LOGGING) { printf("ranges: global %.2f %.2f, file %.2f %.2f, ", gi_ptr->pixel_min, gi_ptr->pixel_max, fi_ptr->slice_min, fi_ptr->slice_max); printf("slice %d %d\n", pmin, pmax); printf("1. scale %.2f offset %.2f\n", scale, offset); } /* Re-scale the images. Again, this has to be done in a * "signedness-aware" way, so that negative values will be * dealt with properly in signed data. */ if (gi_ptr->is_signed) { short *ssh_ptr = (short *) image->data; for (ipix = 0; ipix < imagepix; ipix++) { dvalue = ssh_ptr[ipix]; ssh_ptr[ipix] = (short) rint(dvalue * scale + offset); } } else { unsigned short *ush_ptr = (unsigned short *) image->data; for (ipix = 0; ipix < imagepix; ipix++) { dvalue = ush_ptr[ipix]; ush_ptr[ipix] = (unsigned short) rint(dvalue * scale + offset); } } if (gi_ptr->pixel_max > gi_ptr->pixel_min) { scale = (fi_ptr->slice_max - fi_ptr->slice_min) / (gi_ptr->pixel_max - gi_ptr->pixel_min); } else { scale = 0.0; } offset = fi_ptr->slice_min - scale * gi_ptr->pixel_min; minimum = (double) pmin * scale + offset; maximum = (double) pmax * scale + offset; if (G.Debug >= HI_LOGGING) { printf("2. scale %.2f offset %.2f min %.2f max %.2f\n", scale, offset, minimum, maximum); printf("3. position %ld,%ld,%ld\n", start[0], start[1], start[2]); } /* Write out the max and min values */ mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, NULL, &minimum); mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, NULL, &maximum); if (G.opts & OPTS_NO_RESCALE) { mivarput(mincid, ncvarid(mincid, MIimage), start, count, NC_SHORT, (gi_ptr->is_signed) ? MI_SIGNED : MI_UNSIGNED, image->data); } else { /* Write out the image */ miicv_put(icvid, start, count, image->data); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_file @INPUT : icvid - value returned by create_minc_file @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 30, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void close_minc_file(int icvid) { int mincid; /* Get the minc file id */ miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Write out the complete attribute */ miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); /* Close the file */ miclose(mincid); miicv_free(icvid); } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dcm2mnc.c0000644000175000000620000011042612574624760021025 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dcm2mnc.c @DESCRIPTION: Program to convert dicom files to minc @GLOBALS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : * $Log: dcm2mnc.c,v $ * Revision 1.24 2010-11-23 23:30:50 claude * dcm2mnc: fixed seg fault bug (Claude) and added b-matrix (Ilana) * * Revision 1.23 2008-08-12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 1.22 2008/01/17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.21 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.20 2007/05/30 15:17:34 ilana * fix so that diffusion images all written into 1 4d volume, gradient directions and bvalues are written to mincheader, some fixes for TIM diffusion images * * Revision 1.19 2005/11/11 18:42:54 bert * Latest fixes to dcm2mnc * * Revision 1.18 2005/11/04 22:25:04 bert * Update version string to 2.0.07 * * Revision 1.17 2005/08/26 21:25:54 bert * Latest changes ported from 1.0 branch * * Revision 1.14.2.10 2005/08/18 18:17:36 bert * Add -usecoordinates option * * Revision 1.14.2.9 2005/07/22 20:03:16 bert * Minor change to consider sequence name when finding series boundaries * * Revision 1.14.2.8 2005/07/12 16:01:14 bert * Sort on filename if all else fails * * Revision 1.14.2.7 2005/06/20 22:03:31 bert * Add simple test for binary files to avoid choking on text files * * Revision 1.14.2.6 2005/06/09 20:48:36 bert * Remove obsolete -descr option, add shiny new -fname and -dname options. Also don't explicitly include math.h * * Revision 1.14.2.5 2005/06/02 18:35:32 bert * Change version to 2.0.06 * * Revision 1.14.2.4 2005/05/16 22:39:56 bert * Insert conditionals to make the file build properly under Windows * * Revision 1.14.2.3 2005/05/16 19:55:50 bert * Fix usage of G.command_line * * Revision 1.14.2.2 2005/05/16 19:45:52 bert * Minor fix to argument structure, to reflect correct usage of ARGV_STRING items * * Revision 1.14.2.1 2005/05/12 21:16:47 bert * Initial checkin * * Revision 1.14 2005/05/09 15:32:02 bert * Change version to 2.0.05 * * Revision 1.13 2005/04/29 23:09:36 bert * Add support for -stdin option to read file list from standard input * * Revision 1.12 2005/04/26 23:49:24 bert * Update version * * Revision 1.11 2005/04/18 16:38:42 bert * Fix up file type detection code * * Revision 1.10 2005/04/06 13:26:41 bert * Fix listing option * * Revision 1.9 2005/04/05 21:52:24 bert * Add -minmax option to enable use of explicit DICOM pixel min/max information, and updated version number * * Revision 1.8 2005/03/18 19:10:31 bert * Scan coordinate and location information for validity before relying on it * * Revision 1.7 2005/03/15 17:03:34 bert * Yet another directory expansion fix (sigh) * * Revision 1.6 2005/03/14 22:51:33 bert * Actually get the directory expansion working properly... * * Revision 1.5 2005/03/14 22:25:41 bert * If a directory is specified on the file list, expand it internally. This gets around shell limitations. * * Revision 1.4 2005/03/03 20:10:14 bert * Consider patient_id and patient_name when sorting into series * * Revision 1.3 2005/03/03 18:59:15 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.2 2005/03/02 18:23:32 bert * Added mosaic sequence and bitwise options * * Revision 1.1 2005/02/17 16:38:09 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.5 2002/04/26 12:02:50 rhoge * updated usage statement for new forking defaults * * Revision 1.4 2002/04/26 11:32:48 rhoge * made forking default * * Revision 1.3 2002/03/23 13:17:53 rhoge * added support for Bourget network pushed dicom files, cleaned up * file check and read_numa4_dicom vr check/assignment * * Revision 1.2 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.1 2002/03/22 03:50:02 rhoge * new name for standalone dicom to minc converter * * Revision 1.3 2002/03/22 00:38:08 rhoge * Added progress bar, wait for children at end, updated feedback statements * * Revision 1.2 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.1 2001/12/31 17:26:21 rhoge * adding file to repository- compiles without warning and converts non-mosaic * Numa 4 files. * Will probably not work for Numa 3 files yet. * ---------------------------------------------------------------------------- */ #define GLOBAL_ELEMENT_DEFINITION /* To define elements */ #include "dcm2mnc.h" #include #if HAVE_DIRENT_H #include #endif #include /* Function Prototypes */ static int dcm_sort_function(const void *entry1, const void *entry2); static int use_the_files(int num_files, Data_Object_Info *data_info[], const char *out_dir); static void usage(void); static void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list); static int check_file_type_consistency(int num_files, char **file_list); struct globals G; #define VERSION_STRING "2.01.02 built " __DATE__ " " __TIME__ #ifndef S_ISDIR #define S_ISDIR(x) (((x) & _S_IFMT) == _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(x) (((x) & _S_IFMT) == _S_IFREG) #endif ArgvInfo argTable[] = { {NULL, ARGV_VERINFO, VERSION_STRING, NULL, NULL }, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &G.clobber, "Overwrite output files"}, {"-list", ARGV_CONSTANT, (char *) TRUE, (char *) &G.List, "Print list of series (don't create files)"}, {"-anon", ARGV_CONSTANT, (char *) TRUE, (char *) &G.Anon, "Exclude subject name from file header"}, #if HAVE_POPEN {"-cmd", ARGV_STRING, (char *) 1, (char *) &G.command_line, "Apply to output files (e.g. gzip)"}, #endif {"-verbose", ARGV_CONSTANT, (char *) LO_LOGGING, (char *) &G.Debug, "Print debugging information"}, {"-debug", ARGV_CONSTANT, (char *) HI_LOGGING, (char *) &G.Debug, "Print lots of debugging information"}, {"-nosplitecho", ARGV_CONSTANT, (char *) FALSE, (char *) &G.splitEcho, "Combine all echoes into a single file."}, {"-splitdynamic", ARGV_CONSTANT, (char *) TRUE, (char *)&G.splitDynScan, "Split dynamic scans into a separate files."}, {"-opts", ARGV_INT, (char *) 1, (char *) &G.opts, "Set debugging options"}, {"-descending", ARGV_CONSTANT, (char *) MOSAIC_SEQ_DESCENDING, (char *) &G.mosaic_seq, "Mosaic sequence is in descending slice order."}, {"-ascending", ARGV_CONSTANT, (char *) MOSAIC_SEQ_ASCENDING, (char *) &G.mosaic_seq, "Mosaic sequence is in ascending slice order."}, {"-interleaved", ARGV_CONSTANT, (char *) MOSAIC_SEQ_INTERLEAVED, (char *) &G.mosaic_seq, "Mosaic sequence is in interleaved slice order."}, {"-minmax", ARGV_CONSTANT, (char *)TRUE, (char *) &G.useMinMax, "Honor DICOM pixel minimum and pixel maximum values."}, {"-stdin", ARGV_CONSTANT, (char *)TRUE, (char *)&G.use_stdin, "Read file list from standard input."}, {"-fname", ARGV_STRING, (char *)1, (char *)&G.filename_format, "Set format for output file name."}, {"-dname", ARGV_STRING, (char *)1, (char *)&G.dirname_format, "Set format for output directory name."}, {"-usecoordinates", ARGV_CONSTANT, (char *) TRUE, (char *) &G.prefer_coords, "Derive step value from coordinates rather than slice spacing."}, {"-abort", ARGV_CONSTANT, (char *) TRUE, (char *) &G.abort_on_error, "Stop processing immediately if a file is not parsed properly."}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { int ifile; Acr_Group group_list; char **file_list; /* List of file names */ Data_Object_Info **file_info_list; int num_file_args; /* Number of files on command line */ int num_files; /* Total number of files */ string_t out_dir; /* Output directory */ string_t message; /* Generic message */ int num_files_ok; /* Actual number of DICOM/IMA files */ struct stat st; int length; int exit_status; G.mosaic_seq = MOSAIC_SEQ_UNKNOWN; /* Assume ascending by default. */ G.splitDynScan = FALSE; /* Don't split dynamic scans by default */ G.splitEcho = TRUE; /* Do split by echo by default */ G.use_stdin = FALSE; /* Do not read file list from stdin */ G.filename_format = NULL; G.dirname_format = NULL; G.minc_history = time_stamp(argc, argv); /* Create minc history string */ G.prefer_coords = FALSE; G.abort_on_error = FALSE; G.pname = argv[0]; /* get program name */ /* Get the input parameters and file names. */ if (ParseArgv(&argc, argv, argTable, 0)) { usage(); } if (argc < 2) { usage(); } if (G.List) { num_file_args = argc - 1; /* Assume no directory given. */ } else { num_file_args = argc - 2; /* Assume last arg is directory. */ strcpy(out_dir, argv[argc - 1]); /* make sure path ends with slash */ length = strlen(out_dir); if (out_dir[length - 1] != '/') { out_dir[length++] = '/'; out_dir[length++] = '\0'; } if (stat(out_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { fprintf(stderr, "The final argument, '%s', is not a directory\n", out_dir); exit(EXIT_FAILURE); } } /* Get space for file lists */ /* Allocate the array of pointers used to implement the * list of filenames. */ file_list = malloc(1 * sizeof(char *)); CHKMEM(file_list); /* Go through the list of files, expanding directories where they * are encountered... */ num_files = 0; for (ifile = 0 ; ifile < num_file_args; ifile++) { #if HAVE_DIRENT_H if (stat(argv[ifile + 1], &st) == 0 && S_ISDIR(st.st_mode)) { DIR *dp; struct dirent *np; char *tmp_str; if (G.Debug) { printf("Expanding directory '%s'\n", argv[ifile + 1]); } length = strlen(argv[ifile + 1]); dp = opendir(argv[ifile + 1]); if (dp != NULL) { while ((np = readdir(dp)) != NULL) { /* Generate the full path to the file. */ tmp_str = malloc(length + strlen(np->d_name) + 2); strcpy(tmp_str, argv[ifile + 1]); if (tmp_str[length-1] != '/') { tmp_str[length] = '/'; tmp_str[length+1] = '\0'; } strcat(&tmp_str[length], np->d_name); if (stat(tmp_str, &st) == 0 && S_ISREG(st.st_mode)) { file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = tmp_str; } else { free(tmp_str); } } closedir(dp); } else { fprintf(stderr, "Error opening directory '%s'\n", argv[ifile + 1]); } } else { file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = strdup(argv[ifile + 1]); } #else file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = strdup(argv[ifile + 1]); #endif } if (G.use_stdin) { char linebuf[1024]; char *p; while (fgets(linebuf, sizeof(linebuf), stdin) != NULL) { /* Strip off newline at end of string. */ for (p = linebuf; *p != '\0'; p++) { if (*p == '\n') { *p = '\0'; } } if (strlen(linebuf) != 0) { file_list = realloc(file_list, (num_files + 1) * sizeof(char *)); file_list[num_files++] = strdup(linebuf); } } } file_info_list = malloc(num_files * sizeof(*file_info_list)); CHKMEM(file_info_list); /* figure out what kind of files we have - * supported types are: * * IMA (Siemens .ima format - Numaris 3.5) * N4DCM (Siemens DICOM - Numaris 4) * * if not all same type, return an error * * we start by assuming N4DCM with no offset - we find that the * file is IMA or has an offset (the 128 byte + DICM offset seen * on Syngo CD's and exports) then the appropriate flag will be * set. */ printf("Checking file types...\n"); if (check_file_type_consistency(num_files, file_list) < 0) { exit(EXIT_FAILURE); } /* Now loop over all files, getting basic info on each */ num_files_ok = 0; for (ifile = 0; ifile < num_files; ifile++) { char *cur_fname_ptr = file_list[ifile]; if (!G.Debug) { sprintf(message, "Parsing %d files", num_files); progress(ifile, num_files, message); } if (G.file_type == IMA) { group_list = siemens_to_dicom(cur_fname_ptr, ACR_IMAGE_GID - 1); } else { /* read up to but not including pixel data */ group_list = read_numa4_dicom(cur_fname_ptr, ACR_IMAGE_GID - 1); } if (group_list == NULL) { /* This file appears to be invalid - it is probably a dicomdir * file or some other stray junk in the directory. */ printf("Skipping file %s, which is not in the expected format.\n", cur_fname_ptr); free(cur_fname_ptr); } else { /* Copy it back to the (possibly earlier) position in the real * file list. */ file_list[num_files_ok] = cur_fname_ptr; /* allocate space for the current entry to file_info_list */ file_info_list[num_files_ok] = malloc(sizeof(*file_info_list[0])); CHKMEM(file_info_list[num_files_ok]); file_info_list[num_files_ok]->file_index = num_files_ok; parse_dicom_groups(group_list, file_info_list[num_files_ok]); /* put the file name into the info list */ file_info_list[num_files_ok]->file_name = strdup(file_list[num_files_ok]); /* Delete the group list now that we're done with it */ acr_delete_group_list(group_list); num_files_ok++; } } /* end of loop over files to get basic info */ if (G.Debug) { printf("Using %d files\n", num_files_ok); } num_files = num_files_ok; printf("Sorting %d files... ", num_files); /* sort the files into series based on acquisition number */ qsort(file_info_list, num_files, sizeof(file_info_list[0]), dcm_sort_function); /* If DEBUG, print a list of all files. */ if (G.Debug) { printf("\n"); for (ifile = 0; ifile < num_files; ifile++) { Data_Object_Info *info = file_info_list[ifile]; char *fname; if ((ifile % 16) == 0) { printf("%-4s %-32.32s %-14s %-8s %-8s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-5s %-16s\n", "num", "filename", "studyid", "serialno", "acq", "nec", "iec", "ndy", "idy", "nsl", "isl", "acol", "rcol", "mrow", "img#", "seq"); } /* Print out info about file. Truncate the name if necessary. */ fname = info->file_name; if (strlen(fname) > 32) { fname += strlen(fname) - 32; } printf("%4d %-32.32s %14.6f %8d %8d %4d %4d %4d %4d %4d %4d %4d %4d %4d %5d %-16s\n", ifile, fname, info->study_id, info->scanner_serialno, info->acq_id, info->num_echoes, info->echo_number, info->num_dyn_scans, info->dyn_scan_number, info->num_slices_nominal, info->slice_number, info->acq_cols, info->rec_cols, info->num_mosaic_rows, info->global_image_number, info->sequence_name); } } printf("Done sorting files.\n"); /* Loop over files, processing by acquisition */ if (G.List) { printf("Listing files by series...\n"); } else { printf("Processing files, one series at a time...\n"); } exit_status = use_the_files(num_files, file_info_list, out_dir); if (G.List) { printf("Done listing files.\n"); } else { printf("Done processing files.\n"); } free_list(num_files, file_list, file_info_list); free(file_list); free(file_info_list); exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i = 0; i < num_files; i++) { if (file_list[i] != NULL) { free(file_list[i]); } if (file_info_list[i] != NULL) { free(file_info_list[i]->file_name); free(file_info_list[i]); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : dcm_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two dcm series numbers @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ static int dcm_sort_function(const void *entry1, const void *entry2) { const Data_Object_Info **file_info_list1 = (const Data_Object_Info **) (uintptr_t) entry1; const Data_Object_Info **file_info_list2 = (const Data_Object_Info **) (uintptr_t) entry2; // make a sort-able session ID number: date.time double session1 = (*file_info_list1)->study_date + (*file_info_list1)->study_time / 1e6; double session2 = (*file_info_list2)->study_date + (*file_info_list2)->study_time / 1e6; // series index int series1 = (*file_info_list1)->acq_id; int series2 = (*file_info_list2)->acq_id; // frame index int frame1 = (*file_info_list1)->dyn_scan_number; int frame2 = (*file_info_list2)->dyn_scan_number; // image index int image1 = (*file_info_list1)->global_image_number; int image2 = (*file_info_list2)->global_image_number; int slice1 = (*file_info_list1)->slice_number; int slice2 = (*file_info_list2)->slice_number; if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (frame1 < frame2) return -1; else if (frame1 > frame2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else if (slice1 < slice2) return -1; else if (slice1 > slice2) return 1; /* Last chance - if all else is equal, sort by the file names. */ else return strcmp((*file_info_list1)->file_name, (*file_info_list2)->file_name); } static void usage(void) { fprintf(stderr, "\nUsage: %s [options] file1 file2 file3 ... destdir\n", G.pname); fprintf(stderr, "\n"); fprintf(stderr,"Files are named according to the following convention:\n\n"); fprintf(stderr," Directory: lastname_firstname_yyyymmdd_hhmmss/\n"); fprintf(stderr," Files: lastname_firstname_yyyymmdd_hhmmss_series_modality.mnc\n\n"); exit(EXIT_FAILURE); } static int use_the_files(int num_files, Data_Object_Info *di_ptr[], const char *out_dir) { int ifile; int acq_num_files; const char **acq_file_list; int *used_file; int *acq_file_index; double cur_study_id; int cur_acq_id; int cur_rec_num; int cur_image_type; int cur_echo_number; int cur_dyn_scan_number; string_t cur_patient_name; string_t cur_patient_id; string_t cur_sequence_name; int exit_status; const char *output_file_name; string_t file_prefix; string_t string; FILE *fp; int trust_location; int trust_coord; int user_opts; /* Options as set by user. We may override.. */ if (out_dir != NULL) { /* if an output directory name has been * provided on the command line */ if (G.Debug) { printf("Using directory '%s'\n", out_dir); } strcpy(file_prefix, out_dir); } else { file_prefix[0] = '\0'; } if (G.Debug) { /* debugging */ printf("file_prefix: [%s]\n", file_prefix); } /* Allocate space for acquisition file list. */ acq_file_list = malloc(num_files * sizeof(*acq_file_list)); CHKMEM(acq_file_list); acq_file_index = malloc(num_files * sizeof(*acq_file_index)); CHKMEM(acq_file_index); used_file = malloc(num_files * sizeof(*used_file)); CHKMEM(used_file); for (ifile = 0; ifile < num_files; ifile++) { used_file[ifile] = FALSE; } for (;;) { /* Loop through files, looking for an acquisition * * file groups should already have been sorted into acquisitions * in calling program * * this code is in a `forever' loop because we loop over multiple * acquisitions until all of the files are used up. */ acq_num_files = 0; for (ifile = 0; ifile < num_files; ifile++) { /* If already marked used (can this happen???), we've already * written the file to an output somewhere. */ if (used_file[ifile]) { continue; } if (acq_num_files == 0) { /* found first file: set all current attributes like * study id, acq id, rec num(?), image type, echo * number, dyn scan number, flag for multiple echoes, * flag for multiple time points the flag input file * as `used' */ cur_study_id = di_ptr[ifile]->study_id; cur_acq_id = di_ptr[ifile]->acq_id; cur_rec_num = di_ptr[ifile]->rec_num; cur_image_type = di_ptr[ifile]->image_type; cur_echo_number = di_ptr[ifile]->echo_number; cur_dyn_scan_number = di_ptr[ifile]->dyn_scan_number; strcpy(cur_patient_name, di_ptr[ifile]->patient_name); strcpy(cur_patient_id, di_ptr[ifile]->patient_id); strcpy(cur_sequence_name, di_ptr[ifile]->sequence_name); used_file[ifile] = TRUE; } /* otherwise check if attributes of the new input file match those * of the current output context and flag input file as `used' */ else if ((di_ptr[ifile]->study_id == cur_study_id) && (di_ptr[ifile]->acq_id == cur_acq_id) && (di_ptr[ifile]->rec_num == cur_rec_num) && (di_ptr[ifile]->image_type == cur_image_type) && (di_ptr[ifile]->echo_number == cur_echo_number || !G.splitEcho) && (di_ptr[ifile]->dyn_scan_number == cur_dyn_scan_number || !G.splitDynScan) && !strcmp(cur_patient_name, di_ptr[ifile]->patient_name) && !strcmp(cur_patient_id, di_ptr[ifile]->patient_id)) { used_file[ifile] = TRUE; } if (used_file[ifile]) { /* if input file is flagged as `used', then add its index to the list of files for this acquisition (and increment counter) */ acq_file_list[acq_num_files] = di_ptr[ifile]->file_name; acq_file_index[acq_num_files] = ifile; acq_num_files++; } } /* If no files were added to this acquisition, it implies that * all files have been processed. */ if (acq_num_files == 0) { break; /* All done!!! */ } /* Use the files for this acquisition */ /* Print out the file names if we are debugging. */ if (G.Debug || G.List) { printf("\nSeries %4d %20s %20s (%4d files):\n", cur_acq_id, cur_patient_name, di_ptr[acq_file_index[0]]->protocol_name, acq_num_files); for (ifile = 0; ifile < acq_num_files; ifile++) { printf(" %s\n", di_ptr[acq_file_index[ifile]]->file_name); } if (G.List) { continue; } } /* Do some sanity checks on the acquisition. In particular, we * verify that the coordinate and/or slice location information * looks reliable. */ trust_location = 1; trust_coord = 1; for (ifile = 0; ifile < acq_num_files; ifile++) { int jfile; int ix = acq_file_index[ifile]; if (!di_ptr[ix]->coord_found) { trust_coord = 0; } for (jfile = ifile + 1; jfile < acq_num_files; jfile++) { int jx = acq_file_index[jfile]; if (NEARLY_EQUAL(di_ptr[ix]->slice_location, di_ptr[jx]->slice_location)) { trust_location = 0; } } } /* We also check whether the acquisition number (0x0020, 0x0012) * and image number (0x0020, 0x0013) are informative or not. * They are sometimes absent, constant, or otherwise strange. * Determining this ahead of time helps us make good decisions * about how to treat these fields later. */ G.max_acq_num = INT_MIN; G.min_acq_num = INT_MAX; G.max_img_num = INT_MIN; G.min_img_num = INT_MAX; for (ifile = 0; ifile < acq_num_files; ifile++) { int ix = acq_file_index[ifile]; if (di_ptr[ix]->dyn_scan_number < G.min_acq_num) { G.min_acq_num = di_ptr[ix]->dyn_scan_number; } if (di_ptr[ix]->dyn_scan_number > G.max_acq_num) { G.max_acq_num = di_ptr[ix]->dyn_scan_number; } if (di_ptr[ix]->global_image_number < G.min_img_num) { G.min_img_num = di_ptr[ix]->global_image_number; } if (di_ptr[ix]->global_image_number > G.max_img_num) { G.max_img_num = di_ptr[ix]->global_image_number; } } user_opts = G.opts; if (!trust_coord) { printf("WARNING: Image coordinates absent or incomplete.\n"); if (!trust_location) { printf("WARNING: Slice location is untrustworthy.\n"); G.opts |= OPTS_NO_LOCATION; } } if (G.min_acq_num == G.max_acq_num) { printf("WARNING: Acquisition number is not informative.\n"); } else { int ix = acq_file_index[0]; if (G.max_acq_num == di_ptr[ix]->num_dyn_scans) { /* Acquisition number is per scan (e.g. time). */ printf("WARNING: Acquisition number is per scan.\n"); } else if (G.max_acq_num == di_ptr[ix]->num_slices_nominal * di_ptr[ix]->num_dyn_scans) { printf("WARNING: Acquisition number is global.\n"); } else { printf("WARNING: Acquisition number is a mystery.\n"); } } if (G.min_img_num == G.max_img_num) { /* Acquisition number is uninformative. */ printf("WARNING: Image number is not informative.\n"); } else { int ix = acq_file_index[0]; if (G.max_img_num == di_ptr[ix]->num_slices_nominal) { printf("WARNING: Image number is per slice.\n"); } else if (G.max_img_num == di_ptr[ix]->num_slices_nominal * di_ptr[ix]->num_dyn_scans) { printf("WARNING: Image number is global.\n"); } else { printf("WARNING: Image number is a mystery.\n"); } } if (G.min_acq_num < 0 || G.min_acq_num > 1) { printf("WARNING: Minimum acquisition number is %d\n", G.min_acq_num); } if (G.min_img_num < 0 || G.min_img_num > 1) { printf("WARNING: Minimum image number is %d\n", G.min_img_num); } printf("INFO: Acquisition number ranges from %d to %d\n", G.min_acq_num, G.max_acq_num); printf("INFO: Image number ranges from %d to %d\n", G.min_img_num, G.max_img_num); /* Create minc file */ exit_status = dicom_to_minc(acq_num_files, acq_file_list, NULL, G.clobber, file_prefix, &output_file_name); G.opts = user_opts; if (exit_status != EXIT_SUCCESS) continue; /* Print log message */ if (G.Debug) { printf("Created minc file %s.\n", output_file_name); } #if HAVE_POPEN /* Invoke a command on the file (if requested) and get the * returned file name */ if (G.command_line != NULL && *G.command_line != '\0') { sprintf(string, "%s %s", G.command_line, output_file_name); printf("-Applying command '%s' to output file... ", G.command_line); fflush(stdout); if ((fp = popen(string, "r")) != NULL) { char pipe_output_name[256]; fscanf(fp, "%s", pipe_output_name); if (pclose(fp) != EXIT_SUCCESS) { fprintf(stderr, "Error executing command\n \"%s\"\n", string); } else if (G.Debug) { printf("Executed command \"%s\",\nproducing file %s.\n", string, pipe_output_name); } } else { fprintf(stderr, "Error executing command \"%s\"\n", string); } printf("Done.\n"); } #endif /* HAVE_POPEN */ } /* Free acquisition file list */ free(acq_file_list); free(acq_file_index); free(used_file); return exit_status; } static int is_cdexport_file(const char *fullname) { FILE *fp; char tst_str[DICM_MAGIC_SIZE+1]; int result = 0; if ((fp = fopen(fullname, "rb")) == NULL) { fprintf(stderr, "Error opening file %s!\n", fullname); } else { fseek(fp, DICM_MAGIC_OFFS, SEEK_SET); fread(tst_str, 1, DICM_MAGIC_SIZE, fp); tst_str[DICM_MAGIC_SIZE] = '\0'; if (!strcmp(tst_str, DICM_MAGIC_STR)) { result = 1; } fclose(fp); } return (result); } static int is_ima_file(const char *fullname) { FILE *fp; char mfg_str[IMA_MAGIC_SIZE+1]; int result = 0; if ((fp = fopen(fullname, "rb")) == NULL) { fprintf(stderr, "Error opening file %s!\n", fullname); } else { fseek(fp, IMA_MAGIC_OFFS, SEEK_SET); fread(mfg_str, 1, IMA_MAGIC_SIZE, fp); /* We only deal with Siemens IMA files - not sure any other kinds * exist, frankly. */ if (!memcmp(mfg_str, IMA_MAGIC_STR, IMA_MAGIC_SIZE)) { result = 1; } fclose(fp); } return (result); } /* _very_ limited test for "binary-ness" of a file. This is just to keep * text files and other junk present in a directory from screwing up our * file type detection. */ static int is_binary_file(const char *fullname) { FILE *fp; int result = 0; int i; if ((fp = fopen(fullname, "rb")) == NULL) { fprintf(stderr, "Error opening file %s!\n", fullname); } else { /* This is an extremely trivial test for binary-ness. Basically, we * look at the first 512 bytes, and if there aren't lots of unprintable * characters, we assume it is not a binary file. */ for (i = 0; i < 128; i++) { int cc = getc(fp); if (cc == -1) { result = 0; /* too short!! */ break; } if (!isprint(cc) && cc != '\n' && cc != '\r') { result++; } } fclose(fp); } return (result > 3); /* Binary if more than 3 unprintables */ } static int check_file_type_consistency(int num_files, char *file_list[]) { int i; const char *fn_ptr; int n4_offset = 0; for (i = 0; i < num_files; i++) { fn_ptr = file_list[i]; /* Numaris 4 DICOM CD/Export file? if so, bytes 128-131 will * contain the string `DICM' with no null termination. */ if (is_cdexport_file(fn_ptr)) { if (G.file_type == UNDEF) { G.file_type = N4DCM; n4_offset = 1; printf("File %s appears to be DICOM (CD/Export).\n", fn_ptr); } else if (G.file_type != N4DCM || n4_offset != 1) { printf("MISMATCH: File %s appears to be DICOM (CD/Export).\n", fn_ptr); return (-1); } } else if (is_ima_file(fn_ptr)) { if (G.file_type == UNDEF) { G.file_type = IMA; printf("File %s appears to be Siemens IMA.\n", fn_ptr); } else if (G.file_type != IMA) { printf("MISMATCH: File %s appears to be Siemens IMA.\n", fn_ptr); return (-1); } } else if (is_binary_file(fn_ptr)) { if (G.file_type == UNDEF) { G.file_type = N4DCM; n4_offset = 0; printf("File %s appears to be standard DICOM.\n", fn_ptr); } else if (G.file_type != N4DCM || n4_offset != 0) { printf("MISMATCH: File %s appears to be standard DICOM.\n", fn_ptr); return (-1); } } } return (0); } /* compare two floating-point numbers */ int fcmp(double x, double y, double delta) { return ((fabs(x - y) / ((x == 0.0) ? 1.0 : fabs(x))) < delta); } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dicom_to_minc.c0000644000175000000620000035074212574624760022314 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_to_minc.c @DESCRIPTION: Code to convert a list of DICOM files to minc format. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_to_minc.c,v $ * Revision 1.30 2010-11-23 23:30:50 claude * dcm2mnc: fixed seg fault bug (Claude) and added b-matrix (Ilana) * * Revision 1.29 2010-06-23 13:21:05 rotor * * changed H5Acreate2 calls back to the H5Acreate macro * * Revision 1.28 2008/08/12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 1.27 2008/01/17 06:20:54 stever * * conversion/dcm2mnc/dicom_to_minc.c (copy_element_properties): * Change return type from int to void; no callers require a return value. * * * conversion/micropet/upet2mnc.c (main): Return 0 at end of function. * * Revision 1.26 2008/01/17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.25 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.24 2008/01/11 07:17:07 stever * Remove unused variables. * * Revision 1.23 2007/11/23 20:28:23 ilana * condition on picking between DICOM slice spacing and coordinate spacing was wrong (extra ! in if statement) * * Revision 1.22 2007/08/13 16:34:52 ilana * mods to handle naming scheme of more diffusion sequences * * Revision 1.21 2007/06/08 20:28:57 ilana * added several fields to mincheader (dicom elements and found in ASCONV header) * * Revision 1.20 2007/05/30 15:17:34 ilana * fix so that diffusion images all written into 1 4d volume, gradient directions and bvalues are written to mincheader, some fixes for TIM diffusion images * * Revision 1.19 2006/05/11 14:45:14 bert * Fix endian-ness issues when parsing Siemens proprietary fields * * Revision 1.18 2006/04/09 15:39:04 bert * Improve support for DTI * * Revision 1.17 2006/02/09 20:54:29 bert * More changes to dcm2mnc * * Revision 1.16 2005/12/13 17:31:13 bert * 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. * * Revision 1.15 2005/08/26 21:25:54 bert * Latest changes ported from 1.0 branch * * Revision 1.13.2.5 2005/08/18 18:18:35 bert * Implement the -usecoordinates option, also some minor cleanup and fixes for some warning messages. * * Revision 1.13.2.4 2005/08/18 16:38:44 bert * Minor updates for dealing w/older numaris data * * Revision 1.13.2.3 2005/07/14 16:47:55 bert * Handle additional classes of Numa3 mosaics by using the Siemens 'base raw matrix size' element * * Revision 1.13.2.2 2005/06/20 22:01:15 bert * Add basic support for multiframe DICOM images * * Revision 1.13.2.1 2005/05/12 21:16:47 bert * Initial checkin * * Revision 1.13 2005/05/09 15:33:20 bert * Fix handing of descending mosaic sequences; don't print GEMS field missing warnings by default * * Revision 1.12 2005/04/28 17:38:06 bert * Insert and retrieve width information when sorting a dimension * * Revision 1.11 2005/04/21 22:31:29 bert * Copy SPI_Magnetic_field_strength to standard field in copy_spi_to_acr(); Relax some tests to avoid spurious warnings * * Revision 1.10 2005/04/20 23:25:50 bert * Add copy_spi_to_acr() function, minor name and comment changes * * Revision 1.9 2005/04/20 17:48:16 bert * Copy SPI_Number_of_slices_nominal to ACR_Image_in_acquisition for Siemens * * Revision 1.8 2005/04/18 21:04:25 bert * Get rid of old is_reversed behavior, always use most natural sort of the image. Also tried to fix listing function somewhat. * * Revision 1.7 2005/04/05 21:55:33 bert * Update handling of Siemens ASCCONV to reflect value of uc2DInterpolation field for mosaic images. Additional cleanup of mosaic code and minor tweak to suppress GEMS warning message for PET data. * * Revision 1.6 2005/03/29 20:20:42 bert * Add checks for GE Medical Systems proprietary files * * Revision 1.5 2005/03/14 22:26:40 bert * Dump the entire coordinate array for each MRI dimension after sorting. Also detect GE scans. * * Revision 1.4 2005/03/13 19:35:11 bert * Lots of changes for dealing with some proprietary Philips stuff * * Revision 1.3 2005/03/03 18:59:15 bert * Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) * * Revision 1.2 2005/03/02 20:16:24 bert * Latest changes and cleanup * * Revision 1.1 2005/02/17 16:38:10 bert * Initial checkin, revised DICOM to MINC converter * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.18 2002/09/26 15:24:33 rhoge * Before was only skipping time sort for multi-slice N4 mosaics. Turns out * this was also causing failure on single-slice scans. * Changed slices>1 to slices>0 so that now N4 dicom scans never get sorted * on their (apparently nonsensical) time value. The if statement should * really be reworked, and should keep an eye on this. Seems like EPI * time series sequences never sort properly on 'time'. * * Revision 1.17 2002/09/25 17:25:43 rhoge * changed void's to int's * * Revision 1.16 2002/05/08 19:32:40 rhoge * fixed handling of diffusion scans with separate series for each average * * Revision 1.15 2002/05/01 21:29:34 rhoge * removed MrProt from minc header - encountered files with large strings, * causing seg faults * * Revision 1.14 2002/04/30 12:36:35 rhoge * fixes to handle current (and hopefully final) diffusion sequence * * Revision 1.13 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.12 2002/04/08 03:40:56 rhoge * fixed mosaic extraction for non-square scans and 3D scans. * added some new dicom elements * * Revision 1.11 2002/03/27 19:38:08 rhoge * small comment change * * Revision 1.10 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.9 2002/03/23 13:17:53 rhoge * added support for Bourget network pushed dicom files, cleaned up * file check and read_numa4_dicom vr check/assignment * * Revision 1.8 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.7 2002/03/21 13:31:56 rhoge * updated comments * * Revision 1.6 2002/03/19 22:10:16 rhoge * removed time sorting for N4DCM mosaics - time is random for mosaics * * Revision 1.5 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.4 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.3 2000/12/14 21:37:11 rhoge * log message cleanup * * Revision 1.2 2000/12/14 21:36:22 rhoge * changes to restore measurement loop support that was broken by changes * to provide acquisition loop support * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * -now support Siemens acquisition loop scans with and without correction * on sending side * * Revision 6.1 1999/10/29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 "dcm2mnc.h" const char *World_Names[WORLD_NDIMS] = { "X", "Y", "Z" }; const char *Volume_Names[VOL_NDIMS] = { "Slice", "Row", "Column" }; const char *Mri_Names[MRI_NDIMS] = {"Slice", "Echo", "Time", "Phase", "ChmSh"}; /* Private structure definitions. */ /* multi-image (mosaic) info */ typedef struct { int packed; mosaic_seq_t mosaic_seq; int size[2]; int big[2]; int grid[2]; int pixel_size; Acr_Element big_image; Acr_Element small_image; int sub_images; int slice_count; double normal[WORLD_NDIMS]; double step[WORLD_NDIMS]; double position[WORLD_NDIMS]; int slice_inverted; } Mosaic_Info; /* DICOM Multiframe information. NOTE: This represents at best a very * partial implementation of the DICOM multiframe specification, barely * adequate for conversion of basic 3D files. More work is needed to * support dynamic scans and other more complex objects. */ typedef struct { int frame_count; int frame_size; Acr_Element big_image; Acr_Element sub_image; double normal[WORLD_NDIMS]; double step[WORLD_NDIMS]; double position[WORLD_NDIMS]; } Multiframe_Info; /* Structure for sorting dimensions */ typedef struct { int identifier; int original_index; double value; double width; } Sort_Element; /* Private function definitions */ static int mosaic_init(Acr_Group, Mosaic_Info *, int); static void mosaic_cleanup(Mosaic_Info *); static int mosaic_insert_subframe(Acr_Group, Mosaic_Info *, int, int); /* DICOM Multiframe conversion functions (see NOTE: above) */ static void multiframe_init(Acr_Group, Multiframe_Info *, int); static void multiframe_cleanup(Multiframe_Info *); static void multiframe_insert_subframe(Acr_Group, Multiframe_Info *, int, int); static void free_info(General_Info *gi_ptr, File_Info *fi_ptr, int num_files); static int dimension_sort_function(const void *v1, const void *v2); static void sort_dimensions(General_Info *gi_ptr); static int prot_find_string(Acr_Element Protocol, const char *name, char *value); static char *dump_protocol_text(Acr_Element Protocol); /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_to_minc @INPUT : num_files - number of image files file_list - list of file names minc_file - name of minc file to create, or NULL to make one up. clobber - if TRUE, then open the output with NC_CLOBBER file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : EXIT_SUCCESS if no error, EXIT_FAILURE on error. @DESCRIPTION: Routine to convert a list of Siemens dicom files to minc format. @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int dicom_to_minc(int num_files, const char *file_list[], const char *minc_file, int clobber, const char *file_prefix, const char **output_file_name) { Acr_Group group_list; /* List of ACR/NEMA groups & elements */ File_Info *fi_ptr; /* Array of per-file information */ General_Info gi; /* General (common) DICOM file information */ int max_group; /* Maximum group number to read */ Image_Data image; /* Actual image data */ int icvid; /* MINC Image Conversion Variable */ int ifile; /* File index */ Mri_Index imri; /* MRI axis index */ const char *out_file_name; /* Output MINC filename */ int isep; /* Loop counter */ const Loop_Type loop_type = NONE; /* MINC loop type always none for now */ int subimage; /* Loop counter for MOSAIC images per file */ int iimage; /* Loop counter for all files/images */ int num_images; /* Total number of slices (>= # files) */ Mosaic_Info mi; /* Mosaic (multi-image) information */ Multiframe_Info mfi; /* Multiframe information */ int n_slices_in_file; /* Number of slices in file */ gi.subimage_type = SUBIMAGE_TYPE_NONE; /* Allocate space for the file information */ fi_ptr = malloc(num_files * sizeof(*fi_ptr)); CHKMEM(fi_ptr); num_images = num_files; /* Last group needed for first pass */ max_group = ACR_IMAGE_GID - 1; /* Add all control characters as numeric array separators to handle * odd behaviour with Siemens dicom files */ for (isep = 0; isep < 31; isep++) { acr_element_numeric_array_separator(isep); } /* Initialize some values for general info */ gi.initialized = FALSE; gi.group_list = NULL; gi.num_files = num_files; for (imri = 0; imri < MRI_NDIMS; imri++) { gi.cur_size[imri] = -1; gi.indices[imri] = NULL; gi.coordinates[imri] = NULL; gi.widths[imri] = NULL; } /* Loop through file list getting information * (note that we have to duplicate the handling * of multiple images per file in this loop * to accumulate dimension sizes correctly) * need separate counter for images, since some files may * contain more than one image! */ iimage = 0; for (ifile = 0; ifile < num_files; ifile++) { if (G.Debug >= HI_LOGGING) { printf("\nFile %s\n", file_list[ifile]); } if (!G.Debug) { progress(ifile, num_files, "-Parsing series info"); } /* Read the file */ if (G.file_type == N4DCM) { group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (G.file_type == IMA) { group_list = siemens_to_dicom(file_list[ifile], max_group); } if (group_list == NULL) { fprintf(stderr, "Error parsing file '%s' on 1st pass.\n", file_list[ifile]); exit(-1); } if (G.opts & OPTS_NO_MOSAIC) { n_slices_in_file = 1; } else { n_slices_in_file = acr_find_int(group_list, EXT_Slices_in_file, 1); } /* initialize big and small images, if mosaic */ if (n_slices_in_file > 1) { gi.subimage_type = SUBIMAGE_TYPE_MOSAIC; mosaic_init(group_list, &mi, FALSE); num_images += n_slices_in_file - 1; fi_ptr = realloc(fi_ptr, num_images * sizeof(*fi_ptr)); CHKMEM(fi_ptr); } else { /* See if we have an DICOM multiframe image, or at least a * reasonable facsimile of one such as the files produced * by the Shimadzu PET scanner. * * It is probably not correct to rely on the presence of the * "Number of Frames" (0x0028, 0x0008) field, but the Shimadzu * multiframe implementation is far from complete and does not * seem to implement any of the other components of the * specification. */ n_slices_in_file = acr_find_int(group_list, ACR_Number_of_frames, 1); if (n_slices_in_file > 1) { gi.subimage_type = SUBIMAGE_TYPE_MULTIFRAME; multiframe_init(group_list, &mfi, FALSE); num_images += n_slices_in_file - 1; fi_ptr = realloc(fi_ptr, num_images * sizeof(*fi_ptr)); CHKMEM(fi_ptr); } } /* loop over subimages in mosaic */ for (subimage = 0; subimage < n_slices_in_file; subimage++) { /* Modify the group list for this image if mosaic or multiframe */ switch (gi.subimage_type) { case SUBIMAGE_TYPE_MOSAIC: mosaic_insert_subframe(group_list, &mi, subimage, FALSE); break; case SUBIMAGE_TYPE_MULTIFRAME: multiframe_insert_subframe(group_list, &mfi, subimage, FALSE); break; default: break; } /* Get file-specific information */ get_file_info(group_list, &fi_ptr[iimage], &gi, file_list[ifile]); //ilana debug /*int acq=acr_find_int(group_list, ACR_Acquisition, 1); printf("****WE HAVE ACQUISITION# %i",acq);*/ /* increment iimage here */ iimage++; } /* Delete the group list */ acr_delete_group_list(group_list); /* cleanup mosaic struct if used */ switch (gi.subimage_type) { case SUBIMAGE_TYPE_MOSAIC: mosaic_cleanup(&mi); break; case SUBIMAGE_TYPE_MULTIFRAME: multiframe_cleanup(&mfi); break; default: break; } } /* Sort the dimensions */ sort_dimensions(&gi); /* Create the output file */ if (gi.initialized) { icvid = create_minc_file(minc_file, clobber, &gi, file_prefix, &out_file_name, loop_type); } if (output_file_name != NULL) { *output_file_name = out_file_name; } /* Check that we found the general info and that the minc file was * created okay */ if ((!gi.initialized) || (icvid == MI_ERROR)) { if (gi.initialized) { fprintf(stderr, "Error creating MINC file %s.\n", out_file_name); } free_info(&gi, fi_ptr, num_files); free(fi_ptr); return EXIT_FAILURE; } if (G.Debug) { printf("Writing %d images to MINC file\n", num_files); } /* Last group needed for second pass * we now have to read up to and including the image, * since image pointers are needed in mosaic_init */ max_group = ACR_IMAGE_GID; /* Loop through the files again and put images into the minc file */ iimage = 0; for (ifile = 0; ifile < num_files; ifile++) { if (!G.Debug) { progress(ifile, num_files, "-Creating minc file"); } /* Check that we have a valid file */ if (!fi_ptr[ifile].valid) { printf("WARNING: file %s was marked invalid\n", file_list[ifile]); continue; } /* Read the file */ if (G.file_type == N4DCM) { group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (G.file_type == IMA) { group_list = siemens_to_dicom(file_list[ifile], max_group); } if (group_list == NULL) { if (G.abort_on_error) { fprintf(stderr, "ERROR: parsing file '%s' during 2nd pass.\n", file_list[ifile]); exit(-1); } else { fprintf(stderr, "WARNING: parsing file '%s' during 2nd pass.\n", file_list[ifile]); fi_ptr[ifile].valid = FALSE; continue; } } /* initialize big and small images, if mosaic */ switch (gi.subimage_type) { case SUBIMAGE_TYPE_MOSAIC: mosaic_init(group_list, &mi, TRUE); break; case SUBIMAGE_TYPE_MULTIFRAME: multiframe_init(group_list, &mfi, TRUE); break; default: break; } /* loop over subimages in mosaic */ for (subimage = 0; subimage < n_slices_in_file; subimage++) { /* Modify the group list for this image if mosaic */ switch (gi.subimage_type) { case SUBIMAGE_TYPE_MOSAIC: mosaic_insert_subframe(group_list, &mi, subimage, TRUE); break; case SUBIMAGE_TYPE_MULTIFRAME: multiframe_insert_subframe(group_list, &mfi, subimage, TRUE); break; default: break; } /* Get image */ get_dicom_image_data(group_list, &image); /* Save the image and any other information */ save_minc_image(icvid, &gi, &fi_ptr[iimage], &image); /* Free the image data */ if (image.data != NULL) { free(image.data); image.data = NULL; } /* increment image counter */ iimage++; } /* Delete the group list */ acr_delete_group_list(group_list); /* cleanup mosaic struct if used */ switch (gi.subimage_type) { case SUBIMAGE_TYPE_MOSAIC: mosaic_cleanup(&mi); break; case SUBIMAGE_TYPE_MULTIFRAME: multiframe_cleanup(&mfi); break; default: break; } } /* Close the output file */ close_minc_file(icvid); /* Free the gi and fi_ptr stuff */ free_info(&gi, fi_ptr, num_files); free(fi_ptr); return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_std_dicom @INPUT : filename - name of siemens Numaris 4 `dicom' file to read max_group - maximum group number to read @OUTPUT : (none) @RETURNS : group list read in from file @DESCRIPTION: Routine to read in a group list from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 18, 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group read_std_dicom(const char *filename, int max_group) { FILE *fp; Acr_File *afp; Acr_Group group_list; int status; /* Open the file */ fp = fopen(filename, "rb"); if (fp == NULL) { return NULL; } /* Connect to input stream */ afp = acr_file_initialize(fp, 0, acr_stdio_read); if (afp == NULL) { return NULL; } acr_set_ignore_errors(afp, 1); /* ignore protocol errors */ if (acr_test_dicom_file(afp) != ACR_OK) { return NULL; } // Read in group list status = acr_input_group_list(afp, &group_list, max_group); if (status != ACR_END_OF_INPUT && status != ACR_OK) { return NULL; } // Close the file acr_file_free(afp); fclose(fp); return (group_list); } /* Return non-zero if this image looks like it is a mosaic image. */ int is_siemens_mosaic(Acr_Group group_list) { Acr_String str_ptr; str_ptr = acr_find_string(group_list, ACR_Image_type, ""); if (strstr(str_ptr, "MOSAIC") != NULL) return 1; /* slam dunk, this is mosaic data */ /* For some reason, some Localizer scans look like MOSAICs in that * they appear to be upsampled in such a way that their * rows/columns are larger than the acquisition matrix. The * details of this are probably buried in the Siemens ASCCONV dump * and therefore I have not been able to figure out how to tweak * the MOSAIC stuff to get this right. Instead I am taking the * cheesy way out and declaring that anything that is NOT * explictly marked MOSAIC, and IS marked a localizer, should not * be treated as a mosaic. */ str_ptr = acr_find_string(group_list, ACR_Series_description, ""); if (strstr(str_ptr, "localizer") || strstr(str_ptr, "LOCALIZER")) { return 0; } /* OK, we did not find the word "mosaic" in the image type. But this * could still be a mosaic image. Let's look for some other clues. */ /*the SPI_Number_of_slices_nominal and SPI_Number_of_raw_partitions_nomimal are not always good fields with which to determine whether it's Mosaic or not, these fields often get set whether we have a mosaic image or not*/ /*if (acr_find_int(group_list, SPI_Number_of_slices_nominal, 0) > 1) return 1; */ /* probably mosaic. */ /*if (acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 0) > 1) return 1; */ /* probably mosaic */ return 0; /* probably not mosaic */ } #define MAXVM 32 Acr_Group parse_siemens_proto2(Acr_Group group_list, Acr_Element element) { size_t byte_cnt; size_t byte_pos; char *byte_ptr; Acr_Long n_items; Acr_Long n_values; Acr_Long vm; char vr[4]; Acr_Long syngodt; unsigned int i; char name[64]; char *value[MAXVM]; Acr_Long len; byte_cnt = element->data_length; byte_ptr = element->data_pointer; byte_pos = 0; /* See if the magic 8-byte header is present. If not, we start * right off with the number of elements. */ if (byte_ptr[0] == 'S' && byte_ptr[1] == 'V' && byte_ptr[2] == '1' && byte_ptr[3] == '0') { byte_pos += 8; /* Skip header */ } acr_get_long(ACR_LITTLE_ENDIAN, 1, byte_ptr + byte_pos, &n_items); /* I have recently received a PET-CT scan file which used yet * another undocumented format for this tag. I have no idea how to * read it, but it appears to be big-endian and to start with the * byte length of the overall element data. For now I just ignore * this element if it is not in a known format. * * Since each element in the "known" format is 84 bytes in length, * this test verifies that we don't have an impossible number of * items (bert). */ #define CSA_ELEMENT_SIZE (84) if (n_items * CSA_ELEMENT_SIZE >= byte_cnt) { return group_list; /* Return list unmodified. */ } byte_pos += 8; /* Skip # items and 4 bytes of unknown junk */ while (n_items-- > 0) { strncpy(name, (byte_ptr + byte_pos), 64); byte_pos += 64; acr_get_long(ACR_LITTLE_ENDIAN, 1, byte_ptr + byte_pos, &vm); byte_pos += 4; strncpy(vr, (byte_ptr + byte_pos), 4); byte_pos += 4; acr_get_long(ACR_LITTLE_ENDIAN, 1, byte_ptr + byte_pos, &syngodt); byte_pos += 4; acr_get_long(ACR_LITTLE_ENDIAN, 1, byte_ptr + byte_pos, &n_values); byte_pos += 4; byte_pos += 4; /* skip dummy */ if (n_values > 0) { for (i = 0; i < n_values; i++) { byte_pos += 4; /* skip */ acr_get_long(ACR_LITTLE_ENDIAN, 1, byte_ptr + byte_pos, &len); byte_pos += 4; byte_pos += 8; if (i < vm && i < MAXVM) { value[i] = (char *)(byte_ptr + byte_pos); } byte_pos += ((len + 3) / 4) * 4; } } if (!strcmp(name, "DiffusionDirectionality")) { acr_insert_string(&group_list, ACR_Diffusion_directionality, value[0]); } else if (!strcmp(name, "B_matrix")) { if (!acr_find_group_element(group_list, SPI_B_matrix) && n_values == B_MATRIX_COUNT) { double tmp[B_MATRIX_COUNT]; for (i = 0; i < B_MATRIX_COUNT; i++) { tmp[i] = atof(value[i]); } acr_insert_double(&group_list, SPI_B_matrix, B_MATRIX_COUNT, tmp); } } else if (!strcmp(name, "B_value")) { /*double tmp = atof(value[0]); this atof makes the value null! ilana*/ Acr_Double tmp = atoi(value[0]); /*need a hack for ICBM scan, see below ilana*/ acr_insert_double(&group_list, ACR_Diffusion_b_value, 1, &tmp); } else if (!strcmp(name, "DiffusionGradientDirection")) { Acr_Double tmp[3]; if (vm == 3 && n_values >= vm) { /*For the ICBM WIP scan, the b0 images do not have B_value=0 or DiffusionGradientDirection=0 0 0, they actually have DiffusionGradientDirection=-1.00010000 -1.00010000 -1.00010000 Use this to detect the b=0 images and hopefully this doesn't ever correspond to a real gradient direction (have to change bvalues correspondingly)! ilana*/ if(!strcmp(value[0],"-1.00010000") && !strcmp(value[1],"-1.00010000") && !strcmp(value[2],"-1.00010000")){ value[0] = value[1] = value[2] = "0"; /*grad directions should be 0*/ Acr_Double tmp2 = atoi("0"); acr_insert_double(&group_list, ACR_Diffusion_b_value, 1, &tmp2); /*also have to modify B values ilana*/ } tmp[0] = atof(value[0]); tmp[1] = atof(value[1]); tmp[2] = -1*atof(value[2]); /*dicom z = -minc z ilana*/ acr_insert_double(&group_list, ACR_Diffusion_gradient_orientation, 3, tmp); } } else if (!strcmp(name, "SliceNormalVector")) { if (vm == 3 && n_values >= vm) { Acr_Double tmp[3]; Acr_Short orientation; tmp[0] = atof(value[0]); tmp[1] = atof(value[1]); tmp[2] = atof(value[2]); orientation = TRANSVERSE; if (tmp[0] > tmp[2] && tmp[0] > tmp[1]) { orientation = SAGITTAL; } else if (tmp[1] > tmp[2] && tmp[1] > tmp[0]) { orientation = CORONAL; } acr_insert_numeric(&group_list, EXT_Slice_orientation, orientation); } } #if 0 if (G.Debug >= HI_LOGGING) { printf("%s VM=%d VR=%s %d ", name, vm, vr, n_values); if (n_values != 0) { for (i = 0; i < vm && i < MAXVM; i++) { printf("%s ", value[i]); } } printf("\n"); } #endif } return (group_list); } void do_siemens_diffusion(Acr_Group group_list, Acr_Element protocol) { string_t str_buf; int enc_ix, num_encodings, num_b0; int EXT = 0; /* special handling when an external diffusion vector file is used*/ Acr_String str_ptr; Acr_String str_ptr2; int temp; int num_directions = 0; /* Modified by ilana to handle the common types of diffusion scans (ref: siemens_dicom_to_minc for dicomserver) * correct dynamic scan info if *MGH* diffusion scan: * * assumptions: * * - diffusion protocol indicated by sDiffusion.ulMode = 0x100 * - bvalue is in sDiffusionalBValue[1] * - there are 10 b=0 scans and a user defined number of diffusion directions * - b=0 scans have sequence name "ep_b0#0, ep_b0#1... etc" * - encoded scans have seq names "ep_b1000#1, ep_b1000#2, ...,ep_b1300#1, ep_b1300#2, ..." etc. * * actions: * * - change number of dynamic scans to sDiffusion.lDiffDirections + num b0 images * - use sWiPMemBlock.alFree[8] for number of b=0 scans * - modify dynamic scan index to encoding index */ /* correct dynamic scan info if standard *ep2d_diff* diffusion scan * * assumptions: * * - diffusion protocol indicated by sDiffusion.ulMode = 0x100 * - bvalue is in sDiffusion.alBValue[0] * - there is 1 b=0 scans and 12 diffusion directions * - b=0 scan havs sequence name "ep_b0" * - encoded scans have seq names "ep_b1000#1, ep_b1000#2, ..." etc. * * actions: * * - change number of dynamic scans to sDiffusion.lDiffDirections + num b0 images * - modify dynamic scan index to encoding index */ /* correct dynamic scan info if standard *ICBM_WIP* diffusion scan * * assumptions: * * - diffusion protocol indicated by sDiffusion.ulMode = 0x80 * - bvalue is in sDiffusion.alBValue[1] * - there is 1 b=0 scans and user defined number of diffusion directions * - b=0 scan has sequence name "ep_b0" * - encoded scans have seq names "ep_b1000#1, ep_b1000#2, ..." etc. * * actions: * * - change number of dynamic scans to sDiffusion.lDiffDirections + num b0 images * - modify dynamic scan index to encoding index */ str_ptr = acr_find_string(group_list, ACR_Image_type, ""); if (strstr(str_ptr, "DIFFUSION") != 0) { /* * For at least some Siemens diffusion scans, this string will be * present in the image type. Even if present, we do need to insert * the correct name for the acquisition contrast, because Siemens * doesn't seem to do that! */ str_ptr = acr_find_string(group_list, ACR_Acquisition_contrast, ""); if (strcmp(str_ptr, "DIFFUSION") == 0) { if (G.Debug) printf("Acquisition contrast properly set.\n"); } else { if (G.Debug) printf("Inserting acquisition contrast\n"); acr_insert_string(&group_list, ACR_Acquisition_contrast, "DIFFUSION"); } } str_ptr = acr_find_string(group_list, ACR_Protocol_name, ""); if (strstr(str_ptr, "DTI_30DIR") != 0) { /* Scans with this protocol name have 9 B0 images and 30 * B1000 images in most cases. They appear to be related * to ADNI. There is one b0 image before * the directional images, and eight more at the end of the * scan. * * They are correctly handled by the diffusion information in * the SPI fields, so we don't bother with this folderol here. */ return; } prot_find_string(protocol, "sDiffusion.ulMode", str_buf); if (str_buf[0] != '\0') { unsigned long mode = strtol(str_buf, NULL, 0); sprintf(str_buf, "0x%lx", mode); } if (!strcmp(str_buf, "0x100") || !strcmp(str_buf, "0x80")) { /*we have a diffusion scan; flag it*/ acr_insert_string(&group_list, ACR_Acquisition_contrast, "DIFFUSION"); /*----MGH-----*/ if (prot_find_string(protocol,"sWiPMemBlock.alFree[8]", str_buf) && strtol(str_buf, NULL, 0) != 0) { /*num b0 images for MGH sequence*/ /* get number of b=0 images*/ num_b0 = strtol(str_buf, NULL, 0); /* try to get b value */ prot_find_string(protocol, "sDiffusion.alBValue[1]", str_buf); acr_insert_numeric(&group_list, EXT_Diffusion_b_value, (double)strtol(str_buf, NULL, 0)); } else { prot_find_string(protocol, "sDiffusion.ulMode", str_buf); if (str_buf[0] != '\0') { unsigned long mode = strtol(str_buf, NULL, 0); sprintf(str_buf, "0x%lx", mode); } /*-----ep2d_diff-----OR----- ep2d_diff_WIP_ICBM with "Diffusion mode"=MDDW & DiffusionWeightings=2-----------*/ if (!strcmp(str_buf, "0x100")) { /* try to get b value */ prot_find_string(protocol, "sDiffusion.alBValue[1]", str_buf); acr_insert_numeric(&group_list, EXT_Diffusion_b_value, (double)strtol(str_buf, NULL, 0)); num_b0=1; } /*-----ICBM_WIP with "Diffusion mode"=Free (5 b=0 scans) or any time an external vectors file is used-----*/ else if(!strcmp(str_buf, "0x80")) { EXT=1; /* try to get b value */ prot_find_string(protocol, "sDiffusion.alBValue[0]", str_buf); acr_insert_numeric(&group_list, EXT_Diffusion_b_value, (double)strtol(str_buf, NULL, 0)); num_b0=0; /*For ICBM there are 5 b=0 scans but they are not identified any differently than the diffusion weighted images, sDiffusion.lDiffDirections includes the b=0 images. An external DiffusionVectors file can include other b=0 and this messes up the image count*/ } } /* if all averages in one series: */ prot_find_string(protocol,"ucDixon",str_buf); temp = strtol(str_buf, NULL, 0); if (temp == 1) { prot_find_string(protocol, "sDiffusion.lDiffDirections", str_buf); num_directions = strtol(str_buf, NULL, 0); num_encodings = num_directions + num_b0; /* number of 'time points' */ acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, num_encodings * acr_find_double(group_list, ACR_Nr_of_averages, 1)); /* time index of current scan: */ /* For multi-series scans, we DO USE THIS BECAUSE global * image number may be broken!! */ /*Have to also take care of numbered b=0 images (ep_b0#0, etc...) ilana*/ /*the Siemems-based sequences, in MDDW mode with 2 diff weightings have b=0 images called ep_b0*/ str_ptr = strstr(acr_find_string(group_list, ACR_Sequence_name, ""), "b"); str_ptr2 = strstr(acr_find_string(group_list, ACR_Sequence_name, ""), "#"); if (str_ptr == NULL || str_ptr2 == NULL) { printf("WARNING: Failing to get acquisition number from sequence name '%s'.\n", acr_find_string(group_list, ACR_Sequence_name, "")); enc_ix = 0; } else if(strtol(str_ptr + 1, NULL, 0) == 0) { /*a 0 after the b means b=0 image*/ enc_ix = strtol(str_ptr2 + 1, NULL, 0); } else { enc_ix = strtol(str_ptr2 + 1, NULL, 0) + num_b0; /*should be in diffusion weighted images now*/ } /* however with the current sequence, we get usable * time indices from floor(global_image_num/num_slices)*/ /*i'm not sure that works here*/ acr_insert_numeric(&group_list, ACR_Acquisition, (double)enc_ix); /*acr_insert_numeric(&group_list, ACR_Acquisition, (acr_find_int(group_list, ACR_Image, 1)-1) / num_slices);*/ } else { /* averages in different series - no special handling needed? */ prot_find_string(protocol, "sDiffusion.lDiffDirections", str_buf); num_directions = strtol(str_buf, NULL, 0); num_encodings = num_directions + num_b0; /* number of 'time points' */ acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, (double)num_encodings); /* For multi-series scans, we DO USE THIS BECAUSE global * image number may be broken!! */ /*Have to also take care of numbered b=0 images (ep_b0#0, etc...) ilana*/ str_ptr = strstr(acr_find_string(group_list, ACR_Sequence_name, ""), "b"); str_ptr2 = strstr(acr_find_string(group_list, ACR_Sequence_name, ""), "#"); if (str_ptr == NULL || str_ptr2 == NULL) { enc_ix = 0; } else if(strtol(str_ptr + 1, NULL, 0) == 0){ /*a 0 after the b means b=0 image*/ enc_ix = strtol(str_ptr2 + 1, NULL, 0); } else { enc_ix = strtol(str_ptr2 + 1, NULL, 0) + num_b0; /*should be in diffusion weighted images now*/ } acr_insert_numeric(&group_list, ACR_Acquisition, (double)enc_ix); } if (EXT == 1) { /*if an external DiffusionVectors was used, the encoding index can be wrong because it does not take into account b=0 images within the acquisition. Have to rely on ACR_Image 0020x0013 (but this field won't work for MGH sequence)*/ acr_insert_numeric(&group_list, ACR_Acquisition, acr_find_int(group_list, ACR_Image, 1) ); } /* BUG! TODO! FIXME! In dcm2mnc.c the sequence name is * used as one of the criteria for starting a new * file. For a DTI sequence, we don't want this to * happen. For now I replace the sequence name with a * bogus value in order to keep the DTI scan from being * split into pieces. This isn't right. We should deal * with this in a more graceful way. */ acr_insert_string(&group_list, ACR_Sequence_name, "DTI"); /* Reset acquisition time to series time plus the series * index. Otherwise each slice may get its own * time. This is also probably wrong and in the ideal * world we should deal with this gracefully. But we have * no good way of storing per-slice timing information * right now. */ acr_insert_numeric(&group_list, ACR_Acquisition_time, acr_find_double(group_list, ACR_Series_time, 0) + enc_ix); /* end of diffusion scan handling */ /* There is another whole class of (probably newer) diffusion * weighted images that use a very different arrangement and * need better handling. */ } } static Acr_Group add_siemens_info(Acr_Group group_list) { /* needed for group repair - some essential info * only available in ascii dump of MrProt structure */ Acr_Element protocol; Acr_Element element; int num_slices, num_partitions; string_t str_buf; char *str_ptr=NULL; int interpolation_flag; int temp; str_ptr = acr_find_string(group_list, SPI_Private_creator_0029, ""); element = acr_find_group_element(group_list, SPI_Protocol2); if (!strncmp(str_ptr, "SIEMENS CSA HEADER", 18) && element != NULL) { group_list = parse_siemens_proto2(group_list, element); } /* now fix the group list to provide essential info * via standard dicom elements * (this lets the rest of the code be more reusable) * Note that these parameters are mostly dimension lengths, * and are not usually supplied in standard DICOM implementations. * We could do without them, except that the use of mosaics for * multi-slice data makes at least the number of slices necessary. * For such elements, we will spoof our own `static' entries * using Numaris 3.5 coordinates. These should not be used elsewhere * in the code unless there's no other way! Basically things * should be done as follows: * 1) use actual element in dicom group, if possible * 2) if not, then get info from MrProt and insert as * correct dicom element * 3) if no element exists, use an SPI element (careful!) * 4) if no SPI element exists, use and EXT element (careful!) */ /* read in Protocol group */ protocol = acr_find_group_element(group_list, SPI_Protocol); if (protocol != NULL) { /* parse_siemens_junk(protocol); */ if (G.Debug >= HI_LOGGING) { printf("Incorporating Siemens protocol structure...\n"); } /* Add number of dynamic scans: */ prot_find_string(protocol, "lRepetitions", str_buf); if (strtol(str_buf, NULL, 0) == 0){ /* lRepetitions not found in ASCONV header*/ int frames = acr_find_int(group_list, ACR_Cardiac_number_of_images,0); /* this seems to give number of dynamic scans when lRepetitions not there*/ acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, frames); } else { acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, strtol(str_buf, NULL, 0) + 1); } /* add number of echoes: */ prot_find_string(protocol, "lContrasts", str_buf); acr_insert_numeric(&group_list, SPI_Number_of_echoes, (double)strtol(str_buf, NULL, 0)); /* Add receiving coil (for some reason this isn't in generic groups) */ prot_find_string(protocol, "sCOIL_SELECT_MEAS.asList[0].sCoilElementID.tCoilID", str_buf); /*Adjust for change in naming convention (VB15 VA30)*/ if (strtol(str_buf, NULL, 0) == 0) { prot_find_string(protocol, "sCoilSelectMeas[0].asList[0].sCoilElementID.tCoilID", str_buf); } acr_insert_string(&group_list, ACR_Receive_coil_name, str_buf); /* add MrProt dump */ str_ptr = dump_protocol_text(protocol); acr_insert_string(&group_list, EXT_MrProt_dump, (Acr_String)str_ptr); free(str_ptr); /* add number of slices: (called `Partitions' for 3D) */ prot_find_string(protocol, "sSliceArray.lSize", str_buf); num_slices = strtol(str_buf, NULL, 0); prot_find_string(protocol, "sKSpace.lPartitions", str_buf); num_partitions = strtol(str_buf, NULL, 0); /* This is a hack based upon the observation that for at least some * conversions, this value seems to give the true number of slices * rather than the sKSpace.lPartitions value (bert) */ prot_find_string(protocol, "sKSpace.lImagesPerSlab", str_buf); if (str_buf[0] != '\0') { int num_images_per_slab = strtol(str_buf, NULL, 0); if (num_images_per_slab > num_partitions) { num_partitions = num_images_per_slab; } } /* NOTE: for some reason, lPartitions > 1 even for 2D scans * (e.g. EPI, scouts) */ if (!strncmp(acr_find_string(group_list, ACR_MR_acquisition_type,""), "3D", 2)) { /* Use partitions if 3D. * (note that this gets more complicated if the 3D scan * is mosaiced - see below) */ acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, (double)1); acr_insert_numeric(&group_list, SPI_Number_of_3D_raw_partitions_nominal, (double)num_partitions); } else { /* use slices for 2D */ acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, (double)num_slices); acr_insert_numeric(&group_list, SPI_Number_of_3D_raw_partitions_nominal, (double)1); } /* See if the field of view is set, if not, see if we can find * it in the ASCCONV dump and copy it into the proprietary * fields. */ if (acr_find_group_element(group_list, SPI_Field_of_view) == NULL) { /* TODO: This assumes a symmetric field of view. We need to * figure out what to do if this is not true!! */ prot_find_string(protocol, "sSliceArray.asSlice[0].sReadoutFOV", str_buf); if (str_buf[0] != '\0') { int fov = strtol(str_buf, NULL, 0); sprintf(str_buf, "%d\\%d", fov, fov); acr_insert_string(&group_list, SPI_Field_of_view, str_buf); } else { prot_find_string(protocol, "sSliceArray.asSlice[0].dReadoutFOV", str_buf); if (str_buf[0] != '\0') { double fov = atof(str_buf); sprintf(str_buf, "%f\\%f", fov, fov); acr_insert_string(&group_list, SPI_Field_of_view, str_buf); } } } prot_find_string(protocol, "sKSpace.uc2DInterpolation", str_buf); interpolation_flag = strtol(str_buf, NULL, 0); /* find Delay time in TR in ASCONV header ilana */ prot_find_string(protocol, "lDelayTimeInTR", str_buf); acr_insert_numeric(&group_list, EXT_Delay_in_TR, (double)atol(str_buf)); /* Find slice acquisition mode in ASCONV header, can't seem to find is in a standard dicom field sSliceArray.ucMode takes on 3 values: 0x1 means ASCENDING 0x2 means DESCENDING 0x4 means INTERLEAVED */ prot_find_string(protocol, "sSliceArray.ucMode", str_buf); if (str_buf[0] != '\0') { int mode = strtol(str_buf, NULL, 0); sprintf(str_buf, "0x%x", mode); acr_insert_string(&group_list,EXT_Slice_order,str_buf); } /* Based on inspection of the AFNI code and other sources, it * appears that these fields (sSliceArray.ucImageNumbXxx) * indicate inverted slice ordering if the field corresponding * to the slice axis has the value 1. */ temp = acr_find_int(group_list, EXT_Slice_orientation, -1); prot_find_string(protocol, "sSliceArray.ucImageNumbTra", str_buf); if (str_buf[0] != '\0') { if (temp == TRANSVERSE && strtol(str_buf, NULL, 0) == 1) { acr_insert_string(&group_list, EXT_Slice_inverted, "1"); } } prot_find_string(protocol, "sSliceArray.ucImageNumbSag", str_buf); if (str_buf[0] != '\0') { if (temp == SAGITTAL && strtol(str_buf, NULL, 0) == 1) { acr_insert_string(&group_list, EXT_Slice_inverted, "1"); } } prot_find_string(protocol, "sSliceArray.ucImageNumbCor", str_buf); if (str_buf[0] != '\0') { if (temp == CORONAL && strtol(str_buf, NULL, 0) == 1) { acr_insert_string(&group_list, EXT_Slice_inverted, "1"); } } do_siemens_diffusion(group_list, protocol); } else { /* If no protocol dump was found we have to assume no * interpolation since at the momemnt I have no idea where it * would live... */ interpolation_flag = 0; num_partitions = acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 0); num_slices = acr_find_int(group_list, SPI_Number_of_slices_nominal, 0); } /* Handle mosaic images if necessary. */ if (is_siemens_mosaic(group_list)) { int mosaic_rows, mosaic_cols; Acr_Short subimage_size[4]; int subimage_rows, subimage_cols; /* Now figure out mosaic rows and columns, and put in EXT * shadow group. Check for interpolation - will require 2x * scaling of rows and columns. */ /* Assign defaults in case something goes wrong below... */ subimage_rows = subimage_cols = acr_find_int(group_list, SPI_Base_raw_matrix_size, 0); /* If we don't have an "SPI_Base_raw_matrix_size" field, try using * the acquisition matrix. */ if (subimage_rows == 0) { /* Compute mosaic rows and columns * Here is a hack to handle non-square mosaiced images * * WARNING: as far as I can tell, the phase-encoding dir (row/col) * is reversed for mosaic EPI scans (don't know if this is a * mosaic thing, an EPI thing, or whatever). * (Something is wrong here, it seems that it's not reversed, but * not sure obviously doesn't show up when the acquisition matrix is * square) * Get the array of sizes: freq row/freq col/phase row/phase col */ element = acr_find_group_element(group_list, ACR_Acquisition_matrix); if (element == NULL) { printf("WARNING: Can't find acquisition matrix\n"); } else if (acr_get_element_short_array(element, 4, subimage_size) != 4) { printf("WARNING: Can't read acquisition matrix\n"); } else { if (G.Debug >= HI_LOGGING) { printf(" * Acquisition matrix %d %d %d %d\n", subimage_size[0], subimage_size[1], subimage_size[2], subimage_size[3]); } /* Get subimage dimensions, assuming the OPPOSITE of the * reported phase-encode direction!! */ str_ptr = (char *)acr_find_string(group_list, ACR_Phase_encoding_direction, ""); if (!strncmp(str_ptr, "COL", 3)) { /*TODO test that this is right for non-square acquisition matrices*/ subimage_rows = subimage_size[3]; subimage_cols = subimage_size[0]; } else if (!strncmp(str_ptr, "ROW", 3)) { /*not sure the subimage dimensions should be opposite of * the phase encoding direction, running into some problems, * so changed it to be the same but I also might be breaking * for other cases!*/ /*subimage_rows = subimage_size[2]; subimage_cols = subimage_size[1];*/ subimage_rows = subimage_size[1]; subimage_cols = subimage_size[2]; } else { printf("WARNING: Unknown phase encoding direction '%s'\n", str_ptr); } /* If interpolation, multiply rows and columns by 2 */ if (interpolation_flag) { subimage_rows *= 2; subimage_cols *= 2; } } } /* If these are not set or still zero, assume this is NOT a mosaic * format file. */ if (subimage_rows == 0 || subimage_cols == 0) { subimage_rows = acr_find_int(group_list, ACR_Rows, 1); subimage_cols = acr_find_int(group_list, ACR_Columns, 1); mosaic_rows = 1; mosaic_cols = 1; } else { if (G.Debug >= HI_LOGGING) { printf("Assuming %dx%d mosaic\n", subimage_rows, subimage_cols); } mosaic_rows = acr_find_int(group_list, ACR_Rows, 1) / subimage_rows; mosaic_cols = acr_find_int(group_list, ACR_Columns, 1) / subimage_cols; } acr_insert_numeric(&group_list, EXT_Mosaic_rows, (double)mosaic_rows); acr_insert_numeric(&group_list, EXT_Mosaic_columns, (double)mosaic_cols); if (mosaic_rows * mosaic_cols > 1) { str_ptr = (char *)acr_find_string(group_list, ACR_MR_acquisition_type, ""); /* assume any mosaiced file contains all slices * (we now support mosaics for 2D and 3D acquisitions, * so we may need to use partitions instead of slices) */ if (!strncmp(str_ptr, "2D", 2)) { if (num_slices == 0) { /* Aw hell. We got all the way here, and there was * no prior indication of the number of slices in * the file. We'll have to make it up. */ num_slices = mosaic_rows * mosaic_cols; } acr_insert_numeric(&group_list, EXT_Slices_in_file, (double)num_slices); acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, (double)num_slices); } /* if 3D mosaiced scan, write number of partitions to number of * slices in dicom group */ else if (!strncmp(str_ptr, "3D", 2)) { acr_insert_numeric(&group_list, EXT_Slices_in_file, (double)num_partitions); acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, (double)num_partitions); /* also have to provide slice spacing - in case of 3D it's same * as slice thickness (and not provided in dicom header!) */ acr_insert_numeric(&group_list, ACR_Spacing_between_slices, acr_find_double(group_list, ACR_Slice_thickness, 1.0)); } } else { acr_insert_numeric(&group_list, EXT_Slices_in_file, (double)1); } /* Correct the rows and columns values - * These will reflect those of the subimages in the mosaics * NOT the total image dimensions */ acr_insert_short(&group_list, EXT_Sub_image_columns, subimage_cols); acr_insert_short(&group_list, EXT_Sub_image_rows, subimage_rows); /* TODO: should also correct the image position here? */ } /* Hacks for specific numaris3 sequences. */ if (is_numaris3(group_list)) { int tmp; str_ptr = (char *)acr_find_string(group_list, ACR_Sequence_name, ""); /* This first hack is for Sebastian's Numaris-3 DTI scans. */ if (!strcmp(str_ptr, "ep_d33a ")) { /* Numaris-3 DTI scan. Individual scans are identified by * echo number. We need to change echos to time. */ tmp = acr_find_int(group_list, SPI_Number_of_echoes, 1); acr_insert_numeric(&group_list, SPI_Number_of_echoes, (double)1); element = acr_find_group_element(group_list, ACR_Acquisitions_in_series); if (element != NULL) { int grp_id = acr_get_element_group(element); int elm_id = acr_get_element_element(element); acr_group_remove_element(acr_find_group(group_list, grp_id), elm_id); } acr_insert_short(&group_list, ACR_Number_of_time_slices, tmp); tmp = acr_find_int(group_list, ACR_Echo_number, 0); acr_insert_numeric(&group_list, ACR_Echo_number, (double)1); acr_insert_numeric(&group_list, ACR_Frame_reference_time, (double)(tmp * 1000)); /* Just use defaults for these values. */ acr_insert_numeric(&group_list, ACR_Actual_frame_duration, (double)1000); } /* An additional hack required for Sebastian's Numaris-3 FMRI scans. */ if (!strcmp(str_ptr, "ep_fid")) { /* Numaris-3 FMRI scan. Each time slice uses a different * series number!! */ tmp = acr_find_int(group_list, ACR_Series_time, 0); acr_insert_numeric(&group_list, ACR_Series, (double)tmp); tmp = acr_find_int(group_list, ACR_Acquisitions_in_series, 0); acr_insert_short(&group_list, ACR_Number_of_time_slices, tmp); element = acr_find_group_element(group_list, ACR_Acquisitions_in_series); if (element != NULL) { int grp_id = acr_get_element_group(element); int elm_id = acr_get_element_element(element); acr_group_remove_element(acr_find_group(group_list, grp_id), elm_id); } } } return (group_list); } #define PMS_SET_CREATOR(el, cr) \ (el)->element_id = ((el)->element_id & 0xff) + ((cr)->element_id << 8) static Acr_Group add_philips_info(Acr_Group group_list) { struct Acr_Element_Id creator_id; Acr_Group pms_group; Acr_Element pms_element; Acr_Element pms_element_list; char *str_ptr; int slice_count; int slice_index; char str_buf[128]; /* To use the Philips proprietary group, we have to figure out the * DICOM static creator ID in use. The group ID is always 0x2001, * but the upper eight bits of the element ID are somewhat variable. * To tell what they are, we have to search through the element ID's * from 0x0001-0x00ff and find one that contains the text * "PHILIPS IMAGING DD 001" or "Philips Imaging DD 001". The value * of this element is then used for the upper eight bits of all * subsequent Philips static element ID's. */ pms_group = acr_find_group(group_list, PMS_PRIVATE_GROUP_ID); if (pms_group != NULL) { pms_element_list = acr_get_group_element_list(pms_group); creator_id.group_id = PMS_PRIVATE_GROUP_ID; creator_id.vr_code = ACR_VR_LO; for (creator_id.element_id = 0x0001; creator_id.element_id <= 0x00ff; creator_id.element_id++) { pms_element = acr_find_element_id(pms_element_list, &creator_id); if (pms_element != NULL) { str_ptr = (char *)acr_get_element_string(pms_element); if (str_ptr != NULL && (!strcmp(str_ptr, "Philips Imaging DD 001") || !strcmp(str_ptr, "PHILIPS IMAGING DD 001"))) { /* Found it!!! */ break; } } } } else { creator_id.element_id = 0; } if (creator_id.element_id > 0xff || creator_id.element_id < 0x01) { printf("WARNING: Can't find Philips static creator ID.\n"); /* OK, this may be an old Philips file with the SPI stuff in it. */ str_ptr = (char *)acr_find_string(group_list, SPI_PMS_grp19_tag, ""); if (!strncmp(str_ptr, "PHILIPS MR", 10)) { if (G.Debug >= HI_LOGGING) { printf("Found Philips SPI information\n"); } str_ptr = (char *)acr_find_string(group_list, ACR_Image_position_patient, ""); if (*str_ptr == '\0') { double x, y, z; x = (double)acr_find_double(group_list, SPI_PMS_lr_position2, 0); y = (double)acr_find_double(group_list, SPI_PMS_ap_position2, 0); z = (double)acr_find_double(group_list, SPI_PMS_cc_position2, 0); sprintf(str_buf, "%.15g\\%.15g\\%.15g", x, y, z); acr_insert_string(&group_list, ACR_Image_position_patient, (Acr_String)str_buf); } str_ptr = (char *)acr_find_string(group_list, ACR_Number_of_slices, ""); if (*str_ptr == '\0') { Acr_Short n; n = acr_find_short(group_list, SPI_PMS_slice_count, 0); acr_insert_short(&group_list, ACR_Number_of_slices, n); } str_ptr = (char*)acr_find_string(group_list, ACR_Image_orientation_patient, ""); if (strchr(str_ptr, '\\') == NULL) { int orientation = acr_find_int(group_list, SPI_PMS_slice_orientation, 1); switch (orientation) { case 1: /* transverse, slice=Z */ strcpy(str_buf, "1\\0\\0\\0\\1\\0"); break; case 2: /* sagittal, slice=X */ strcpy(str_buf, "0\\1\\0\\0\\0\\1"); break; case 3: /* coronal, slice=Y */ strcpy(str_buf, "1\\0\\0\\0\\0\\1"); break; } acr_insert_string(&group_list, ACR_Image_orientation_patient, str_buf); } #if 0 /* not clear that this is needed */ str_ptr = (char *)acr_find_string(group_list, ACR_Pixel_size, ""); if (*str_ptr == '\0') { pms_element = acr_find_group_element(group_list, SPI_PMS_field_of_view); if (pms_element != NULL) { double fov[2]; acr_get_element_numeric_array(pms_element, 2, fov); if (fov[0] != 0.0 && fov[1] != 0.0) { double derived_spacing[2]; int rows = acr_find_int(group_list, ACR_Rows, 1); int cols = acr_find_int(group_list, ACR_Columns, 1); derived_spacing[0] = fov[0] / cols; derived_spacing[1] = fov[1] / rows; sprintf(str_buf, "%.15g\\%.15g", derived_spacing[0], derived_spacing[1]); printf("PHILIPS: New pixel size %s\n", str_buf); acr_insert_string(&group_list, ACR_Pixel_size, str_buf); } } } #endif } } else { if (G.Debug >= HI_LOGGING) { printf("Found Philips static creator ID at %#x\n", creator_id.element_id); } PMS_SET_CREATOR(PMS_Number_of_Slices_MR, &creator_id); slice_count = acr_find_int(group_list, PMS_Number_of_Slices_MR, -1); if (slice_count < 0) { printf("WARNING: Can't find Philips slice count\n"); } else { acr_insert_short(&group_list, ACR_Images_in_acquisition, slice_count); } PMS_SET_CREATOR(PMS_Slice_Number_MR, &creator_id); slice_index = acr_find_int(group_list, PMS_Slice_Number_MR, -1); if (slice_index < 0) { printf("WARNING: Can't find Philips slice index\n"); } else { /* TODO: It is really quite gross that we have to resort to * using a Siemens-proprietary field to tell the rest of the * code which slice we are on. But such is life, for now... */ acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) slice_index); } } return (group_list); } Acr_Group add_gems_info(Acr_Group group_list) { Acr_String tmp_str; int image_count; int image_index; double tr; int ok; ok = 1; tmp_str = acr_find_string(group_list, GEMS_Acqu_private_creator_id, ""); if (strcmp(tmp_str, "GEMS_ACQU_01")) { ok = 0; } tmp_str = acr_find_string(group_list, GEMS_Sers_private_creator_id, ""); if (strcmp(tmp_str, "GEMS_SERS_01")) { ok = 0; } if (!ok) { /* Warn only for non-PET things. We know the PET scanner doesn't * rely on this proprietary nonsense. */ tmp_str = acr_find_string(group_list, ACR_Modality, ""); if (strcmp(tmp_str, "PT") && G.Debug) { printf("WARNING: GEMS data not found\n"); } } tmp_str = acr_find_string(group_list, ACR_Image_type, ""); image_index = acr_find_int(group_list, ACR_Image, -1); image_count = acr_find_int(group_list, GEMS_Images_in_series, -1); tr = (double)acr_find_double(group_list, ACR_Repetition_time, 0.0); /* If we found a valid image count in the proprietary field, copy it * into the standard field if the standard field is not already set. */ if (image_count > 0 && acr_find_int(group_list, ACR_Images_in_acquisition, -1) < 0) { acr_insert_long(&group_list, ACR_Images_in_acquisition, image_count); } /* Do this only for EPI images for now */ if (strstr(tmp_str, "\\EPI") != NULL && image_index >= 0 && image_count > 0 && tr != 0.0) { int frame_count = acr_find_int(group_list, GEMS_Fast_phases, -1); if (frame_count > 0) { if (acr_find_int(group_list, ACR_Acquisitions_in_series, -1) < 0) { acr_insert_long(&group_list, ACR_Acquisitions_in_series, (Acr_Long)frame_count); } } if (acr_find_double(group_list, ACR_Frame_reference_time, -1.0) < 0) { acr_insert_numeric(&group_list, ACR_Frame_reference_time, (double)(((image_index - 1) / image_count) * tr) ); } if (acr_find_double(group_list, ACR_Actual_frame_duration, -1.0) < 0) { acr_insert_numeric(&group_list, ACR_Actual_frame_duration, tr); } if (G.Debug >= HI_LOGGING) { printf("EPI slice %d timing information: %f, %f\n", image_index, ((image_index - 1) / image_count) * tr, tr); } } /* See if this scan uses GE's proprietary "locations in * acquisition" field (0021,104f) rather than the standard * (0054,0081). Copy the proprietary field to the standard field * if needed. */ tmp_str = acr_find_string(group_list, GEMS_Rela_private_creator_id, ""); if (strcmp(tmp_str, "GEMS_RELA_01") == 0) { image_count = acr_find_int(group_list, GEMS_Locations_in_acquisition, -1); if (image_count > 0 && acr_find_int(group_list, ACR_Number_of_slices, -1) < 0) { acr_insert_short(&group_list, ACR_Number_of_slices, image_count); } } return (group_list); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_spi_to_acr() @INPUT : group_list - read a standard DICOM file @OUTPUT : (none) @RETURNS : modified group list @DESCRIPTION: Routine to copy Siemens-specific (SPI) fields to generic ACR/NEMA fields. @METHOD : @GLOBALS : @CALLS : @CREATED : 2005 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group copy_spi_to_acr(Acr_Group group_list) { int itemp; double dtemp; Acr_String stemp; Acr_Element element; stemp = acr_find_string(group_list, SPI_Private_creator, ""); if (!strcmp(stemp, "SIEMENS MR HEADER ")) { stemp = acr_find_string(group_list, SPI_Image_header_type, ""); if (!strcmp(stemp, "IMAGE NUM 4 ")) { if (G.Debug) { printf("Incorporating Siemens private information.\n"); } /* If we have this header, things like b-values are easy, because * they can be found readily here. */ itemp = acr_find_int(group_list, SPI_Diffusion_b_value, -1); if (itemp > 0) { acr_insert_numeric(&group_list, EXT_Diffusion_b_value, (double)itemp); } element = acr_find_group_element(group_list, SPI_Diffusion_gradient_direction); if (element != NULL) { double tmp[3]; acr_get_element_double_array(element, 3, tmp); tmp[2] = -1*tmp[2]; /* Is this really needed? */ acr_insert_double(&group_list, ACR_Diffusion_gradient_orientation, 3, tmp); } } } itemp = acr_find_int(group_list, SPI_Number_of_slices_nominal, 0); if (itemp != 0) { acr_insert_numeric(&group_list, ACR_Images_in_acquisition, (double)itemp); } itemp = acr_find_int(group_list, SPI_Number_of_echoes, 0); if (itemp != 0) { acr_insert_numeric(&group_list, ACR_Echo_train_length, (double)itemp); } dtemp = (double)acr_find_double(group_list, SPI_Magnetic_field_strength, 0); if (dtemp != 0.0) { acr_insert_numeric(&group_list, ACR_Magnetic_field_strength, dtemp); } return (group_list); } Acr_Group add_shimadzu_info(Acr_Group group_list) { Acr_String str_ptr; /* TODO: Figure out what can be done here... */ str_ptr = acr_find_string(group_list, ACR_Series_instance_UID, ""); if (*str_ptr != '\0') { } return (group_list); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_numa4_dicom @INPUT : filename - read a standard DICOM file max_group - maximum group number to read @OUTPUT : (none) @RETURNS : group list read in from file @DESCRIPTION: Routine to read in a group list from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 18, 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group read_numa4_dicom(const char *filename, int max_group) { Acr_Group group_list; Acr_String str_ptr; group_list = read_std_dicom(filename, max_group); if (group_list == NULL) { return NULL; } /* Check the manufacturer. If it is one we know, try to interpret * the static/proprietary data if present. This is converted to * standard DICOM fields whereever it makes sense to do so. */ str_ptr = acr_find_string(group_list, ACR_Manufacturer, ""); if (strstr(str_ptr, "SIEMENS") != NULL || strstr(str_ptr, "Siemens") != NULL) { group_list = add_siemens_info(group_list); /* Now copy proprietary fields into the correct places in the * standard groups. */ group_list = copy_spi_to_acr(group_list); } else if (strstr(str_ptr, "Philips") != NULL) { group_list = add_philips_info(group_list); } else if (strstr(str_ptr, "GEMS") != NULL || strstr(str_ptr, "GE MEDICAL") != NULL) { group_list = add_gems_info(group_list); } else if (strstr(str_ptr, "shimadzu") != NULL) { group_list = add_shimadzu_info(group_list); } return (group_list); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_info @INPUT : gi_ptr fi_ptr num_files @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to free contents of general and file info structures. @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void free_info(General_Info *gi_ptr, File_Info *fi_ptr, int num_files) { Mri_Index imri; /* Free the general info pointers */ for (imri = 0; imri < MRI_NDIMS; imri++) { if (gi_ptr->indices[imri] != NULL) { free(gi_ptr->indices[imri]); } if (gi_ptr->coordinates[imri] != NULL) { free(gi_ptr->coordinates[imri]); } if (gi_ptr->widths[imri] != NULL) { free(gi_ptr->widths[imri]); } } /* Free the group list */ if (gi_ptr->group_list != NULL) { acr_delete_group_list(gi_ptr->group_list); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : search_list @INPUT : value list_ptr list_length start_index - point from which search should start @OUTPUT : (none) @RETURNS : Index in list where value is found, or -1 is value not found. @DESCRIPTION: Routine to search a list for a value, returning the index into the list. If the value is not found, then -1 is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int search_list(int value, const int *list_ptr, int list_length, int start_index) { int index; /* If nothing on list, just return. */ if (list_length <= 0) { return (-1); } /* If starting point is invalid, start at zero. */ if ((start_index >= list_length) || (start_index < 0)) { start_index = 0; } /* Loop over indices, wrapping at the end of the list */ index = start_index; do { if (list_ptr[index] == value) { return index; /* Found it. */ } index++; if (index >= list_length) { index = 0; } } while (index != start_index); return -1; /* Search failed. */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_dimensions @INPUT : gi_ptr @OUTPUT : gi_ptr @RETURNS : (nothing) @DESCRIPTION: Routine to sort the MRI dimensions according to their coordinates. It also fills in the step and start values for the SLICE dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void sort_dimensions(General_Info *gi_ptr) { Mri_Index imri; Sort_Element *sort_array; int nvalues; int i,j; int reverse_array; /* Sort the dimensions, if needed. */ for (imri = 0; imri < MRI_NDIMS; imri++) { /* Sort each dimension iff the size is greater than 1 and it is not * a time dimension in an N4 mosaic. */ if (gi_ptr->cur_size[imri] <= 1 || /* Don't sort on time for N4 mosaics (TODO: Why not??) */ ((G.file_type == N4DCM) && (imri == TIME) && (gi_ptr->num_slices_in_file > 1))) { if (G.Debug >= HI_LOGGING) { printf("Not sorting %s dimension\n", Mri_Names[imri]); } continue; } if (G.Debug >= HI_LOGGING) { printf("Sorting %s dimension\n", Mri_Names[imri]); printf(" slice order is:%s\n",gi_ptr->acq.slice_order ); } /* Set up the array for sorting. */ nvalues = gi_ptr->cur_size[imri]; sort_array = malloc(nvalues * sizeof(*sort_array)); CHKMEM(sort_array); for (i = 0; i < nvalues; i++) { sort_array[i].identifier = gi_ptr->indices[imri][i]; sort_array[i].original_index = i; sort_array[i].value = gi_ptr->coordinates[imri][i]; sort_array[i].width = gi_ptr->widths[imri][i]; } /* Sort the array. */ qsort((void *) sort_array, (size_t) nvalues, sizeof(*sort_array), dimension_sort_function); /* Added this from dicomserver conversion, it was probably removed. * Although it should not matter whether we start negative and * step positive or the other way around, we should try and stay * consistent with previous versions and with the dicom header. * It is especially important for slice times */ /* Figure out if we should reverse the array to keep something * similar to the original ordering. * Also need to check for slice ordering, MOSAIC images are * always sorted from bottom to top whether the sequence was * ascending or descending */ reverse_array = (sort_array[0].original_index > sort_array[nvalues-1].original_index) || (!strcmp(gi_ptr->acq.slice_order,"descending")&& !strcmp(Mri_Names[imri], "Slice")); /* Copy the information back into the appropriate arrays */ for (i=0; i < nvalues; i++) { j = (reverse_array ? nvalues - i - 1 : i); gi_ptr->indices[imri][i] = sort_array[j].identifier; gi_ptr->coordinates[imri][i] = sort_array[j].value; gi_ptr->widths[imri][i] = sort_array[j].width; } if (G.Debug >= HI_LOGGING) { /* Print out all of the information about this dimension. */ if(reverse_array){ printf(" nvalues %d min %f max %f\n", nvalues, gi_ptr->coordinates[imri][nvalues - 1], gi_ptr->coordinates[imri][0]); }else{ printf(" nvalues %d min %f max %f\n", nvalues, gi_ptr->coordinates[imri][0], gi_ptr->coordinates[imri][nvalues - 1]); } for (i = 0; i < nvalues; i++) { printf("%3d %6d %8.3f\n", i, gi_ptr->indices[imri][i], gi_ptr->coordinates[imri][i]); } } /* Free the temporary array we used to sort the coordinate axis. */ free(sort_array); /* Now verify that the slice coordinate array looks sane. We * don't complain about things like missing time slices, since * many PET scanners produce irregular timings. */ if (nvalues >= 2 && imri == SLICE) { /* Calculate the spacing between the first and second slice. */ double delta = (gi_ptr->coordinates[imri][1] - gi_ptr->coordinates[imri][0]); for (i = 1; i < nvalues; i++) { /* Check that each successive slice has roughly the same * spacing, to within 2 percent of the initial delta. */ /* Check against absolute value of epsilon, could be positive * or negative, because of reversal (fcmp does not handle this) */ if (!fcmp(delta, (gi_ptr->coordinates[imri][i] - gi_ptr->coordinates[imri][i - 1]), fabs(2.0 * (delta / 100.0)))) { /* TODO: Perhaps this message could be improved?? */ printf("WARNING: Missing %s data at index %d, %g %g\n", Mri_Names[imri], i, delta, (gi_ptr->coordinates[imri][i] - gi_ptr->coordinates[imri][i - 1])); } } } /* Update slice step and start. */ if (imri == SLICE) { if (gi_ptr->coordinates[imri][0] != gi_ptr->coordinates[imri][nvalues-1]) { double dbl_tmp1; double dbl_tmp2; dbl_tmp1 = gi_ptr->step[gi_ptr->slice_world]; dbl_tmp2 = (gi_ptr->coordinates[imri][nvalues - 1] - gi_ptr->coordinates[imri][0]) / ((double) gi_ptr->cur_size[imri] - 1.0); /* OK, so now we have to figure out who wins the great * battle to become the slice step value. If the value * we have been assuming up until now is the same as the * calculated value with only a sign change, or if the * assumed value is the default (1.0), we adopt the * calculated value. */ /* Check against absolute value of epsilon, could be positive * or negative, because of reversal (fcmp does not handle this) */ if (!fcmp(dbl_tmp1, dbl_tmp2, fabs((dbl_tmp1 / 1000.0)))) { printf("WARNING: Coordinate spacing (%g) differs from DICOM slice spacing (%g)\n", dbl_tmp2, dbl_tmp1); if (!G.prefer_coords) { printf(" (perhaps you should consider the -usecoordinates option)\n"); } if (dbl_tmp1 == 1.0 || G.prefer_coords || fcmp(dbl_tmp1, -dbl_tmp2, fabs(dbl_tmp1 / 1000.0))) { /*Although in the comment above it says that we should use the calculated value if the assumed and calculated value only differ by a sign change, this was not included in this if statement, added by ilana because default was wrong (i.e. w/o using -usecoordinates option)*/ gi_ptr->step[gi_ptr->slice_world] = dbl_tmp2; } printf(" Using %g for the slice spacing value.\n", gi_ptr->step[gi_ptr->slice_world]); } } gi_ptr->start[gi_ptr->slice_world] = gi_ptr->coordinates[imri][0]; if (G.Debug >= HI_LOGGING) { printf("Set slice %d step to %f, start to %f\n", gi_ptr->slice_world, gi_ptr->step[gi_ptr->slice_world], gi_ptr->start[gi_ptr->slice_world]); } } /* If slice dimension */ } /* for all mri dimensions */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : dimension_sort_function @INPUT : v1, v2 - values to compare @OUTPUT : (none) @RETURNS : -1, 0 or 1 if v1 < v2, v1 == v2 or v1 > v2 @DESCRIPTION: Function to compare to array elements for sorting. Elements are compared first on value, then on their original array index (this tries to preserve the original sequence). @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int dimension_sort_function(const void *v1, const void *v2) { const Sort_Element *value1, *value2; value1 = (const Sort_Element *) v1; value2 = (const Sort_Element *) v2; if (value1->value < value2->value) return -1; else if (value1->value > value2->value) return 1; else if (value1->original_index < value2->original_index) return -1; else if (value1->original_index > value2->original_index) return 1; else return 0; } int prot_find_string(Acr_Element elem_ptr, const char *name_str, char *value) { static const char prot_head[] = "### ASCCONV BEGIN "; long cur_offset,tmp_offset; long max_offset; char *field_ptr; int ix1, ix2; max_offset = elem_ptr->data_length - sizeof(prot_head); tmp_offset = max_offset; // Scan through the element containing the protocol, to find the // ASCII dump of the MrProt structure. // /*For some reason, some dicom files have 2 "ASCCONV BEGIN" tags, this screws up the search. Keep the second one we find for now, don't know if this will always work for these dual-ASCCONV files. IRL*/ for (cur_offset = 0; cur_offset < max_offset; cur_offset++) { if (!memcmp(elem_ptr->data_pointer + cur_offset, prot_head, sizeof(prot_head) - 1)) { tmp_offset = cur_offset; } } cur_offset = tmp_offset; /*set it to the last occurence of "ASCCONV BEGIN"*/ /* bail if we didn't find the protocol */ if (cur_offset == max_offset) { return (0); } field_ptr = strstr(elem_ptr->data_pointer + cur_offset, name_str); if (field_ptr != NULL) { sscanf(field_ptr, "%*s %*s %s", value); ix1 = 0; for (ix2 = 0; value[ix2] != '\0'; ix2++) { if (value[ix2] != '"') { value[ix1++] = value[ix2]; } } } else { strcpy(value, "0"); } return (1); } static char * dump_protocol_text(Acr_Element elem_ptr) { const char prot_head[] = "### ASCCONV BEGIN "; const char prot_tail[] = "### ASCCONV END ###"; char *output = malloc(elem_ptr->data_length); int prot_found = FALSE; long cur_offset, tmp_offset; long max_offset; CHKMEM(output); // scan throught the group containing the protocol, to find the // ascii dump of the MrProt structure max_offset = elem_ptr->data_length - sizeof (prot_head); /*For some reason, some dicom files have 2 "ASCCONV BEGIN" tags, this screws up the search. Keep the second one we find for now, don't know if this will always work for these dual-ASCCONV files. IRL*/ for (cur_offset = 0; cur_offset < max_offset; cur_offset++) { if (!memcmp(elem_ptr->data_pointer + cur_offset, prot_head, sizeof(prot_head) - 1)) { prot_found = TRUE; tmp_offset = cur_offset; } } cur_offset = tmp_offset; /*set it to the last occurence of "ASCCONV BEGIN"*/ if (prot_found) { int ix1 = 0; char *tmp_ptr = elem_ptr->data_pointer + cur_offset; for ( ; cur_offset < max_offset; cur_offset++) { if (!memcmp(tmp_ptr, prot_tail, sizeof(prot_tail) - 1)) { break; } if (*tmp_ptr != '"') { output[ix1++] = *tmp_ptr; } tmp_ptr++; } output[ix1] = '\0'; } return (output); } static void copy_element_properties(Acr_Element new_element, Acr_Element old_element) { acr_set_element_byte_order(new_element, acr_get_element_byte_order(old_element)); acr_set_element_vr_encoding(new_element, acr_get_element_vr_encoding(old_element)); } /* Since at least software version VA25 (and thus VA30, VB15), * the mosaic sequencing in the file is the same, regardless * of the acquisition (always ascending). * Also, the following code is based on a field that is * deprecated (0x0021 0x123f)... still keep old behavior in case * its an older image type. */ static int old_mosaic_ordering(Acr_Group group_list) { Acr_String str_tmp; Acr_String str_ver; int is_old = 1; str_ver = acr_find_string(group_list, ACR_Software_versions, ""); if (G.Debug >= HI_LOGGING) { printf("Scanner version %s\n", str_ver); } str_tmp = strstr(str_ver, "syngo MR 2004A 4VA"); if (str_tmp != NULL && atoi(str_tmp + 18) >= 25) { /* software version >= VA25 */ is_old = 0; } str_tmp = strstr(str_ver, "syngo MR A"); if (str_tmp != NULL && atoi(str_tmp + 10) >= 25) { /* software version >= VA25 */ is_old = 0; } str_tmp = strstr(str_ver, "syngo MR B"); if (str_tmp != NULL && atoi(str_tmp + 10) >= 11) { /* software version >= VB11 */ is_old = 0; } str_tmp = strstr(str_ver, "syngo MR D"); if (str_tmp != NULL) { is_old = 0; } return is_old; } static int mosaic_init(Acr_Group group_list, Mosaic_Info *mi_ptr, int load_image) { int group_id, element_id; int grid_size; long new_image_size; void *data; Acr_Element element; int i; double pixel_spacing[2]; Acr_Double separation; double RowColVec[6]; double dircos[VOL_NDIMS][WORLD_NDIMS]; Acr_String str_tmp, str_tmp2; int old = 1; if (G.Debug >= HI_LOGGING) { printf("mosaic_init(%lx, %lx, %d)\n", (unsigned long) group_list, (unsigned long) mi_ptr, load_image); } str_tmp = acr_find_string(group_list, EXT_Slice_inverted, "0"); mi_ptr->slice_inverted = strtol(str_tmp, NULL, 0) > 0; if (mi_ptr->slice_inverted) { if (G.Debug) { printf("Inverting mosaic slice ordering.\n"); } } str_tmp = acr_find_string(group_list, SPI_Order_of_slices, ""); /* Seems like this field is often not found, * the sSliceArray.ucMode field found in the ASCONV header * seems trustworthy. */ /* 0x1 means ASCENDING * 0x2 means DESCENDING * 0x4 means INTERLEAVED*/ str_tmp2 = acr_find_string(group_list, EXT_Slice_order, ""); if (G.mosaic_seq == MOSAIC_SEQ_UNKNOWN) { if (!strncmp(str_tmp, "INTERLEAVED", 11) || !strncmp(str_tmp2, "0x4", 3)) { mi_ptr->mosaic_seq = MOSAIC_SEQ_INTERLEAVED; str_tmp = "interleaved"; } else if (!strncmp(str_tmp, "DESCENDING", 10) || !strncmp(str_tmp2, "0x2", 3)) { mi_ptr->mosaic_seq = MOSAIC_SEQ_DESCENDING; str_tmp = "descending"; } else { /* Assume ascending if no other information is given. */ mi_ptr->mosaic_seq = MOSAIC_SEQ_ASCENDING; str_tmp = "ascending"; } } else { mi_ptr->mosaic_seq = G.mosaic_seq; switch (G.mosaic_seq) { case MOSAIC_SEQ_INTERLEAVED: str_tmp = "interleaved (user)"; break; case MOSAIC_SEQ_ASCENDING: str_tmp = "ascending (user)"; break; case MOSAIC_SEQ_DESCENDING: str_tmp = "descending (user)"; break; default: fprintf(stderr, "ERROR: Unknown mosaic ordering %d\n", G.mosaic_seq); exit(-1); } } if (G.Debug >= HI_LOGGING) { printf(" ordering is %s\n", str_tmp); } /* Get some basic image information. * big[0/1] is number of columns/rows in whole mosaic. */ mi_ptr->big[0] = acr_find_int(group_list, ACR_Columns, 1); mi_ptr->big[1] = acr_find_int(group_list, ACR_Rows, 1); mi_ptr->pixel_size = (acr_find_int(group_list, ACR_Bits_allocated, 16) - 1) / 8 + 1; /* Get the image size * (size[0/1] is number of columns/rows in a single slice) */ mi_ptr->size[0] = (int)acr_find_short(group_list, EXT_Sub_image_columns, 1); mi_ptr->size[1] = (int)acr_find_short(group_list, EXT_Sub_image_rows, 1); // Get the grid shape, checking that it is not too big if specified mi_ptr->grid[0] = mi_ptr->big[0] / mi_ptr->size[0]; mi_ptr->grid[1] = mi_ptr->big[1] / mi_ptr->size[1]; if ((mi_ptr->grid[0] < 1) || (mi_ptr->grid[0] < 1)) { fprintf(stderr, "Grid too small: %d x %d, size %d x %d, big %d x %d\n", mi_ptr->grid[0], mi_ptr->grid[1], mi_ptr->size[0], mi_ptr->size[1], mi_ptr->big[0], mi_ptr->big[1]); exit(EXIT_FAILURE); } // Check whether we need to do anything (1x1 grid may be the whole image) mi_ptr->big_image = NULL; mi_ptr->small_image = NULL; mi_ptr->packed = FALSE; grid_size = mi_ptr->grid[0] * mi_ptr->grid[1]; if ((grid_size == 1) && (mi_ptr->size[0] == mi_ptr->big[0]) && (mi_ptr->size[1] == mi_ptr->big[1])) { /* had to remove this as now ANY images acquired with the mosaic sequence need special treatment */ mi_ptr->packed = FALSE; return 1; } /* Update the number of image rows and columns */ acr_insert_short(&group_list, ACR_Rows, mi_ptr->size[1]); acr_insert_short(&group_list, ACR_Columns, mi_ptr->size[0]); /* Get image image index info (number of slices in file) */ mi_ptr->slice_count = acr_find_int(group_list, EXT_Slices_in_file, 1); /* sub_images is now just the number of mosaic elements, even if * they don't all contain slices */ mi_ptr->sub_images = mi_ptr->grid[0] * mi_ptr->grid[1]; /* Get the pixel size. */ dicom_read_pixel_size(group_list, pixel_spacing); /* Get step between slices */ separation = acr_find_double(group_list, ACR_Slice_thickness, 0.0); if (separation == 0.0) { separation = acr_find_double(group_list, ACR_Spacing_between_slices, 1.0); } /* get image normal vector * (need to compute based on dicom field, which gives * unit vectors for row and column direction) * TODO: This code (and other code in this function) could probably * be broken out and made redundant with other code in the converter. */ if (dicom_read_orientation(group_list, RowColVec)) { memcpy(dircos[VCOLUMN], RowColVec, sizeof(*RowColVec) * WORLD_NDIMS); memcpy(dircos[VROW], &RowColVec[3], sizeof(*RowColVec) * WORLD_NDIMS); /* compute slice normal as cross product of row/column unit vectors * (should check for unit length?) */ mi_ptr->normal[XCOORD] = dircos[VCOLUMN][YCOORD] * dircos[VROW][ZCOORD] - dircos[VCOLUMN][ZCOORD] * dircos[VROW][YCOORD]; mi_ptr->normal[YCOORD] = dircos[VCOLUMN][ZCOORD] * dircos[VROW][XCOORD] - dircos[VCOLUMN][XCOORD] * dircos[VROW][ZCOORD]; mi_ptr->normal[ZCOORD] = dircos[VCOLUMN][XCOORD] * dircos[VROW][YCOORD] - dircos[VCOLUMN][YCOORD] * dircos[VROW][XCOORD]; } /* compute slice-to-slice step vector */ for (i = 0; i < WORLD_NDIMS; i++) { mi_ptr->step[i] = separation * mi_ptr->normal[i]; } /* Get position and correct to first slice */ if (!dicom_read_position(group_list, 0, mi_ptr->position)) { if (G.Debug) { printf("WARNING: No image position found\n"); } mi_ptr->position[XCOORD] = mi_ptr->position[YCOORD] = mi_ptr->position[ZCOORD] = 0.0; } if (G.Debug >= HI_LOGGING) { printf(" step %.3f %.3f %.3f pos %.3f %.3f %.3f normal %.3f,%.3f,%.3f\n", mi_ptr->step[0], mi_ptr->step[1], mi_ptr->step[2], mi_ptr->position[0], mi_ptr->position[1], mi_ptr->position[2], mi_ptr->normal[0], mi_ptr->normal[1], mi_ptr->normal[2] ); } old = old_mosaic_ordering(group_list); if (old) { /*old behavior*/ if (G.Debug) { printf("Performing old position correction (%d).\n", mi_ptr->mosaic_seq); } if (mi_ptr->mosaic_seq != MOSAIC_SEQ_INTERLEAVED) { if (is_numaris3(group_list)) { for (i = 0; i < WORLD_NDIMS; i++) { mi_ptr->position[i] -= (double) (mi_ptr->sub_images-1) * mi_ptr->step[i]; } } else { /* Numaris 4 mosaic correction: * - position given is edge of huge slice constructed as if * real slice was at center of mosaic * - mi_ptr->big[0,1] are number of columns and rows of mosaic * - mi_ptr->size[0,1] are number of columns and rows of sub-image */ if (G.Debug >= HI_LOGGING) { printf(" big = %d,%d, size=%d,%d spacing %f,%f\n", mi_ptr->big[0], mi_ptr->big[1], mi_ptr->size[0], mi_ptr->size[1], pixel_spacing[0], pixel_spacing[1]); } for (i = 0; i < WORLD_NDIMS; i++) { /* Correct offset from mosaic Center */ mi_ptr->position[i] += (double) ((dircos[VCOLUMN][i] * mi_ptr->big[0] * pixel_spacing[0]/2.0) + (dircos[VROW][i] * mi_ptr->big[1] * pixel_spacing[1]/2)); /* Move from center to corner of slice */ mi_ptr->position[i] -= ((dircos[VCOLUMN][i] * mi_ptr->size[0] * pixel_spacing[0]/2.0) + (dircos[VROW][i] * mi_ptr->size[1] * pixel_spacing[1]/2.0)); } } } } else{ /*new behavior*/ if (G.Debug) { printf("Performing new position correction.\n"); } for (i = 0; i < WORLD_NDIMS; i++) { /* Correct offset from mosaic Center */ mi_ptr->position[i] += (double) ((dircos[VCOLUMN][i] * mi_ptr->big[0] * pixel_spacing[0]/2.0) + (dircos[VROW][i] * mi_ptr->big[1] * pixel_spacing[1]/2)); /* Move from center to corner of slice */ mi_ptr->position[i] -= ((dircos[VCOLUMN][i] * mi_ptr->size[0] * pixel_spacing[0]/2.0) + (dircos[VROW][i] * mi_ptr->size[1] * pixel_spacing[1]/2.0)); } } if (G.Debug >= HI_LOGGING) { printf(" corrected position %.3f %.3f %.3f\n", mi_ptr->position[0], mi_ptr->position[1], mi_ptr->position[2]); } /* Now that we've corrected the _position_, we still might need to correct * the pixel spacing. For some gadawful reason, some (but not all) Siemens * mosaic files store the pixel spacing such that it is scaled by the ratio * between the mosaic image size and the subimage size. The best way I can * think of to detect this is by comparing the field of view to the product * of the pixel spacing and the actual subimage size, and if they are * wildly different, assume that we need to perform the scaling... */ element = acr_find_group_element(group_list, SPI_Field_of_view); if (element != NULL) { double fov[2]; /* field of view (row/column) */ acr_get_element_numeric_array(element, 2, fov); if (fov[0] != 0.0 && fov[1] != 0.0) { double derived_spacing[2]; int ratio[2]; char str_buf[128]; /* Calculate the actual spacing required to cover the field of view. */ derived_spacing[0] = fov[0] / mi_ptr->size[0]; derived_spacing[1] = fov[1] / mi_ptr->size[1]; ratio[0] = (int) rint(derived_spacing[0] / pixel_spacing[0]); ratio[1] = (int) rint(derived_spacing[1] / pixel_spacing[1]); /* If the ratio of the derived spacing to the pixel spacing is * the same as the ratio between the edge length of the large * and small images, adopt the derived spacing as the correct * value. */ if (ratio[0] == (mi_ptr->big[0] / mi_ptr->size[0]) || ratio[1] == (mi_ptr->big[1] / mi_ptr->size[1])) { if (G.Debug) { printf("Updating mosaic pixel spacing from %f,%f to %f,%f\n", pixel_spacing[0],pixel_spacing[1], derived_spacing[0],derived_spacing[1]); } /* Store the updated pixel spacing in the group list. */ sprintf(str_buf, "%.15g\\%.15g", derived_spacing[0], derived_spacing[1]); acr_insert_string(&group_list, ACR_Pixel_size, str_buf); } } } if (!load_image) { mi_ptr->big_image = NULL; mi_ptr->small_image = NULL; } else { /* Steal the image element from the group list */ mi_ptr->packed = TRUE; mi_ptr->big_image = acr_find_group_element(group_list, ACR_Pixel_data); if (mi_ptr->big_image == NULL) { fprintf(stderr, "Couldn't find an image\n"); exit(EXIT_FAILURE); } group_id = acr_get_element_group(mi_ptr->big_image); element_id = acr_get_element_element(mi_ptr->big_image); acr_group_steal_element(acr_find_group(group_list, group_id), mi_ptr->big_image); /* Add a small image */ new_image_size = mi_ptr->size[0] * mi_ptr->size[1] * mi_ptr->pixel_size; data = malloc(new_image_size); CHKMEM(data); mi_ptr->small_image = acr_create_element(group_id, element_id, acr_get_element_vr(mi_ptr->big_image), new_image_size, data); acr_set_element_vr(mi_ptr->small_image, acr_get_element_vr(mi_ptr->big_image)); copy_element_properties(mi_ptr->small_image, mi_ptr->big_image); acr_insert_element_into_group_list(&group_list, mi_ptr->small_image); } /* Return number of sub-images in this image */ return mi_ptr->sub_images; } static int mosaic_insert_subframe(Acr_Group group_list, Mosaic_Info *mi_ptr, int iimage, int load_image) { int irow; int idim; int nbyte; int isub; int jsub; char *new; char *old; long old_offset; long new_offset; double position[WORLD_NDIMS]; string_t string; int islice; int oldb=1; if (G.Debug >= HI_LOGGING) { printf("mosaic_insert_subframe(%lx, %lx, %d, %d)\n", (unsigned long)group_list, (unsigned long)mi_ptr, iimage, load_image); } /* Figure out what to do based upon the mosaic sequencing. */ oldb = old_mosaic_ordering(group_list); if (mi_ptr->mosaic_seq == MOSAIC_SEQ_INTERLEAVED && oldb) { //old behavior /* For interleaved sequences, we have to map the odd slices to * the range slice_count/2..slice_count-1 and the even slices * from zero to slice_count/2-1 */ if (iimage & 1) { /* Odd?? */ islice = (mi_ptr->slice_count / 2) + (iimage / 2); } else { islice = iimage / 2; } } else { if (mi_ptr->slice_inverted) { islice = mi_ptr->slice_count - iimage - 1; } else { /* Otherwise, just use the image number without modification for * ascending or unknown slice ordering. */ islice = iimage; } } #if 0 if (is_numaris3(group_list)) { islice = mi_ptr->slice_count - islice - 1; } #endif /* Check the image number */ if ((iimage < 0) || (iimage > mi_ptr->sub_images)) { fprintf(stderr, "Invalid image number to send: %d of %d\n", iimage, mi_ptr->sub_images); exit(EXIT_FAILURE); } /* Update the index */ acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) iimage + 1); /* Update the position */ for (idim = 0; idim < WORLD_NDIMS; idim++) { position[idim] = mi_ptr->position[idim] + (double) iimage * mi_ptr->step[idim]; } /* If the sequence is descending, invert the slice coordinate. * This involves subtracting the step * index from the mosaic * slice position. */ /*This should already have been taken care of in sort_dimensions above*/ /*if (mi_ptr->mosaic_seq == MOSAIC_SEQ_DESCENDING) { position[ZCOORD] = mi_ptr->position[ZCOORD] - (double) islice * mi_ptr->step[ZCOORD]; }*/ sprintf(string, "%.15g\\%.15g\\%.15g", position[XCOORD], position[YCOORD], position[ZCOORD]); acr_insert_string(&group_list, ACR_Image_position_patient, string); acr_insert_string(&group_list, SPI_Image_position, string); /* HMM - is this necessary?? */ if (is_numaris3(group_list)) { update_coordinate_info(group_list); } if (G.Debug >= HI_LOGGING) { printf(" position %s\n", string); } if (load_image) { /* Figure out the sub-image indices */ isub = islice % mi_ptr->grid[0]; jsub = islice / mi_ptr->grid[0]; /* Get pointers */ old = acr_get_element_data(mi_ptr->big_image); new = acr_get_element_data(mi_ptr->small_image); /* Copy the image */ nbyte = mi_ptr->size[0] * mi_ptr->pixel_size; for (irow = 0; irow < mi_ptr->size[1]; irow++) { old_offset = isub * mi_ptr->size[0] + (jsub * mi_ptr->size[1] + irow) * mi_ptr->big[0]; old_offset *= mi_ptr->pixel_size; new_offset = (irow * mi_ptr->size[0]) * mi_ptr->pixel_size; memcpy(&new[new_offset], &old[old_offset], nbyte); } /* Reset the byte order and VR encoding. This will be modified on each * send according to what the connection needs. */ copy_element_properties(mi_ptr->small_image, mi_ptr->big_image); } return 1; } static void mosaic_cleanup(Mosaic_Info *mi_ptr) { if (mi_ptr) { if (mi_ptr->packed && mi_ptr->big_image != NULL) { acr_delete_element(mi_ptr->big_image); mi_ptr->packed = FALSE; mi_ptr->big_image = NULL; } } } /************************************************************************ * Multiframe functions, which mimic the behavior of the older mosaic * functions. This is not yet a complete or correct implementation of * multiframe DICOM - see the NOTE: above! */ /* ----------------------------- MNI Header ----------------------------------- @NAME : multiframe_init() @INPUT : group_list - the list of DICOM groups/elements that make up this file. int load_image - a boolean value, non-zero if the function should actually load the data into memory. @OUTPUT : mfi_ptr - a pointer to a Multiframe_Info structure that will contain information used to expand this multiframe file into a series of slices. @RETURNS : void @DESCRIPTION: Initialize a multiframe conversion process. @METHOD : @GLOBALS : @CALLS : @CREATED : June 3, 2005 Bert Vincent @MODIFIED : ---------------------------------------------------------------------------- */ static void multiframe_init(Acr_Group group_list, Multiframe_Info *mfi_ptr, int load_image) { int grp_id; int elm_id; void *data_ptr; Acr_Element element; int i; Acr_Double spacing; double RowColVec[6]; double dircos[VOL_NDIMS][WORLD_NDIMS]; int rows; int cols; int pixel_size; if (G.Debug >= HI_LOGGING) { printf("multiframe_init(%lx, %lx, %d)\n", (unsigned long) group_list, (unsigned long) mfi_ptr, load_image); } cols = acr_find_int(group_list, ACR_Columns, 1); rows = acr_find_int(group_list, ACR_Rows, 1); pixel_size = (acr_find_int(group_list, ACR_Bits_allocated, 16) - 1) / 8 + 1; /* Get image image index info (number of slices in file) */ mfi_ptr->frame_count = acr_find_int(group_list, ACR_Number_of_frames, 1); /* Get spacing between slices */ spacing = acr_find_double(group_list, ACR_Slice_thickness, 0.0); if (spacing == 0.0) { spacing = acr_find_double(group_list, ACR_Spacing_between_slices, 1.0); } /* get image normal vector * (need to compute based on dicom field, which gives * unit vectors for row and column direction) * TODO: This code (and other code in this function) could probably * be broken out and made redundant with other code in the converter. */ if (dicom_read_orientation(group_list, RowColVec)) { memcpy(dircos[VCOLUMN], RowColVec, sizeof(*RowColVec) * WORLD_NDIMS); memcpy(dircos[VROW], &RowColVec[3], sizeof(*RowColVec) * WORLD_NDIMS); convert_dicom_coordinate(dircos[VROW]); convert_dicom_coordinate(dircos[VCOLUMN]); /* compute slice normal as cross product of row/column unit vectors * (should check for unit length?) */ mfi_ptr->normal[XCOORD] = dircos[VCOLUMN][YCOORD] * dircos[VROW][ZCOORD] - dircos[VCOLUMN][ZCOORD] * dircos[VROW][YCOORD]; mfi_ptr->normal[YCOORD] = dircos[VCOLUMN][ZCOORD] * dircos[VROW][XCOORD] - dircos[VCOLUMN][XCOORD] * dircos[VROW][ZCOORD]; mfi_ptr->normal[ZCOORD] = dircos[VCOLUMN][XCOORD] * dircos[VROW][YCOORD] - dircos[VCOLUMN][YCOORD] * dircos[VROW][XCOORD]; } /* If the normal is unreliable, use a default value. */ if (mfi_ptr->normal[XCOORD] == mfi_ptr->normal[YCOORD] && mfi_ptr->normal[XCOORD] == mfi_ptr->normal[ZCOORD]) { mfi_ptr->normal[ZCOORD] = -1.0; mfi_ptr->normal[YCOORD] = 0.0; mfi_ptr->normal[XCOORD] = 0.0; } /* Compute slice-to-slice step vector */ for (i = 0; i < WORLD_NDIMS; i++) { mfi_ptr->step[i] = spacing * mfi_ptr->normal[i]; } /* Get position and correct to first slice */ if (!dicom_read_position(group_list, 0, mfi_ptr->position)) { if (G.Debug) { printf("WARNING: No image position found\n"); } mfi_ptr->position[XCOORD] = mfi_ptr->position[YCOORD] = mfi_ptr->position[ZCOORD] = 0.0; } convert_dicom_coordinate(mfi_ptr->position); if (G.Debug >= HI_LOGGING) { printf(" step %.3f %.3f %.3f position %.3f %.3f %.3f\n", mfi_ptr->step[0], mfi_ptr->step[1], mfi_ptr->step[2], mfi_ptr->position[0], mfi_ptr->position[1], mfi_ptr->position[2]); } if (!load_image) { mfi_ptr->big_image = NULL; mfi_ptr->sub_image = NULL; } else { /* We need to load the image (we're probably on the second pass). */ /* Steal the image element from the group list */ element = acr_find_group_element(group_list, ACR_Pixel_data); if (element == NULL) { fprintf(stderr, "Couldn't find an image\n"); exit(EXIT_FAILURE); } mfi_ptr->big_image = element; /* Save pointer to pixel data element. */ grp_id = acr_get_element_group(element); elm_id = acr_get_element_element(element); acr_group_steal_element(acr_find_group(group_list, grp_id), element); /* Add a small image */ mfi_ptr->frame_size = rows * cols * pixel_size; data_ptr = malloc(mfi_ptr->frame_size); CHKMEM(data_ptr); mfi_ptr->sub_image = acr_create_element(grp_id, elm_id, acr_get_element_vr(element), mfi_ptr->frame_size, data_ptr); copy_element_properties(mfi_ptr->sub_image, mfi_ptr->big_image); acr_insert_element_into_group_list(&group_list, mfi_ptr->sub_image); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : multiframe_insert_subframe() @INPUT : group_list - the list of DICOM groups/elements that make up this file. mfi_ptr - a pointer to a Multiframe_Info structure that will contain information used to expand this multiframe file into a series of slices. int iimage - the index of the subimage to parse and possibly to load. int load_image - a boolean value, non-zero if the function should actually load the data into memory. @OUTPUT : mfi_ptr - may be modified by the function. @RETURNS : void @DESCRIPTION: This function decomposes a multiframe DICOM image into a series of single-frame images. Modifies the group_list to include updated image and position information. @METHOD : @GLOBALS : @CALLS : @CREATED : June 3, 2005 Bert Vincent @MODIFIED : ---------------------------------------------------------------------------- */ static void multiframe_insert_subframe(Acr_Group group_list, Multiframe_Info *mfi_ptr, int iframe, int load_image) { int idim; char *new_ptr; char *old_ptr; double position[WORLD_NDIMS]; string_t string; int result; if (G.Debug >= HI_LOGGING) { printf("multiframe_insert_subframe(%lx, %lx, %d, %d)\n", (unsigned long)group_list, (unsigned long)mfi_ptr, iframe, load_image); } /* Check the frame number */ if ((iframe < 0) || (iframe > mfi_ptr->frame_count)) { fprintf(stderr, "Invalid image number to send: %d of %d\n", iframe, mfi_ptr->frame_count); exit(EXIT_FAILURE); } /* Update the index in the file's group list. */ acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) iframe + 1); result = dicom_read_position(group_list, iframe, position); convert_dicom_coordinate(position); if (result != DICOM_POSITION_LOCAL) { /* If either no position was found for this frame number, or if * only a global position was found, we need to update the * position for this particular frame number. * * If a local position is found, as in some multiframe files, * this step is unnecessary and possibly wrong. */ for (idim = 0; idim < WORLD_NDIMS; idim++) { position[idim] = mfi_ptr->position[idim] + (double) iframe * mfi_ptr->step[idim]; } } sprintf(string, "%.15g\\%.15g\\%.15g", position[XCOORD], position[YCOORD], position[ZCOORD]); acr_insert_string(&group_list, ACR_Image_position_patient, string); if (G.Debug >= HI_LOGGING) { printf(" position %s\n", string); } if (load_image) { /* Get pointers */ old_ptr = acr_get_element_data(mfi_ptr->big_image); new_ptr = acr_get_element_data(mfi_ptr->sub_image); /* Copy the image */ memcpy(new_ptr, /* destination */ old_ptr + (iframe * mfi_ptr->frame_size), /* source */ mfi_ptr->frame_size); /* length */ /* Reset the byte order and VR encoding. This will be modified * on each send according to what the connection needs. */ copy_element_properties(mfi_ptr->sub_image, mfi_ptr->big_image); } } static void multiframe_cleanup(Multiframe_Info *mfi_ptr) { if (mfi_ptr) { if (mfi_ptr->big_image != NULL) { acr_delete_element(mfi_ptr->big_image); mfi_ptr->big_image = NULL; } } } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/siemens_header_table.h0000644000175000000620000001502312574624760023626 0ustar stevestaffSiemens_hdr_entry Siemens_hdr_table[] = { {0x0008, 0x0020, &IMA_hdr.G08.StudyDate, create_ima_date_t_element, 1}, {0x0008, 0x0022, &IMA_hdr.G08.AcquisitionDate, create_ima_date_t_element, 1}, {0x0008, 0x0023, &IMA_hdr.G08.ContentDate, create_ima_date_t_element, 1}, {0x0008, 0x0030, &IMA_hdr.G08.StudyTime, create_ima_time_t_element, 1}, {0x0008, 0x0032, &IMA_hdr.G08.AcquisitionTime, create_ima_time_t_element, 1}, {0x0008, 0x0033, &IMA_hdr.G08.ContentTime, create_ima_time_t_element, 1}, {0x0008, 0x0060, &IMA_hdr.G08.Modality, create_modality_element, 1}, {0x0008, 0x0070, &IMA_hdr.G08.Manufacturer, create_char_element, N_MANUFACTURER+1}, {0x0008, 0x0080, &IMA_hdr.G08.InstitutionName, create_char_element, N_STRING+1}, {0x0008, 0x0090, &IMA_hdr.G08.PhysicianName, create_char_element, N_STRING+1}, {0x0008, 0x1010, &IMA_hdr.G08.StationName, create_char_element, N_STRING+1}, {0x0008, 0x1030, &IMA_hdr.G08.StudyDescription, create_char_element, N_STRING+1}, {0x0008, 0x1080, &IMA_hdr.G08.AdmittingDiagnoses, create_char_element, N_DIAGNOSIS+1}, {0x0008, 0x1090, &IMA_hdr.G08.ModelName, create_char_element, N_STRING+1}, {0x0010, 0x0010, &IMA_hdr.G10.PatientName, create_char_element, N_STRING+1}, {0x0010, 0x0020, &IMA_hdr.G10.PatientID, create_char_element, N_PATIENTID+1}, {0x0010, 0x0030, &IMA_hdr.G10.PatientDOB, create_ima_date_t_element, 1}, {0x0010, 0x0040, &IMA_hdr.G10.PatientSex, create_sex_element, 1}, {0x0010, 0x1005, &IMA_hdr.G10.PatientBirthName, create_char_element, N_STRING+1}, {0x0010, 0x1010, &IMA_hdr.G10.PatientAge, create_age_element, 1}, {0x0010, 0x1030, &IMA_hdr.G10.PatientWeight, create_long_element, 1}, {0x0018, 0x0050, &IMA_hdr.G18.SliceThickness, create_double_element, 1}, {0x0018, 0x0080, &IMA_hdr.G18.RepetitionTime, create_double_element, 1}, {0x0018, 0x0081, &IMA_hdr.G18.EchoTime, create_double_element, 1}, {0x0018, 0x0082, &IMA_hdr.G18.InversionTime, create_double_element, 1}, {0x0018, 0x0083, &IMA_hdr.G18.NumberOfAverages, create_long_element, 1}, {0x0018, 0x0084, &IMA_hdr.G18.ImagingFrequency, create_double_element, 1}, {0x0018, 0x0085, &IMA_hdr.G18.ImagedNucleus, create_char_element, N_NUCLEUS+1}, {0x0018, 0x0086, &IMA_hdr.G18.EchoNumber, create_long_element, 1}, {0x0018, 0x0090, &IMA_hdr.G18.DataCollectionDiameter, create_long_element, 1}, {0x0018, 0x1000, &IMA_hdr.G18.SerialNumber, create_char_element, N_STRING+1}, {0x0018, 0x1020, &IMA_hdr.G18.SoftwareVersion, create_char_element, N_SWVERSION+1}, {0x0018, 0x1200, &IMA_hdr.G18.CalibrationDate, create_ima_date_t_element, 1}, {0x0018, 0x1201, &IMA_hdr.G18.CalibrationTime, create_ima_time_t_element, 1}, {0x0018, 0x1250, &IMA_hdr.G18.ReceiveCoilName, create_char_element, N_STRING+1}, {0x0018, 0x5100, &IMA_hdr.G18.PatientPosition, create_ima_position_t_element, 1}, {0x0019, 0x1060, &IMA_hdr.G19.NumberOfDataBytes, create_long_element, 1}, {0x0019, 0x1220, &IMA_hdr.G19.FourierLinesNominal, create_long_element, 1}, {0x0019, 0x1226, &IMA_hdr.G19.FourierLinesAfterZero, create_long_element, 1}, {0x0019, 0x1228, &IMA_hdr.G19.FirstMeasuredFourierLine, create_long_element, 1}, {0x0019, 0x1230, &IMA_hdr.G19.AcquisitionColumns, create_long_element, 1}, {0x0019, 0x1231, &IMA_hdr.G19.ReconstructionColumns, create_long_element, 1}, {0x0019, 0x1250, &IMA_hdr.G19.NumberOfAverages, create_long_element, 1}, {0x0019, 0x1260, &IMA_hdr.G19.FlipAngle, create_double_element, 1}, {0x0019, 0x1270, &IMA_hdr.G19.NumberOfPrescans, create_long_element, 1}, {0x0019, 0x1290, &IMA_hdr.G19.SaturationRegions, create_long_element, 1}, {0x0019, 0x1412, &IMA_hdr.G19.MagneticFieldStrength, create_double_element, 1}, {0x0020, 0x0010, &IMA_hdr.G20.StudyID, create_long_element, 1}, {0x0020, 0x0012, &IMA_hdr.G20.AcquisitionNumber, create_long_element, 1}, {0x0020, 0x0013, &IMA_hdr.G20.InstanceNumber, create_long_element, 1}, {0x0020, 0x0050, &IMA_hdr.G20.Location, create_long_element, 1}, {0x0020, 0x0060, &IMA_hdr.G20.Laterality, create_laterality_element, 1}, {0x0020, 0x1001, &IMA_hdr.G20.AcquisitionsInSeries, create_long_element, 1}, {0x0021, 0x1120, &IMA_hdr.G21.FieldOfView, create_field_of_view_t_element, 1}, {0x0021, 0x1122, &IMA_hdr.G21.ImageMagnificationFactor, create_double_element, 1}, {0x0021, 0x1130, &IMA_hdr.G21.ViewDirection, create_view_direction_t_element, 1}, {0x0021, 0x1132, &IMA_hdr.G21.RestDirection, create_rest_direction_t_element, 1}, {0x0021, 0x1160, &IMA_hdr.G21.ImagePosition, create_ima_vector_t_element, 1}, {0x0021, 0x1161, &IMA_hdr.G21.ImageNormal, create_ima_vector_t_element, 1}, {0x0021, 0x1163, &IMA_hdr.G21.ImageDistance, create_double_element, 1}, {0x0021, 0x116a, &IMA_hdr.G21.ImageRow, create_ima_vector_t_element, 1}, {0x0021, 0x116b, &IMA_hdr.G21.ImageColumn, create_ima_vector_t_element, 1}, {0x0021, 0x1170, &IMA_hdr.G21.OrientationSet1, create_ima_orientation_t_element, 1}, {0x0021, 0x1171, &IMA_hdr.G21.OrientationSet2, create_ima_orientation_t_element, 1}, {0x0021, 0x1180, &IMA_hdr.G21.StudyName, create_char_element, N_STRING+1}, {0x0021, 0x1330, &IMA_hdr.G21.NumberOf3DRawPartNom, create_long_element, 1}, {0x0021, 0x1331, &IMA_hdr.G21.NumberOf3DRawPartCur, create_long_element, 1}, {0x0021, 0x1334, &IMA_hdr.G21.NumberOf3DImaPart, create_long_element, 1}, {0x0021, 0x1336, &IMA_hdr.G21.Actual3DImaPartNumber, create_long_element, 1}, {0x0021, 0x1339, &IMA_hdr.G21.SlabThickness, create_double_element, 1}, {0x0021, 0x1340, &IMA_hdr.G21.NumberOfSlicesNom, create_long_element, 1}, {0x0021, 0x1341, &IMA_hdr.G21.NumberOfSlicesCur, create_long_element, 1}, {0x0021, 0x1342, &IMA_hdr.G21.CurrentSliceNumber, create_long_element, 1}, {0x0021, 0x1343, &IMA_hdr.G21.CurrentGroupNumber, create_long_element, 1}, {0x0021, 0x134f, &IMA_hdr.G21.SliceOrder, create_slice_order_element, 1}, {0x0021, 0x1370, &IMA_hdr.G21.NumberOfEchoes, create_long_element, 1}, {0x0028, 0x0005, &IMA_hdr.G28.ImageDimension, create_short_element, 1}, {0x0028, 0x0010, &IMA_hdr.G28.Rows, create_short_element, 1}, {0x0028, 0x0011, &IMA_hdr.G28.Columns, create_short_element, 1}, {0x0028, 0x0030, &IMA_hdr.G28.PixelSpacing, create_pixel_spacing_t_element, 1}, {0x0028, 0x0100, &IMA_hdr.G28.BitsAllocated, create_short_element, 1}, {0x0028, 0x0101, &IMA_hdr.G28.BitsStored, create_short_element, 1}, {0x0028, 0x0102, &IMA_hdr.G28.HighBit, create_short_element, 1}, {0x0028, 0x0103, &IMA_hdr.G28.PixelRepresentation, create_short_element, 1}, {0x0028, 0x1050, &IMA_hdr.G28.WindowCenter, create_window_t_element, 1}, {0x0028, 0x1051, &IMA_hdr.G28.WindowWidth, create_window_t_element, 1}, {0x0028, 0x1052, &IMA_hdr.G28.RescaleIntercept, create_long_element, 1}, {0x0028, 0x1053, &IMA_hdr.G28.RescaleSlope, create_long_element, 1}, {0, 0, NULL, NULL, 0} }; minc-tools-2.3.00+dfsg/conversion/dcm2mnc/spi_element_defs.h0000644000175000000620000001442712574624760023020 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : spi_element_defs.h @DESCRIPTION: Element definitions for Siemens "Standard Product Interconnect" @METHOD : @GLOBALS : @CALLS : @CREATED : November 23, 1993 (Peter Neelin) @MODIFIED : @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. ---------------------------------------------------------------------------- */ /* Element id's for SPI */ /* Most of this information is available at David Clunie's medical imaging * website (www.dclunie.com). */ GLOBAL_ELEMENT(SPI_PMS_grp19_tag , 0x0019, 0x0010, CS); GLOBAL_ELEMENT(SPI_PMS_field_of_view , 0x0019, 0x1000, DS); GLOBAL_ELEMENT(SPI_PMS_cc_angle , 0x0019, 0x1005, DS); GLOBAL_ELEMENT(SPI_PMS_ap_angle , 0x0019, 0x1006, DS); GLOBAL_ELEMENT(SPI_PMS_lr_angle , 0x0019, 0x1007, DS); /* 0 undefined, 1 head first, 2 feet first */ GLOBAL_ELEMENT(SPI_PMS_patient_position , 0x0019, 0x1008, CS); /* 0 undefined, 1 supine, 2 prone, 3 left decubitus, 4 right decubitus */ GLOBAL_ELEMENT(SPI_PMS_patient_orientation , 0x0019, 0x1009, CS); /* 0 undefined 1 transverse 2 sagittal 3 coronal */ GLOBAL_ELEMENT(SPI_PMS_slice_orientation , 0x0019, 0x100a, CS); /* caudal-cranial position (MINC Z) */ GLOBAL_ELEMENT(SPI_PMS_cc_position , 0x0019, 0x100b, DS); /* anterior-posterior position (MINC Y) */ GLOBAL_ELEMENT(SPI_PMS_ap_position , 0x0019, 0x100c, DS); /* left-right position (MINC X) */ GLOBAL_ELEMENT(SPI_PMS_lr_position , 0x0019, 0x100d, DS); GLOBAL_ELEMENT(SPI_PMS_slice_count , 0x0019, 0x100f, IS); GLOBAL_ELEMENT(SPI_PMS_flip_angle , 0x0019, 0x101a, DS); /* caudal-cranial position (MINC X) */ GLOBAL_ELEMENT(SPI_PMS_lr_position2 , 0x0019, 0x110b, DS); /* anterior-posterior position (MINC Z) */ GLOBAL_ELEMENT(SPI_PMS_cc_position2 , 0x0019, 0x110c, DS); /* left-right position (MINC Z) */ GLOBAL_ELEMENT(SPI_PMS_ap_position2 , 0x0019, 0x110d, DS); /* These are the actual Siemens values. */ GLOBAL_ELEMENT(SPI_Private_creator , 0x0019, 0x0010, LO); GLOBAL_ELEMENT(SPI_Image_header_type , 0x0019, 0x1008, CS); GLOBAL_ELEMENT(SPI_Diffusion_b_value , 0x0019, 0x100c, IS); GLOBAL_ELEMENT(SPI_Diffusion_gradient_direction , 0x0019, 0x100e, FD); GLOBAL_ELEMENT(SPI_B_matrix , 0x0019, 0x1027, FD); /*added by ilana, seems to exits for software >= VB*/ GLOBAL_ELEMENT(SPI_Number_of_data_bytes , 0x0019, 0x1060, IS); GLOBAL_ELEMENT(SPI_Fourier_lines_nominal , 0x0019, 0x1220, IS); GLOBAL_ELEMENT(SPI_Fourier_lines_after_zero , 0x0019, 0x1226, IS); GLOBAL_ELEMENT(SPI_First_measured_fourier_line , 0x0019, 0x1228, IS); GLOBAL_ELEMENT(SPI_Acquisition_columns , 0x0019, 0x1230, LO); GLOBAL_ELEMENT(SPI_Reconstruction_columns , 0x0019, 0x1231, LO); GLOBAL_ELEMENT(SPI_Number_of_averages , 0x0019, 0x1250, IS); GLOBAL_ELEMENT(SPI_Flip_angle , 0x0019, 0x1260, DS); GLOBAL_ELEMENT(SPI_Number_of_prescans , 0x0019, 0x1270, IS); GLOBAL_ELEMENT(SPI_Saturation_regions , 0x0019, 0x1290, IS); GLOBAL_ELEMENT(SPI_Magnetic_field_strength , 0x0019, 0x1412, DS); GLOBAL_ELEMENT(SPI_Base_raw_matrix_size , 0x0019, 0x14d4, IS); GLOBAL_ELEMENT(SPI_Field_of_view , 0x0021, 0x1120, DS); GLOBAL_ELEMENT(SPI_Image_magnification_factor , 0x0021, 0x1122, DS); GLOBAL_ELEMENT(SPI_View_direction , 0x0021, 0x1130, CS); GLOBAL_ELEMENT(SPI_Rest_direction , 0x0021, 0x1132, CS); GLOBAL_ELEMENT(SPI_Image_position , 0x0021, 0x1160, DS); GLOBAL_ELEMENT(SPI_Image_normal , 0x0021, 0x1161, DS); GLOBAL_ELEMENT(SPI_Image_distance , 0x0021, 0x1163, DS); GLOBAL_ELEMENT(SPI_Image_row , 0x0021, 0x116a, DS); GLOBAL_ELEMENT(SPI_Image_column , 0x0021, 0x116b, DS); GLOBAL_ELEMENT(SPI_Patient_orientation_set1 , 0x0021, 0x1170, CS); GLOBAL_ELEMENT(SPI_Patient_orientation_set2 , 0x0021, 0x1171, CS); GLOBAL_ELEMENT(SPI_Study_name , 0x0021, 0x1180, CS); GLOBAL_ELEMENT(SPI_Study_type , 0x0021, 0x1182, CS); GLOBAL_ELEMENT(SPI_Number_of_3D_raw_partitions_nominal, 0x0021, 0x1330, IS); GLOBAL_ELEMENT(SPI_Number_of_3d_raw_part_cur , 0x0021, 0x1331, IS); GLOBAL_ELEMENT(SPI_Number_of_3D_image_partitions , 0x0021, 0x1334, IS); GLOBAL_ELEMENT(SPI_Actual_3D_partition_number , 0x0021, 0x1336, IS); GLOBAL_ELEMENT(SPI_Slab_thickness , 0x0021, 0x1339, DS); GLOBAL_ELEMENT(SPI_Number_of_slices_nominal , 0x0021, 0x1340, IS); GLOBAL_ELEMENT(SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); GLOBAL_ELEMENT(SPI_Current_slice_number , 0x0021, 0x1342, IS); GLOBAL_ELEMENT(SPI_Order_of_slices , 0x0021, 0x134f, IS); GLOBAL_ELEMENT(SPI_Number_of_echoes , 0x0021, 0x1370, IS); /* These are the two large fields in Siemens files in which many parameters * are dumped. The ASCCONV block is located in the second (0029, 1020), but * many of the DTI parameters are available in the first. */ GLOBAL_ELEMENT(SPI_Private_creator_0029 , 0x0029, 0x0010, LO); GLOBAL_ELEMENT(SPI_Protocol2 , 0x0029, 0x1010, CS); GLOBAL_ELEMENT(SPI_Protocol , 0x0029, 0x1020, CS); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/siemens_to_dicom.c0000644000175000000620000007367712574624760023042 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_to_dicom.c @DESCRIPTION: File containing routines to read in a Siemens vision internal file (.IMA extension) and convert it to a DICOM representation. @METHOD : @GLOBALS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : $Log: siemens_to_dicom.c,v $ @MODIFIED : Revision 1.11 2009-01-20 11:58:13 rotor @MODIFIED : * CMakeLists.txt: updated version @MODIFIED : * Updated Changelog to include releases @MODIFIED : * Warning cleanups below @MODIFIED : * conversion/dcm2mnc/minc_file.c: fixed printf type @MODIFIED : * conversion/dcm2mnc/siemens_to_dicom.c: fixed printf type @MODIFIED : * conversion/ecattominc/machine_indep.c: added string.h and fixed @MODIFIED : 2 fprintf missing format args @MODIFIED : * conversion/micropet/upet2mnc.c: fixed two fprintf format args @MODIFIED : * conversion/minctoecat/ecat_write.c: added string.h @MODIFIED : * conversion/minctoecat/minctoecat.c: added missing argument to fprintf @MODIFIED : * conversion/nifti1/mnc2nii.c: fixed incorrect printf type @MODIFIED : * progs/mincview/invert_raw_image.c: added fwrite checking @MODIFIED : @MODIFIED : Revision 1.10 2008/08/12 05:00:23 rotor @MODIFIED : * large number of changes from Claude (64 bit and updates) @MODIFIED : @MODIFIED : Revision 1.9 2008/01/17 02:33:01 rotor @MODIFIED : * removed all rcsids @MODIFIED : * removed a bunch of ^L's that somehow crept in @MODIFIED : * removed old (and outdated) BUGS file @MODIFIED : @MODIFIED : Revision 1.8 2008/01/12 19:08:14 stever @MODIFIED : Add __attribute__ ((unused)) to all rcsid variables. @MODIFIED : @MODIFIED : Revision 1.7 2006/02/09 20:54:29 bert @MODIFIED : More changes to dcm2mnc @MODIFIED : @MODIFIED : Revision 1.6 2005/04/21 22:32:15 bert @MODIFIED : Continue Siemens IMA code cleanup @MODIFIED : @MODIFIED : Revision 1.5 2005/04/18 16:21:16 bert @MODIFIED : Fix definition of siemens_to_dicom @MODIFIED : @MODIFIED : Revision 1.4 2005/04/05 21:56:47 bert @MODIFIED : Add some conversion functions, remove some more proprietary junk, and improve range-checking on some functions @MODIFIED : @MODIFIED : Revision 1.3 2005/03/03 18:59:16 bert @MODIFIED : Fix handling of image position so that we work with the older field (0020, 0030) as well as the new (0020, 0032) @MODIFIED : @MODIFIED : Revision 1.2 2005/03/02 20:06:23 bert @MODIFIED : Update conversions to reflect simplified header structures and types @MODIFIED : @MODIFIED : Revision 1.1 2005/02/17 16:38:11 bert @MODIFIED : Initial checkin, revised DICOM to MINC converter @MODIFIED : @MODIFIED : Revision 1.1.1.1 2003/08/15 19:52:55 leili @MODIFIED : Leili's dicom server for sonata @MODIFIED : @MODIFIED : Revision 1.1 2001/12/31 17:28:34 rhoge @MODIFIED : adding file to repos - now needed for reading .ima files in directly @MODIFIED : @MODIFIED : Revision 1.2 2000/12/17 01:05:24 rhoge @MODIFIED : temporary activation of offset table printing macro @MODIFIED : @MODIFIED : Revision 1.1.1.1 2000/11/30 02:05:54 rhoge @MODIFIED : imported sources to CVS repository on amoeba @MODIFIED : * Revision 1.4 1998/11/16 19:54:15 neelin * Added definitions for SunOS. * * Revision 1.3 1998/11/13 16:02:09 neelin * Modifications to support packed images and asynchronous transfer. * * Revision 1.2 1997/11/04 14:31:30 neelin * *** empty log message *** * * Revision 1.1 1997/08/11 12:50:53 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include #include #include #include "dcm2mnc.h" #include "siemens_header_defs.h" /* Constants */ #define SIEMENS_IMAGE_OFFSET 6144 /* From dclunie.com */ #define IMAGE_NDIMS 2 /* Types */ #define ELEMENT_FUNC_ARGS \ (int grp_id, int elm_id, void *data, int length) #define DEFINE_ELEMENT_FUNC(name) \ static Acr_Element name ELEMENT_FUNC_ARGS #define DECLARE_ELEMENT_FUNC(name) \ static Acr_Element name ELEMENT_FUNC_ARGS typedef Acr_Element (*Create_Element_Function) ELEMENT_FUNC_ARGS; typedef struct { int grp_id; int elm_id; void *data; Create_Element_Function function; int length; } Siemens_hdr_entry; /* Functions */ static Acr_Element_Id get_elid(int grp_id, int elm_id, Acr_VR_Type vr_code); DECLARE_ELEMENT_FUNC(create_char_element); DECLARE_ELEMENT_FUNC(create_long_element); DECLARE_ELEMENT_FUNC(create_short_element); DECLARE_ELEMENT_FUNC(create_double_element); DECLARE_ELEMENT_FUNC(create_ima_date_t_element); DECLARE_ELEMENT_FUNC(create_ima_time_t_element); DECLARE_ELEMENT_FUNC(create_modality_element); DECLARE_ELEMENT_FUNC(create_sex_element); DECLARE_ELEMENT_FUNC(create_age_element); DECLARE_ELEMENT_FUNC(create_slice_order_element); DECLARE_ELEMENT_FUNC(create_pixel_spacing_t_element); DECLARE_ELEMENT_FUNC(create_window_t_element); DECLARE_ELEMENT_FUNC(create_ima_vector_t_element); DECLARE_ELEMENT_FUNC(create_laterality_element); DECLARE_ELEMENT_FUNC(create_ima_position_t_element); DECLARE_ELEMENT_FUNC(create_rest_direction_t_element); DECLARE_ELEMENT_FUNC(create_view_direction_t_element); DECLARE_ELEMENT_FUNC(create_ima_orientation_t_element); DECLARE_ELEMENT_FUNC(create_field_of_view_t_element); /* Define the table of header values */ ima_header_t IMA_hdr; /* Must define this first */ #include "siemens_header_table.h" /* Now include the table */ /* flag to print offset table, useful in debugging byte pad issues with Linux/sun porting */ /* #define PRINT_OFFSET_TABLE 1 */ /* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_to_dicom @INPUT : filename - name of Siemens internal file read_image - if TRUE, then the image is added to the group list, otherwise it is not read in. @OUTPUT : (none) @RETURNS : Acr-nema group list containing contents of Siemens file @DESCRIPTION: Function to read in a siemens internal format file (.IMA) and store it in an ACR-NEMA group list. @METHOD : @GLOBALS : @CALLS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group siemens_to_dicom(const char *filename, int max_group) { FILE *fp; Siemens_hdr_entry *entry; Acr_Group group_list; Acr_Element element; size_t image_size; long pixel_size; void *image; double flip_angle; Acr_Short rows; Acr_Short cols; int n_slices; #ifdef PRINT_OFFSET_TABLE void *header_ptr; /* debug junk */ void *data_ptr; /* debug junk */ long offset; /* debug junk */ #endif /* Check the structure offsets */ assert(((char *)&IMA_hdr.G08 - (char *)&IMA_hdr) == 0x0000); assert(((char *)&IMA_hdr.G10 - (char *)&IMA_hdr) == 0x0300); assert(((char *)&IMA_hdr.G18 - (char *)&IMA_hdr) == 0x0600); assert(((char *)&IMA_hdr.G19 - (char *)&IMA_hdr) == 0x0780); assert(((char *)&IMA_hdr.G20 - (char *)&IMA_hdr) == 0x0C80); assert(((char *)&IMA_hdr.G21 - (char *)&IMA_hdr) == 0x0E80); assert(((char *)&IMA_hdr.G28 - (char *)&IMA_hdr) == 0x1380); if (G.Debug >= HI_LOGGING) { printf("siemens_to_dicom(%s, %x)\n", filename, max_group); } /* Open the file */ if ((fp = fopen(filename, "rb")) == NULL) { fprintf(stderr, "Error opening file %s\n", filename); return NULL; } /* Read in the header */ if (fread(&IMA_hdr, sizeof(IMA_hdr), 1, fp) != 1) { fprintf(stderr, "Error reading header in %s\n", filename); fclose(fp); return NULL; } /* Get the image if it is needed */ if (max_group >= ACR_IMAGE_GID) { /* Figure out how much space we need for the image */ pixel_size = 2; /* Apparently this never changes?? */ /* Need to byte swap row/col values if needed */ acr_get_short(ACR_BIG_ENDIAN, 1, &IMA_hdr.G28.Rows, &rows); acr_get_short(ACR_BIG_ENDIAN, 1, &IMA_hdr.G28.Columns, &cols); image_size = rows * cols; image = malloc(pixel_size * image_size); CHKMEM(image); /* Read in the image */ if (fseek(fp, (long) SIEMENS_IMAGE_OFFSET, SEEK_SET)) { printf("ERROR: Error finding image in %s\n", filename); fclose(fp); return NULL; } if (fread(image, pixel_size, image_size, fp) != image_size) { printf("ERROR: Error reading image in %s\n", filename); fclose(fp); return NULL; } } /* If (max_group >= ACR_IMAGE_GID) */ /* Close the file */ fclose(fp); /* Loop through the header table, creating a header */ group_list = NULL; for (entry = Siemens_hdr_table; entry->data != NULL; entry++) { #ifdef PRINT_OFFSET_TABLE data_ptr = entry->data; header_ptr = &IMA_hdr; offset = (long) data_ptr - (long) header_ptr; printf("DEBUG: group = 0x%x, element = 0x%x, offset = 0x%lx, length = 0x%x\n", entry->grp_id, entry->elm_id, offset, entry->length); #endif if (entry->function == NULL) { continue; } element = entry->function(entry->grp_id, entry->elm_id, entry->data, entry->length); if (element == NULL) { continue; } acr_insert_element_into_group_list(&group_list, element); } /* Insert flip angle element */ element = acr_find_group_element(group_list, SPI_Flip_angle); if (element != NULL) { flip_angle = acr_get_element_numeric(element); if (flip_angle >= 0.0) { acr_insert_numeric(&group_list, ACR_Flip_angle, flip_angle); } } n_slices = acr_find_int(group_list, SPI_Number_of_slices_nominal, 1); if (n_slices > 1) { Acr_Short acq_cols; int n_acq; int n_avg; acq_cols = acr_find_short(group_list, SPI_Acquisition_columns, acr_find_short(group_list, ACR_Columns, 1)); rows = acr_find_int(group_list, ACR_Rows, 1); acr_insert_long(&group_list, EXT_Mosaic_rows, (Acr_Long)rows); cols = acr_find_int(group_list, ACR_Columns, 1); acr_insert_long(&group_list, EXT_Mosaic_columns, (Acr_Long)cols); /* Don't allow acq_cols greater than actual size! */ if (acq_cols > rows) { n_slices = 1; acq_cols = rows; } if (acq_cols > cols) { n_slices = 1; acq_cols = cols; } acr_insert_long(&group_list, EXT_Slices_in_file, (Acr_Long)n_slices); acr_insert_short(&group_list, EXT_Sub_image_rows, (Acr_Long)acq_cols); acr_insert_short(&group_list, EXT_Sub_image_columns, (Acr_Long)acq_cols); /* We need to set up the ACR_Acquisition (and * ACR_Acquisitions_in_series) objects to make everyone happy. * * TODO: This appears to work for SOME IMA files, but it may * not be correct for all sequences. * * BERT: My latest change here is to examine both values and * use the larger of the two. Jens Pruessner had some IMA files * that had a value for acquisitions_in_series that was correctly * giving the number of time steps, while the nr_averages was 1. * Other files apparently contradict this! */ acr_insert_long(&group_list, ACR_Acquisition, (Acr_Long)acr_find_int(group_list, ACR_Image, 1)); n_avg = acr_find_int(group_list, ACR_Nr_of_averages, 1); n_acq = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); if (n_avg > n_acq) { acr_insert_long(&group_list, ACR_Acquisitions_in_series, (Acr_Long)n_avg); } } /* Insert a series number */ acr_insert_numeric(&group_list, ACR_Series, 1.0); /* Insert appropriate image position and orientation information */ update_coordinate_info(group_list); /* Add the image if it is needed */ if (max_group >= ACR_IMAGE_GID) { /* Insert the image location */ acr_insert_short(&group_list, ACR_Image_location, ACR_IMAGE_GID); /* Add the image. We don't byte-swap here since it will be done automatically when the data is written out. */ element = acr_create_element(ACR_IMAGE_GID, ACR_IMAGE_EID, ACR_VR_OW, image_size * pixel_size, image); acr_insert_element_into_group_list(&group_list, element); /* explicitly label image data as big-endian */ acr_set_element_byte_order(element, ACR_BIG_ENDIAN); } /* if (max_group >= ACR_IMAGE_GID) */ /* Return the group list */ return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_coordinate_info @INPUT : group_list @OUTPUT : group_list @RETURNS : (nothing) @DESCRIPTION: Function to modify the DICOM coordinate information to match the Siemens info. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void update_coordinate_info(Acr_Group group_list) { Acr_Element element; int nrows, ncolumns; int i; double coord[WORLD_NDIMS]; double row[WORLD_NDIMS]; double column[WORLD_NDIMS]; double normal[WORLD_NDIMS]; double pixel_spacing[IMAGE_NDIMS]; string_t string; int n_slices; if (G.Debug >= HI_LOGGING) { printf("update_coordinate_info(%lx)\n", (unsigned long) group_list); } /* Look for the normal vector */ element = acr_find_group_element(group_list, SPI_Image_normal); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, normal) != WORLD_NDIMS)) { normal[XCOORD] = 0.0; normal[YCOORD] = 0.0; normal[ZCOORD] = 1.0; } /* Look for the row vector. */ element = acr_find_group_element(group_list, SPI_Image_row); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, row) != WORLD_NDIMS)) { row[XCOORD] = 1.0; row[YCOORD] = 0.0; row[ZCOORD] = 0.0; } /* Look for the column vector */ element = acr_find_group_element(group_list, SPI_Image_column); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, column) != WORLD_NDIMS)) { column[XCOORD] = 0.0; column[YCOORD] = 1.0; column[ZCOORD] = 0.0; } if (G.Debug >= HI_LOGGING) { printf("R %.3f %.3f %.3f C %.3f %.3f %.3f N %.3f %.3f %.3f\n", row[0], row[1], row[2], column[0], column[1], column[2], normal[0], normal[1], normal[2]); } /* Put in the dicom orientation (patient) field */ sprintf(string, "%.15g\\%.15g\\%.15g\\%.15g\\%.15g\\%.15g", row[XCOORD], -row[YCOORD], -row[ZCOORD], column[XCOORD], -column[YCOORD], -column[ZCOORD]); acr_insert_string(&group_list, ACR_Image_orientation_patient, string); /* Look for the position. */ element = acr_find_group_element(group_list, SPI_Image_position); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, coord) != WORLD_NDIMS)) { coord[XCOORD] = 0.0; coord[YCOORD] = 0.0; coord[ZCOORD] = 0.0; } else { if (G.Debug >= HI_LOGGING) { printf(" old %.3f %.3f %.3f, ", coord[0], coord[1], coord[2]); } } /* Get the number of rows and columns. */ nrows = acr_find_int(group_list, ACR_Rows, 0); ncolumns = acr_find_int(group_list, ACR_Columns, 0); if ((nrows <= 0) || (ncolumns <= 0)) { printf("ERROR: Illegal image size in Siemens IMA file\n"); exit(1); } /* Get the pixel size */ element = acr_find_group_element(group_list, ACR_Pixel_size); if ((element == NULL) || (acr_get_element_numeric_array(element, IMAGE_NDIMS, pixel_spacing) != IMAGE_NDIMS)) { pixel_spacing[0] = pixel_spacing[1] = 1.0; } /* Calculate the position of the first pixel. This coordinate * is still in the Siemens space, not dicom space and will * need to be flipped. Note that ncolumns is used with row, * since they are the size and unit vector of the same * dimension. */ for (i = 0; i < WORLD_NDIMS; i++) { coord[i] -= pixel_spacing[0] * ((double) ncolumns - 1.0) / 2.0 * row[i] + pixel_spacing[1] * ((double) nrows - 1.0) / 2.0 * column[i]; } sprintf(string, "%.15g\\%.15g\\%.15g", coord[0], -coord[1], -coord[2]); acr_insert_string(&group_list, ACR_Image_position_patient, string); if (G.Debug >= HI_LOGGING) { printf(" new %.3f %.3f %.3f\n", coord[0], -coord[1], -coord[2]); } /* Copy non-standard fields to standard fields. */ group_list = copy_spi_to_acr(group_list); /* If this is a Mosaic image, we need to adjust the pixel spacing * to reflect the ratio between the number of mosaic columns * divided the number of image columns. */ n_slices = acr_find_int(group_list, EXT_Slices_in_file, 1); if (n_slices != 1) { int acq_cols = (int)acr_find_short(group_list, SPI_Acquisition_columns, (Acr_Short)ncolumns); if (G.Debug >= HI_LOGGING) { printf("Hmmm... This appears to be a mosaic image %d %d %d\n", acq_cols, ncolumns, n_slices); } /* Mosaic images in IMA format appear to need to have their * pixel spacing scaled up. I don't fully understand why this * should be necessary, but there it is... */ pixel_spacing[0] *= (double) nrows / (double) acq_cols; pixel_spacing[1] *= (double) ncolumns / (double) acq_cols; sprintf(string, "%.15g\\%.15g", pixel_spacing[0], pixel_spacing[1]); acr_insert_string(&group_list, ACR_Pixel_size, string); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : create__element @INPUT : grp_id elm_id data - pointer to data in Siemens header length - number of values in array (if appropriate) @OUTPUT : (none) @RETURNS : New element containing data @DESCRIPTION: Series of functions to convert Siemens vision header types to ACR-NEMA elements @METHOD : @GLOBALS : @CALLS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Element_Id get_elid(int grp_id, int elm_id, Acr_VR_Type vr_code) { static struct Acr_Element_Id elid_struct = {0, 0, ACR_VR_UNKNOWN}; elid_struct.group_id = grp_id; elid_struct.element_id = elm_id; elid_struct.vr_code = vr_code; return &elid_struct; } DEFINE_ELEMENT_FUNC(create_char_element) { char *old, *new; int oldsize, newsize, i; /* Get a pointer to the old string */ old = (char *) data; /* Figure out the length of the new string up to the first NUL. Make * sure that there is a room for an additional NUL if necessary */ for (i = 0; (i < length - 1) && (old[i] != '\0'); i++) ; newsize = ((old[i] == '\0') ? i + 1 : length + 1); oldsize = newsize - 1; if ((newsize % 2) != 1) { /* Assure even length overall */ newsize++; } /* Copy the string, making sure that there is a NUL on the end */ new = malloc(newsize); CHKMEM(new); for (i = 0; i < newsize-1; i++) { if (i < oldsize) new[i] = old[i]; else new[i] = ' '; } new[newsize-1] = '\0'; /* Create the element */ return acr_create_element(grp_id, elm_id, ACR_VR_ST, (long) newsize-1, (void *) new); } DEFINE_ELEMENT_FUNC(create_long_element) { Acr_Long data_out; acr_get_long(ACR_BIG_ENDIAN, 1, data, &data_out); return acr_create_element_long(get_elid(grp_id, elm_id, ACR_VR_IS), data_out); } DEFINE_ELEMENT_FUNC(create_short_element) { Acr_Short data_out; acr_get_short(ACR_BIG_ENDIAN, 1, data, &data_out); return acr_create_element_short(get_elid(grp_id, elm_id, ACR_VR_US), data_out); } DEFINE_ELEMENT_FUNC(create_double_element) { Acr_Double data_out; acr_get_double(ACR_BIG_ENDIAN, 1, data, &data_out); return acr_create_element_numeric(get_elid(grp_id, elm_id, ACR_VR_DS), (double)data_out); } DEFINE_ELEMENT_FUNC(create_ima_date_t_element) { string_t string; ima_date_t *ptr; Acr_Long year; Acr_Long month; Acr_Long day; ptr = (ima_date_t *) data; acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->year, &year); acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->month, &month); acr_get_long(ACR_BIG_ENDIAN, 1, (long *) &ptr->day, &day); if ((year < 1900) || (year > 9999)) return NULL; if ((month < 1) || (month > 12)) return NULL; if ((day < 1) || (day > 31)) return NULL; sprintf(string, "%04d%02d%02d", (int) year, (int) month, (int) day); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DA), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_ima_time_t_element) { string_t string; ima_time_t *ptr; Acr_Long hour; Acr_Long minute; Acr_Long second; Acr_Long msec; ptr = (ima_time_t *) data; /* Convert data from big endian to native: */ acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->hour, &hour); acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->minute, &minute); acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->second, &second); acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->msec, &msec); if (hour >= 24) return NULL; if (minute >= 60) return NULL; if (second >= 60) return NULL; if (msec > 999) return NULL; sprintf(string, "%02d%02d%02d.%03d", (int) hour, (int) minute, (int) second, (int) msec); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_TM), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_modality_element) { Acr_String string; Acr_Long modality; /* Get the appropriate string */ acr_get_long(ACR_BIG_ENDIAN, 1, data, &modality); switch (modality) { case 1: string = "CT"; break; case 2: string = "MR"; break; default: return NULL; } /* Return a new element */ return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_sex_element) { Acr_String string; Acr_Long sex; /* Get the appropriate string */ acr_get_long(ACR_BIG_ENDIAN, 1, data, &sex); switch (sex) { case 1: string = "F "; break; case 2: string = "M "; break; case 3: string = "O "; break; default: return NULL; } /* Return a new element */ return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_age_element) { string_t string; int i; int is_ok; is_ok = 1; /* The age string has a fixed length of 4 */ memcpy(string, data, 4); string[4] = '\0'; for (i = 0; i < 3; i++) { if (string[i] < '0' || string[i] > '9') { is_ok = 0; } } if (string[3] != 'Y' && string[3] != 'M' && string[3] != 'W' && string[3] != 'D') { is_ok = 0; } if (!is_ok) { printf("WARNING: Invalid age field '%s'\n", string); return NULL; } return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_AS), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_slice_order_element) { Acr_String string; Acr_Long slice_order; /* Get the appropriate string */ acr_get_long(ACR_BIG_ENDIAN, 1, data, &slice_order); switch ((ima_slice_order_t)slice_order) { case SO_ASCENDING: string = "ASCENDING "; break; case SO_DESCENDING: string = "DESCENDING "; break; case SO_INTERLEAVED: string = "INTERLEAVED "; break; case SO_NONE: string = "NONE "; break; default: string = "UNDEFINED "; break; } /* Return a new element */ return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_pixel_spacing_t_element) { pixel_spacing_t *ptr; string_t string; Acr_Double row; Acr_Double col; /* Get the pixel sizes */ ptr = (pixel_spacing_t *) data; /* Convert from big endian to native format */ acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->row, &row); acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->col, &col); sprintf(string, "%.15g\\%.15g", row, col); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_window_t_element) { window_t *ptr; string_t string; Acr_Long x; Acr_Long y; ptr = (window_t *) data; /* Get the window info */ /* Convert from big endian to native format */ acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->x, &x); acr_get_long(ACR_BIG_ENDIAN, 1, &ptr->y, &y); /* Surely this isn't right? - AJ - (found while chasing warnings) */ /* sprintf(string, "%ld\\%ld", (double)x, (double)y); */ sprintf(string, "%g\\%g", (double)x, (double)y); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_IS), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_ima_vector_t_element) { ima_vector_t *ptr; string_t string; Acr_Double x, y, z; /* Get the coordinate */ ptr = (ima_vector_t *) data; acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->x, &x); acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->y, &y); acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->z, &z); sprintf(string, "%.15g\\%.15g\\%.15g", x, y, z); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_laterality_element) { Acr_Long laterality; Acr_String string; acr_get_long(ACR_BIG_ENDIAN, 1, data, &laterality); switch (laterality) { case 1: string = "L "; break; case 2: string = ""; break; case 3: string = "R "; break; default: return NULL; } return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_ima_position_t_element) { Acr_Long position; Acr_String string; acr_get_long(ACR_BIG_ENDIAN, 1, data, &position); switch ((ima_position_t)position) { case PP_LEFT: string = "HFL"; break; case PP_RIGHT: string = "HFR"; break; case PP_PRONE: string = "HFP"; break; case PP_SUPINE: string = "HFS"; break; default: string = ""; break; } return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_rest_direction_t_element) { Acr_Long dir; Acr_String string; acr_get_long(ACR_BIG_ENDIAN, 1, data, &dir); switch ((ima_rest_direction_t)dir) { case RD_HEAD: string = "HEAD"; break; case RD_FEET: string = "FEET"; break; default: string = ""; break; } return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } DEFINE_ELEMENT_FUNC(create_view_direction_t_element) { Acr_Long dir; Acr_String string; acr_get_long(ACR_BIG_ENDIAN, 1, data, &dir); switch ((ima_view_direction_t)dir) { case VD_HEAD: string = "HEAD"; break; case VD_FEET: string = "FEET"; break; case VD_AtoP: string = "AtoP"; break; case VD_LtoR: string = "LtoR"; break; case VD_PtoA: string = "PtoA"; break; case VD_RtoL: string = "RtoL"; break; default: string = ""; break; } return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), string); } static void copy_to_space(char *dst_ptr, char *src_ptr, int max_chr) { while (*src_ptr != ' ' && *src_ptr != '\0' && max_chr > 0) { *dst_ptr++ = *src_ptr++; max_chr--; } *dst_ptr = '\0'; } DEFINE_ELEMENT_FUNC(create_ima_orientation_t_element) { ima_orientation_t *po_ptr = (ima_orientation_t *) data; char y[N_ORIENTATION + 1]; char x[N_ORIENTATION + 1]; char z[N_ORIENTATION + 1]; string_t string; copy_to_space(y, po_ptr->y, N_ORIENTATION); copy_to_space(x, po_ptr->x, N_ORIENTATION); copy_to_space(z, po_ptr->z, N_ORIENTATION); sprintf(string, "%s\\%s\\%s", y, x, z); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_CS), (Acr_String)string); } DEFINE_ELEMENT_FUNC(create_field_of_view_t_element) { ima_field_of_view_t *ptr; string_t string; Acr_Double height; Acr_Double width; ptr = (ima_field_of_view_t *) data; acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->height, &height); acr_get_double(ACR_BIG_ENDIAN, 1, &ptr->width, &width); sprintf(string, "%.15g\\%.15g", height, width); return acr_create_element_string(get_elid(grp_id, elm_id, ACR_VR_DS), (Acr_String)string); } minc-tools-2.3.00+dfsg/conversion/dcm2mnc/dcm2mnc.man10000644000175000000620000002160312574624760021435 0ustar stevestaff.TH dcm2mnc 1 "May 03 2005" "$Revision: 1.5 $" "" .SH NAME .B dcm2mnc - convert sets of DICOM files to one or more MINC format files. .SH SYNOPSIS .B dcm2mnc .I [] .B dcm2mnc .I -help .SH DESCRIPTION The .B dcm2mnc command is used to convert DICOM format files to MINC format. DICOM (Digital Imaging and Communications in Medicine) format is used by many vendors of medical imaging equipment as a standard means of data interchange. The DICOM specification is extremely complex and includes protocols for data interchange and communications as well as the specifics of the data format. In most cases, tens or even hundreds of DICOM files must be combined to produce a single MINC file. In normal operation, the .I input-list will consist of the names of one or more files or directories. The program scans all specified input files and directories and attempts to identify groups of files that should be combined into a single MINC file. Once these groups (or "series") of DICOM files are identified, the program analyzes the data for each series and attempts to determine the correct geometry and ancillary information to be incorporated into the MINC file. If all goes well, one or more MINC files will be created in one or more subdirectories created in the specified directory .IR output-dir . These directories and files will be automatically named according to the patient's name, the acquisition date, acquisition time, series identifier, and modality. For a variety of reasons, medical imaging manufacturers have chosen to implement a number of proprietary extensions to the DICOM format. This program attempts to be very general, but it does some extra checking for specific proprietary fields where useful or necessary. However, as device settings change and software is updated, the precise details of the DICOM output for a given device may shift. Different devices from the same manufacturer may produce substantially different DICOM output. .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH "Output file options" .TP .BI -clobber Overwrite existing files. By default, .B dcm2mnc will not write over an existing file. .TP .BI -anon Do not store the patient name in the MINC file. The string "anonymous" will be used instead. Note that all other identifying information will still be stored in the file. .TP .BI -nosplitecho Do not split echoes into separate files. If multiple echoes are present in a series, they will all be stored in a single MINC file with a dimension named "echo". .TP .BI -splitdynamic Split dynamic scans into separate files. Normally dynamic scans are stored in a single MINC file with a "time" dimension. If this option is specified, each time slice will be saved in a separate file. .TP .BI -fname " " Set the format of the output file name. See FILENAMES section for details on this option. .TP .BI -dname " " Set the format of the output subdirectory name. See FILENAMES section for details on this option. Set this to the empty string to avoid creating a subdirectory. .SH "Siemens mosaic specific options" These two options control the manner in which Siemens mosaic data is converted. Siemens scanners commonly represent fMRI data as a "mosaic" of subimages combined into a single large image. Normally these are in what we call "ascending" order, but if your functional image is not converted properly, you may need to specify one of these options. NOTE that the mosaic order is often not the same as the slice acquisition order. .TP .BI -descending The mosaic image is stored in descending order. .TP .BI -interleaved The mosaic image is stored in alternating (odd/even) order. .SH "Other options" .TP .BI -stdin This option tells .B dcm2mnc to read a list of input files from the standard input in addition to any files specified on the command line. .TP .BI -cmd " " This option will apply the given command string to each output file after it is created. Can be used to run gzip or compress on each output file, for example. .TP .BI -minmax Use the values for the largest and smallest pixel value as stored in the DICOM file. This is useful especially with GE PET data, but may be needed to get a quantitatively accurate conversion with other manufacturers. If this option is not specified. .B dcm2mnc uses the full range of the datatype as specified by the number of bits stored per voxel (field # 0028,0101). When this option is specified, the 0028,0106 and 0028,0107 will be used to set the valid range of pixels. .TP .BI -list List files in series, but do not perform conversion. Sometimes useful for verifying the validity of a dataset, and for debugging problems with .BR dcm2mnc . .TP .BI -verbose Verbose operation. Prints a large amount of additional information about the program's operation. This information can probably only be interpreted by someone familiar with both this program and the DICOM standard. .TP .BI -debug Extremely verbose operation. Prints a huge amount of additional information about the program's operation. This information can probably only be interpreted by someone familiar with both this program and the DICOM standard. .TP .BI -usecoordinates This option requests that the conversion rely on the slice coordinates rather than the standard DICOM fields for slice thickness and spacing. It is useful if for some reason the standard DICOM fields for slice thickness and spacing are incorrect. .TP .BI -opts " " This is a private option intended only for debugging purposes. Please avoid using it. .SH "Generic options for all commands" .TP .BI -help Print summary of command\-line options and abort .TP .BI -version Print the program and library versions and abort .SH FILENAMES To avoid naming collisions when converting a large set of input DICOM files to a smaller set of MINC output files, .B dcm2mnc automatically generates the names of output files according to various parameters of the DICOM file information. The normal behavior is to place all of the output files in a subdirectory of the given output directory which has its name derived from the patient's name and the study date and time as follows: patientname\_yyyymmdd\_hhmmss/ The individual files are named according to the patient name, study date and time, series identifer, and modality information as follows: patientname\_yyyymmdd\_hhmmss\_series\_scan\_modality.mnc The optional scan information includes the echo number ('e'), slice number ('sl'), time series position ('d'), phase number ('p'), or chemical shift ('cs'). The optional modality information consists of either the string "_pet" or "_mri". No suffix is added for unrecognized modalities. The .B -fname and .B -dname commands allow the user to override the standard file naming behavior by specifying alternative output directory and file formats. The arguments to these options are template strings that will be expanded to include information from the DICOM sequences in specified locations. Replacements are specified by a '%' character followed by a single alphabetic character, as follows: %N - Name of patient %D - Date of scan %T - Time of scan %S - Study ID (typically 'yyyymmdd.hhmmss') %A - Acquisition or series ID %s - Optional slice label %e - Optional echo number %t - Optional dynamic scan number %p - Optional phase number %c - Optional chemical shift number %m - Optional modality The default file name convention is therefore given by the format string: %N_%D_%T_%A%s%e%t%p%c%m and the default directory name is given by the format string: %N_%D_%T If you wish to avoid creating a subdirectory, you may do so by giving a zero-length string as the argument to the .B -dname option: dcm2mnc -dname \'\' filenames... .SH AUTHORS Peter Neelin and Richard D. Hoge Please direct all complaints and inquiries to Robert Vincent (bert@bic.mni.mcgill.ca) .SH BUGS Probably many. For best results, output files should be checked by a competent human to verify that the conversion was performed properly. DICOM is a very complex format, and it is difficult to anticipate all of the possible combinations of fields and values that may be encountered. If you have a problem, please contact the maintainer. It will be extremely useful if you can provide an example dataset that exhibits the problem you have discovered. .SH "SEE ALSO" For more information on DICOM, visit the NEMA (National Electrical Manufacturer's Association) website at .B http://dicom.nema.org and also see David Clunie's excellent website on medical image formats at .B http://www.dclunie.com Many manufacturers create "DICOM Conformance Statements" for each software release associated with their medical imaging products. These can be useful sources of information. .SH "COPYRIGHTS" Copyrights 1993-2005 by Peter Neelin for the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/conversion/dcm2mnc/ext_element_defs.h0000644000175000000620000000465412574624760023026 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ext_element_defs.h @DESCRIPTION: Element definitions for extra elements needed for mosaics, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : December 2001 (Rick Hoge) @MODIFIED : @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. ---------------------------------------------------------------------------- */ /* Element id's for EXT */ /* bert- These appear to be completely nonstandard - I believe they were * created by Peter and Rick to facilitate communication among the various * pieces of Siemens Mosaic handling code. They do not represent any * externally defined DICOM standard and therefore they may conflict with * other manufacturer's proprietary fields. */ GLOBAL_ELEMENT(EXT_Mosaic_rows , 0x0023, 0x0001, LO); GLOBAL_ELEMENT(EXT_Mosaic_columns , 0x0023, 0x0002, LO); GLOBAL_ELEMENT(EXT_Slices_in_file , 0x0023, 0x0003, LO); GLOBAL_ELEMENT(EXT_Sub_image_rows , 0x0023, 0x0004, US); GLOBAL_ELEMENT(EXT_Sub_image_columns , 0x0023, 0x0005, US); GLOBAL_ELEMENT(EXT_MrProt_dump , 0x0023, 0x0006, LO); GLOBAL_ELEMENT(EXT_Diffusion_b_value , 0x0023, 0x0007, LO); GLOBAL_ELEMENT(EXT_Delay_in_TR , 0x0023, 0x0008, LO); /*add for fMRI scans*/ /*Can't find slice acquisition in any standard fields; using ASCONV header sSliceArray.ucMode ilana*/ /*0x1 ascending 0x2 descending 0x3 interleaved*/ GLOBAL_ELEMENT(EXT_Slice_order , 0x0023, 0x0009, LO); /* Non-zero if slice order is inverted. */ GLOBAL_ELEMENT(EXT_Slice_inverted , 0x0023, 0x000A, LO); /* Slice orientation: 1 -> SAGITTAL, 2 -> CORONAL, 0 -> TRANSVERSE */ GLOBAL_ELEMENT(EXT_Slice_orientation , 0x0023, 0x000B, LO); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/progress.h0000644000175000000620000000010012574624760021336 0ustar stevestaffextern void progress(long index, int end, const char *message); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/pms_element_defs.h0000644000175000000620000001073112574624760023016 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : pms_element_defs.h @DESCRIPTION: Element definitions for Philips Medical Systems (no, really) @METHOD : @GLOBALS : @CALLS : @CREATED : March 11, 2005 @MODIFIED : @COPYRIGHT : Copyright (C) 2005 Robert D. 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. ---------------------------------------------------------------------------- */ /* This information was derived from the publically available "DICOM * Conformance Statement" for the Philips Medical Systems MR Intera * 10.1, 10 April 2003. * * However, not all Philips scanners use the fixed element numbers * implied here. The upper eight bits of the element ID's are not * entirely deterministic. It is necessary to root around in the * image for the private groups. */ #define PMS_PRIVATE_GROUP_ID 0x2001 GLOBAL_ELEMENT(PMS_Chemical_Shift , 0x2001, 0x1001, FL); GLOBAL_ELEMENT(PMS_Chemical_Shift_Number_MR , 0x2001, 0x1002, IS); GLOBAL_ELEMENT(PMS_Diffusion_B_Factor , 0x2001, 0x1003, FL); GLOBAL_ELEMENT(PMS_Diffusion_Direction , 0x2001, 0x1004, CS); GLOBAL_ELEMENT(PMS_Image_Enhanced , 0x2001, 0x1006, CS); GLOBAL_ELEMENT(PMS_Image_Type_ED_ES , 0x2001, 0x1007, CS); GLOBAL_ELEMENT(PMS_Phase_number , 0x2001, 0x1008, IS); /* xx09 is present but not defined */ GLOBAL_ELEMENT(PMS_Slice_Number_MR, /* Slice index in series */ 0x2001, 0x100a, IS); GLOBAL_ELEMENT(PMS_Slice_Orientation, /* SAGITTAL, TRANSVERSAL, e.g. */ 0x2001, 0x100b, CS); /* xx0c, xx0e, xx0f, xx10 are present but not defined */ GLOBAL_ELEMENT(PMS_Diffusion_Echo_Time , 0x2001, 0x1011, FL); GLOBAL_ELEMENT(PMS_Dynamic_Series , 0x2001, 0x1012, CS); GLOBAL_ELEMENT(PMS_EPI_Factor , 0x2001, 0x1013, SL); GLOBAL_ELEMENT(PMS_Number_of_Echoes , 0x2001, 0x1014, SL); GLOBAL_ELEMENT(PMS_Number_of_Locations , 0x2001, 0x1015, SS); GLOBAL_ELEMENT(PMS_Number_of_PC_Locations , 0x2001, 0x1016, SS); GLOBAL_ELEMENT(PMS_Number_of_Phases_MR , 0x2001, 0x1017, SL); GLOBAL_ELEMENT(PMS_Number_of_Slices_MR , 0x2001, 0x1018, SL); GLOBAL_ELEMENT(PMS_Partial_Matrix_Scanned , 0x2001, 0x1019, CS); GLOBAL_ELEMENT(PMS_PC_Velocity , 0x2001, 0x101a, FL); GLOBAL_ELEMENT(PMS_Prepulse_Delay , 0x2001, 0x101b, FL); GLOBAL_ELEMENT(PMS_Prepulse_Type , 0x2001, 0x101c, CS); GLOBAL_ELEMENT(PMS_Reconstruction_Number , 0x2001, 0x101d, IS); /* xx1e is present but not defined */ GLOBAL_ELEMENT(PMS_Respiration_Sync , 0x2001, 0x101f, CS); GLOBAL_ELEMENT(PMS_Scanning_Technique_Description_MR , 0x2001, 0x1020, CS); GLOBAL_ELEMENT(PMS_SPIR , 0x2001, 0x1021, CS); GLOBAL_ELEMENT(PMS_Water_Fat_Shift , 0x2001, 0x1022, FL); /* xx23 xx24 both present but undefined xx23 appears to be the flip angle, as in DICOM (0018,1314) */ GLOBAL_ELEMENT(PMS_Echo_Time_Display , 0x2001, 0x1025, SH); GLOBAL_ELEMENT(PMS_Number_of_Stack_Slices , 0x2001, 0x102d, SS); GLOBAL_ELEMENT(PMS_Stack_Radial_Angle , 0x2001, 0x1032, FL); GLOBAL_ELEMENT(PMS_Stack_Radial_Axis , 0x2001, 0x1033, CS); GLOBAL_ELEMENT(PMS_Stack_Slice_Number , 0x2001, 0x1035, SS); GLOBAL_ELEMENT(PMS_Stack_Type , 0x2001, 0x1036, CS); /* xx52, xx5f both present but undefined */ GLOBAL_ELEMENT(PMS_Number_of_Stacks , 0x2001, 0x1060, SL); GLOBAL_ELEMENT(PMS_Examination_Source , 0x2001, 0x1063, CS); /* additional undefined elements include: xx60-xx62, xx6e, xx7b, xx81-xx8b, 9000 xx83 appears to be the imaging frequency, as in DICOM (0018,0084) */ minc-tools-2.3.00+dfsg/conversion/dcm2mnc/string_to_filename.h0000644000175000000620000000023212574624760023350 0ustar stevestaffextern void string_to_filename(const char *string, char *filename, int maxlen); extern void string_to_initials(char *string, char *filename, int maxlen); minc-tools-2.3.00+dfsg/conversion/dcm2mnc/siemens_header_defs.h0000644000175000000620000004163112574624760023464 0ustar stevestaff#ifndef _SIEMENS_HEADER_DEFS_H_ #define _SIEMENS_HEADER_DEFS_H_ 1 /* CONSTANTS */ #define N_STRING 26 #define N_AGE 4 #define N_DIAGNOSIS 40 #define N_NUCLEUS 8 #define N_MANUFACTURER 8 #define N_ORIENTATION 3 #define N_PATIENTID 12 #define N_SWVERSION 8 typedef double flt64_t; /* ACR-NEMA specific types */ /* Analogous to DICOM field (0028, 0030) */ typedef struct pixel_spacing { flt64_t row; flt64_t col; } pixel_spacing_t; /* Analogous to DICOM fields (0028, 1050) and (0028, 1051) */ typedef struct window { int32_t x; int32_t y; } window_t; /* IMA specific types */ typedef struct ima_date { int32_t year; /* Full year including century */ int32_t month; /* Month from 1(Jan) to 12(Dec) */ int32_t day; /* Day of month from 1 to 31 */ } ima_date_t; typedef struct ima_time { int32_t hour; /* Hour from 0 to 23 */ int32_t minute; /* Minute from 0 to 59 */ int32_t second; /* Second from 0 to 59 */ int32_t msec; /* Milliseconds from 0 to 999 */ } ima_time_t; typedef struct ima_vector { flt64_t x; flt64_t y; flt64_t z; } ima_vector_t; typedef enum ima_slice_order { SO_ASCENDING = 1, SO_DESCENDING = 2, SO_FREE = 3, SO_INTERLEAVED = 4, SO_NONE = 5 } ima_slice_order_t; typedef enum ima_position { PP_LEFT = 1, PP_PRONE = 2, PP_RIGHT = 3, PP_SUPINE = 4, } ima_position_t; typedef enum { RD_FEET = 1, RD_HEAD = 2 } ima_rest_direction_t; typedef enum { VD_FEET = 1, VD_HEAD = 2, VD_AtoP = 3, VD_LtoR = 4, VD_PtoA = 5, VD_RtoL = 6 } ima_view_direction_t; typedef struct { flt64_t height; flt64_t width; } ima_field_of_view_t; typedef struct { char y[N_ORIENTATION + 1]; /* up - down */ char x[N_ORIENTATION + 1]; /* left - right */ char z[N_ORIENTATION + 1]; /* back - front */ } ima_orientation_t; /* * Identifying information group 0x0008 */ struct ima_acr_0008 /* Item# FOffs SOffs */ { ima_date_t StudyDate; /* 0020 0000 */ ima_date_t AcquisitionDate; /* 0022 000C */ ima_date_t ContentDate; /* 0023 0018 */ ima_time_t StudyTime; /* 0030 0024 */ ima_time_t AcquisitionTime; /* 0032 0034 */ ima_time_t ContentTime; /* 0033 0044 */ char pad1[8]; int32_t Modality; /* 0060 005C */ char Manufacturer[N_MANUFACTURER + 1]; /* 0070 0060 */ char InstitutionName[N_STRING + 1]; /* 0080 0069 */ char PhysicianName[N_STRING + 1]; /* 0090 0084 */ char StationName[N_STRING + 1]; /* 1010 009F */ char StudyDescription[N_STRING + 1]; /* 1030 00BA */ char pad2[N_STRING + 1]; char AdmittingDiagnoses[N_DIAGNOSIS + 1]; /* 1080 00F0 */ char ModelName[N_STRING + 1]; /* 1090 0119 */ char pad3[76]; }; /* * Patient information group 0x0010 */ struct ima_acr_0010 /* Item# FOffs SOffs */ { char PatientName[N_STRING + 1]; /* 0010 0300 */ char PatientID[N_PATIENTID + 1]; /* 0020 031B */ ima_date_t PatientDOB; /* 0030 0328 */ int32_t PatientSex; /* 0040 0334 */ char PatientBirthName[N_STRING + 1]; /* 1005 0338 */ char PatientAge[N_AGE + 1]; /* 1010 0353 */ flt64_t PatientSize; /* 1020 0358 */ int32_t PatientWeight; /* 1030 0360 */ char pad1[156]; }; /* * Acquisition information group 0x0018 */ struct ima_acr_0018 { /* Item# FOffs SOffs */ char pad1[8]; /* Dummy padding */ flt64_t SliceThickness; /* 0050 0608 */ char pad2[8]; flt64_t RepetitionTime; /* 0080 0618 */ flt64_t EchoTime; /* 0081 0620 */ flt64_t InversionTime; /* 0082 0628 */ int32_t NumberOfAverages; /* 0083 0630 */ char pad3[4]; /* Dummy padding */ flt64_t ImagingFrequency; /* 0084 0638 */ char pad4[4]; /* 0085 0640 */ int32_t EchoNumber; /* 0086 0644 */ int32_t DataCollectionDiameter; /* 0090 0648 */ char SerialNumber[N_STRING + 1]; /* 1000 064C */ char SoftwareVersion[N_SWVERSION + 1]; /* 1020 0667 */ char pad5[61]; /* Dummy padding */ ima_date_t CalibrationDate; /* 1200 06B0 */ ima_time_t CalibrationTime; /* 1201 06BC */ char pad6[N_STRING + 1]; char ReceiveCoilName[N_STRING + 1]; /* 1250 06E7 */ char pad7[N_STRING + 1]; ima_position_t PatientPosition; /* 5100 0720 */ char ImagedNucleus[N_NUCLEUS + 1]; /* 0085 0724 */ char pad8[80]; /* Pad to 384 bytes */ }; /* * Relationship information group 0x0020 */ struct ima_acr_0020 { /* Item# FOffs */ int32_t StudyID; /* 0010 0C80 */ char pad1[4]; /* */ int32_t AcquisitionNumber; /* 0012 0C88 */ int32_t InstanceNumber; /* 0013 0C8C */ int32_t ImagePosition[3]; /* 0030 0C90 */ char pad2[4]; /* Dummy padding */ flt64_t ImageOrientation[6]; /* 0035 0C94 */ int32_t Location; /* 0050 0CD0 */ int32_t Laterality; /* 0060 0CD4 */ char pad3[4]; int32_t AcquisitionsInSeries; /* 1001 0CDC */ char pad4[416]; /* Pad to 512 bytes */ }; /* * Image presentation Information group 0x0028 */ struct ima_acr_0028 { /* Item# FOffs */ int16_t ImageDimension; /* 0005 1380 */ int16_t Rows; /* 0010 1382 */ int16_t Columns; /* 0011 1384 */ char pad1[2]; pixel_spacing_t PixelSpacing; /* 0030 1388 */ char pad2[8]; int16_t BitsAllocated; /* 0100 13A0 */ int16_t BitsStored; /* 0101 13A2 */ int16_t HighBit; /* 0102 13A4 */ int16_t PixelRepresentation; /* 0103 13A6 */ window_t WindowCenter; /* 1050 13A8 */ window_t WindowWidth; /* 1051 13B0 */ int32_t RescaleIntercept; /* 1052 13B8 */ int32_t RescaleSlope; /* 1053 13BC */ char pad3[192]; /* Pad to 256 bytes */ }; /***** Siemens private group structures *****/ /* * Siemens acquisition group 0x0019 */ struct ima_siemens_0019 { char pad1[20]; /* Padding */ int32_t NumberOfDataBytes; /* 1060 */ char pad2[140]; int32_t FourierLinesNominal; /* 1220 */ char pad3[4]; int32_t FourierLinesAfterZero; /* 1226 */ int32_t FirstMeasuredFourierLine; /* 1228 */ int32_t AcquisitionColumns; /* 1230 */ int32_t ReconstructionColumns; /* 1231 */ int32_t NumberOfAverages; /* 1250 */ flt64_t FlipAngle; /* 1260 */ int32_t NumberOfPrescans; /* 1270 */ char pad4[116]; /* Dummy padding */ int32_t SaturationRegions; /* 1290 */ char pad5[316]; /* Dummy padding */ flt64_t MagneticFieldStrength; /* 1412 */ char pad6[631]; /* Dummy padding */ }; /* * Siemens Relationship group 0x0021 */ struct ima_siemens_0021 { char pad1[32]; /* Dummy padding */ ima_field_of_view_t FieldOfView; /* 1120 0EA0 */ ima_view_direction_t ViewDirection; /* 1130 0EB0 */ ima_rest_direction_t RestDirection; /* 1132 0EB4 */ ima_vector_t ImagePosition; /* 1160 0EB8 */ ima_vector_t ImageNormal; /* 1161 0ED0 */ flt64_t ImageDistance; /* 1163 0EE8 */ char pad3[8]; /* Dummy padding */ ima_vector_t ImageRow; /* 116A 0EF8 */ ima_vector_t ImageColumn; /* 116B 0F10 */ ima_orientation_t OrientationSet1; /* 1170 0F28 */ ima_orientation_t OrientationSet2; /* 1171 0F34 */ char StudyName[N_STRING + 1]; /* 1180 0F40 */ int32_t StudyType; /* 1182 0F5C */ flt64_t ImageMagnificationFactor; /* 1122 0F60 */ char pad4[40]; /* XXXX 0F68 */ int32_t NumberOf3DRawPartNom; /* 1330 0F90 */ int32_t NumberOf3DRawPartCur; /* 1331 0F94 */ int32_t NumberOf3DImaPart; /* 1334 0F98 */ int32_t Actual3DImaPartNumber; /* 1336 0F9C */ char pad5[4]; int32_t NumberOfSlicesNom; /* 1340 0FA4 */ int32_t NumberOfSlicesCur; /* 1341 0FA8 */ int32_t CurrentSliceNumber; /* 1342 0FAC */ int32_t CurrentGroupNumber; /* 1343 0FB0 */ char pad7[88]; /* Dummy padding */ int32_t NumberOfEchoes; /* 1370 */ char pad9[32]; /* Dummy padding */ ima_slice_order_t SliceOrder; /* 134F */ char pad10[4]; /* Dummy padding */ flt64_t SlabThickness; /* 1339 */ char pad11[829]; /* Padding */ }; /* this is a work in progress, based on David Clunie's website */ #define PATIENT_NUMBER_SIZE 12 #define PATIENT_DATE_SIZE 11 #define PATIENT_POSITION_SIZE 11 #define IMAGE_NUMBER_SIZE 11 #define IMAGE_NUMBER_TEXT "IMAGE" #define LABEL_SIZE 5 #define DATE_OF_MEASUREMENT_SIZE 11 #define TIME_OF_MEASUREMENT_SIZE 5 #define TIME_OF_ACQUISITION_SIZE 11 #define TIME_OF_ACQUISITION_TEXT_CT "TI" #define TIME_OF_ACQUISITION_TEXT_MR "TA " #define NUMBER_OF_ACQUISITIONS_SIZE 11 #define NUMBER_OF_ACQUISITIONS_TEXT "AC" #define COMMENT_NO1_SIZE 26 #define COMMENT_NO2_SIZE 26 #define INSTALLATION_NAME_SIZE 26 #define SOFTWARE_VERSION_SIZE 11 #define MATRIX_SIZE 11 #define TYPE_OF_MEASUREMENT_SIZE 11 #define SCAN_NUMBER_SIZE 11 #define SCAN_NUMBER_TEXT "SCAN" #define REPETITION_TIME_SIZE 11 #define REPETITION_TIME_TEXT "TR" #define ECHO_TIME_SIZE 11 #define ECHO_TIME_TEXT "TE" #define GATING_AND_TRIGGER_SIZE 11 #define GATING_AND_TRIGGER_TEXT "TD" #define TUBE_CURRENT_SIZE 11 #define TUBE_CURRENT_TEXT "mA" #define TUBE_VOLTAGE_SIZE 11 #define TUBE_VOLTAGE_TEXT "kV" #define SLICE_THICKNESS_SIZE 11 #define SLICE_THICKNESS_TEXT "SL" #define SLICE_POSITION_SIZE 11 #define SLICE_POSITION_TEXT "SP" #define SLICE_ORIENTATION_NO1_SIZE 11 #define SLICE_ORIENTATION_NO2_SIZE SLICE_ORIENTATION_NO1_SIZE #define COR_TEXT "Cor" #define SAG_TEXT "Sag" #define TRA_TEXT "Tra" #define FIELD_OF_VIEW_SIZE 11 #define FIELD_OF_VIEW_TEXT "FoV" #define ZOOM_CENTER_SIZE 11 #define ZOOM_CENTER_TEXT "CE" #define ZOOM_CENTER_TEXT_MR "MF" #define GANTRY_TILT_SIZE 11 #define GANTRY_TILT_TEXT "GT" #define TABLE_POSITION_SIZE 11 #define TABLE_POSITION_TEXT "TP" #define MIP_HEADLINE_SIZE 3 #define MIP_HEADLINE_TEXT "VOI" #define MIP_LINE_SIZE 15 #define MIP_LINE_TEXT "Lin" #define MIP_COLUMN_SIZE 15 #define MIP_COLUMN_TEXT "Col" #define MIP_SLICE_SIZE 15 #define MIP_SLICE_TEXT "Sli" #define STUDY_NUMBER_SIZE 11 #define STUDY_NUMBER_TEXT "STUDY" #define CONTRAST_SIZE 5 #define CONTRAST_TEXT_CT "+C IV" #define CONTRAST_TEXT_MR "+C " #define CONTRAST_TEXT_NONE " " #define PATIENT_BIRTHDATE_SIZE 11 #define SEQUENCE_INFO_SIZE 11 #define SATURATION_REGIONS_SIZE 11 #define SATURATION_REGIONS_TEXT "SAT" #define DATA_SET_ID_SIZE 26 #define DATA_SET_ID_TEXT_STUDY "STU" #define DATA_SET_ID_TEXT_IMAGE "IMA" #define DATA_SET_ID_TEXT_DELIMITER "/" #define MAGNIFICATION_FACTOR_SIZE 11 #define MAGNIFICATION_FACTOR_TEXT "MF" #define MANUFACTURER_MODEL_SIZE 26 #define PATIENT_NAME_SIZE 26 #define TIME_OF_SCANNING_SIZE 8 typedef struct text_info { char PatientNumber[PATIENT_NUMBER_SIZE + 1]; /* Patient Id */ char PatientSexAndAge[PATIENT_DATE_SIZE + 1]; /* Patient Sex, Patient Age */ char PatientPosition[PATIENT_POSITION_SIZE + 1]; /* Patient Rest Direction, ... */ char ImageNumber[IMAGE_NUMBER_SIZE + 1]; /* Image */ char Label[LABEL_SIZE + 1]; /* Archiving Mark Mask, ... */ char DateOfMeasurement[DATE_OF_MEASUREMENT_SIZE + 1]; /* Acquisition Date */ char TimeOfMeasurement[TIME_OF_MEASUREMENT_SIZE + 1]; /* Acquisition Time */ char TimeOfAcquisition[TIME_OF_ACQUISITION_SIZE + 1]; /* CT: Exposure Time MR: Total Measurement Time */ char NumberOfAcquisitions[NUMBER_OF_ACQUISITIONS_SIZE + 1]; /* Number of Averages */ char CommentNo1[COMMENT_NO1_SIZE + 1]; /* Procedure Description */ char CommentNo2[COMMENT_NO2_SIZE + 1]; char InstallationName[INSTALLATION_NAME_SIZE + 1]; /* Institution ID */ char SoftwareVersion[SOFTWARE_VERSION_SIZE + 1]; /* Software Version */ char Matrix[MATRIX_SIZE + 1]; /* Rows, Columns */ char TypeOfMeasurement[TYPE_OF_MEASUREMENT_SIZE + 1]; /* Calculation Mode */ char ScanNumber[SCAN_NUMBER_SIZE + 1]; /* Acquisition */ char RepetitionTime[REPETITION_TIME_SIZE + 1]; /* Repetition Time */ char EchoTime[ECHO_TIME_SIZE + 1]; /* Echo Time */ char GatingAndTrigger[GATING_AND_TRIGGER_SIZE + 1]; /* Signal Mask */ char TubeCurrent[TUBE_CURRENT_SIZE + 1]; /* Exposure */ char TubeVoltage[TUBE_VOLTAGE_SIZE + 1]; /* Generator Power */ char SliceThickness[SLICE_THICKNESS_SIZE + 1]; /* Slice Thickness */ char SlicePosition[SLICE_POSITION_SIZE + 1]; /* Image Distance */ char SliceOrientationNo1[SLICE_ORIENTATION_NO1_SIZE + 1]; /* Image Position, ... */ char SliceOrientationNo2[SLICE_ORIENTATION_NO2_SIZE + 1]; char FieldOfView[FIELD_OF_VIEW_SIZE + 1]; /* Field of View */ char ZoomCenter[ZOOM_CENTER_SIZE + 1]; /* Target */ char GantryTilt[GANTRY_TILT_SIZE + 1]; /* Gantry Tilt */ char TablePosition[TABLE_POSITION_SIZE + 1]; /* Location */ char MipHeadLine[MIP_HEADLINE_SIZE + 1]; /* */ char MipLine[MIP_LINE_SIZE + 1]; /* MIP x Row */ char MipColumn[MIP_COLUMN_SIZE + 1]; /* MIP x Column */ char MipSlice[MIP_SLICE_SIZE + 1]; /* MIP x Slice */ char StudyNumber[STUDY_NUMBER_SIZE + 1]; /* Study */ char Contrast[CONTRAST_SIZE + 1]; /* Contrast Agent */ char PatientBirthdate[PATIENT_BIRTHDATE_SIZE + 1]; /* Patient Birthday */ char SequenceInformation[SEQUENCE_INFO_SIZE + 1]; /* Sequence File Owner, ... */ char SaturationRegions[SATURATION_REGIONS_SIZE + 1]; /* Saturation Regions, ... */ char DataSetId[DATA_SET_ID_SIZE + 1]; /* Image, Study */ char MagnificationFactor[MAGNIFICATION_FACTOR_SIZE + 1]; /* Image Maginification Factor */ char ManufacturerModel[MANUFACTURER_MODEL_SIZE + 1]; /* Manufacturer Model */ char PatientName[PATIENT_NAME_SIZE + 1]; /* Patient Name */ char TimeOfScanning[TIME_OF_SCANNING_SIZE + 1]; /* Acquisition Time */ } text_info_t; #if 0 struct text_info { char PatientID[12+1]; /* 5504 */ char PatientSex[1]; /* 5517 */ char PatientAge[3]; /* 5518 */ char PatientAgeUnits[1]; /* 5521 */ char pad1[7]; /* 5522 */ char PatientPosition[12]; /* 5529 */ char ImageNumberFlag[5]; /* 5541 */ char ImageNumber[3]; /* 5546 */ char pad2[10]; /* 5551 */ char Date[11+1]; /* 5559 */ char Time[5+1]; /* 5571 */ char AcquisitionTimeFlag[6]; /* 5577 */ char AcquisitionTime[5+1]; /* 5583 */ char AcquisitionCountFlag[6]; /* 5589 */ char AcquisitionCount[5+1]; /* 5595 */ char Annotation[27]; /* 5601 */ char AdmittingDiagnosis[27]; /* 5628 */ char Organization[27]; /* 5655 */ char Station[12]; /* 5682 */ char AcquisitionMatrixPhase[3]; /* 5695 */ char AcquisitionMatrixPhaseAxis[1]; char AcquisitionMatrixFreq[3]; char AcquisitionMatrixFreq0[1]; char AcquisitionMatrixFreqS[1]; char Sequence[8]; char FlipAngle[3]; char ScanNumberFlag[4]; char ScanNumberA[3]; char ScanNumberB[3]; char RepetitionTimeFlag[2]; char RepetitionTime[7]; char EchoTimeFlag[2]; char EchoTime[5]; char EchoNumber[1]; char SliceThicknessFlag[2]; char SliceThickness[7]; char SlicePositionFlag[2]; char SlicePosition[7]; char AngleFlag1[3]; char AngleFlag2[1]; char AngleFlag3[3]; char Angle[4]; char FOVFlag[3]; char FOVH[3]; char FOVV[3]; char TablePositionFlag[2]; char TablePosition[7]; char StudyNumberFlag[5]; char StudyNumber[2]; char DOBDD[2]; char DOBMM[3]; char DOBYYYY[4]; char StudyNumberFlag2[3]; char ImageNumberFlag2[3]; char StudyNumber2[2]; char ImageNumber2[2]; char StudyImageNumber3[5]; char ModelName[15]; char PatientName[27]; /* 6058 */ char ScanStartTimeHH[3]; /* 6085 */ char ScanStartTimeMM[3]; /* 6088 */ char ScanStartTimeSS[3]; /* 6091 */ }; #endif /* Siemens IMA header - total size is 0x1800 bytes */ typedef struct { /* Offset - Description */ struct ima_acr_0008 G08; /* 0x0000 - Identifying Information */ char G09[0x0180]; /* 0x0180 - Siemens specific */ struct ima_acr_0010 G10; /* 0x0300 - Patient Information */ char G11[0x0080]; /* 0x0400 - Siemens specific */ char G13[0x0180]; /* 0x0480 - Siemens specific */ struct ima_acr_0018 G18; /* 0x0600 - Acquisition Information */ struct ima_siemens_0019 G19; /* 0x0780 - Siemens specific */ struct ima_acr_0020 G20; /* 0x0C80 - Relationship Information */ struct ima_siemens_0021 G21; /* 0x0E80 - Siemens specific */ struct ima_acr_0028 G28; /* 0x1380 - Image Presentation Information */ char G29[0x0100]; /* 0x1480 - Siemens specific */ text_info_t ti; } ima_header_t; #endif /* _SIEMENS_HEADER_DEFS_H_ */ minc-tools-2.3.00+dfsg/conversion/dcm2mnc/progress.c0000644000175000000620000000213112574624760021337 0ustar stevestaff // This function prints a text progress bar within a term window // Input arguments assume a for loop starting at zero: // // for (index = 0; index < end; index++) { ... #include #include #include #include "progress.h" void progress(long index, int end, const char *message) { int ix; const int width = 50; int nchars; if (index == 0) { printf("%-20.20s |<--", message); for (ix = 0; ix < width; ix++) { printf(" "); } printf("|"); for (ix = 0; ix < width+1; ix++) { printf("\b"); } } else if ((index > 0) && (index < end)) { nchars = (((float)index/(float)(end-1)) * width) - floor(((float)(index-1)/(float)(end-1)) * width); for (ix = 0; ix < nchars; ix++) { printf("\b->"); fflush(stdout); } // print terminating newline at end if we're done if (index == end-1) { printf("\n"); } } else { fprintf(stderr,"PROGRESS: bad input indices!\n"); } } minc-tools-2.3.00+dfsg/conversion/Acr_nema/0002755000175000000620000000000012574624760017516 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/Acr_nema/group.c0000644000175000000620000014274512574624760021031 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : group.c @DESCRIPTION: Routines for doing acr_nema group operations. @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: group.c,v $ * Revision 6.12 2008-08-12 05:00:22 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.11 2006/04/09 15:29:43 bert * Add acr_insert_double() * * Revision 6.10 2005/05/09 15:34:46 bert * For acr_find_{short,int,long,double}, treat a zero-length element as if it were absent, and return the default value. * * Revision 6.9 2005/03/11 22:05:29 bert * Implement _acr_name_proc to allow printing of field names in dump_acr_nema * * Revision 6.8 2005/03/04 17:09:11 bert * Change several functions to return Acr_Status instead of void; lose and static; Make insert_element() check the return value of acr_get_element_total_length() * * Revision 6.7 2004/10/29 13:08:42 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.6 2002/12/08 22:31:34 neelin * When a last fragment is received, the dicom watchpoint is only updated when the next read happens, so the peek ahead will fail after the watchpoint test in acr_input_group_with_max. * * Revision 6.5 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.4 2000/05/01 17:18:07 neelin * Modifications tohandle end-of-input properly, both on first group * read, and when ignoring protocol errors. * * Revision 6.3 2000/04/28 15:03:11 neelin * Added support for ignoring non-fatal protocol errors (cases where redundant * information is inconsistent). In particular, it is possible to ignore * differences between the group length element and the true group length. * * Revision 6.2 1999/10/29 17:51:53 neelin * Fixed Log keyword * * Revision 6.1 1998/11/06 19:41:06 neelin * Added functions acr_group_steal_element and acr_find_group. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.2 1997/08/21 13:24:55 neelin * Pre-release * * Revision 4.1 1997/06/17 23:49:08 neelin * Added routines for inserting elements into a group list. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.2 1995/02/08 21:16:06 neelin * Changes to make irix 5 lint happy. * * Revision 2.1 1995/01/04 08:10:16 neelin * Improved string printing in dump function (longer strings and replace * carriage returns, linefeeds and formfeeds by spaces). * * Revision 2.0 94/09/28 10:36:16 neelin * Release of minc version 0.2 * * Revision 1.11 94/09/28 10:35:45 neelin * Pre-release * * Revision 1.10 94/04/07 10:05:04 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.9 93/12/10 09:20:32 neelin * Added acr_find_ routines. * * Revision 1.8 93/12/08 09:04:59 neelin * Fixed memory leak in acr_input_group_with_max. * Fixed acr_input_group_list (didn't stop reading when reached max group). * * Revision 1.7 93/11/30 08:57:42 neelin * Added group and group list copy routines. * * Revision 1.6 93/11/26 18:47:51 neelin * Added group and group list copy routines. * * Revision 1.5 93/11/25 10:36:57 neelin * Fixed input_group_list (wasn't checking max properly). * * Revision 1.4 93/11/24 12:05:12 neelin * Changed format of dump. * * Revision 1.3 93/11/24 11:25:38 neelin * Added some group list stuff (dump, input_group_list). * * Revision 1.2 93/11/22 13:11:58 neelin * Changed to use new Acr_Element_Id stuff * * Revision 1.1 93/11/19 12:48:52 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 #include #include #include #include /* Private functions */ static void steal_element(Acr_Group group, Acr_Element element, Acr_Element previous); static void remove_element(Acr_Group group, Acr_Element element, Acr_Element previous); static Acr_Status insert_element(Acr_Group group, Acr_Element element, Acr_Element previous); static void update_group_length_element(Acr_Group group, Acr_VR_encoding_type vr_encoding); static Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, int max_group_id); acr_name_proc_t _acr_name_proc = NULL; /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_group @INPUT : group_id @OUTPUT : (none) @RETURNS : Pointer to group structure @DESCRIPTION: Creates an acr-nema group structure @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Group acr_create_group(int group_id) { Acr_Group group; Acr_Element length_element; long group_length = 0; /* Allocate the group */ group = MALLOC(sizeof(*group)); /* Create a length element */ group_length = 0; length_element = acr_create_element(group_id, ACR_EID_GRPLEN, ACR_VR_UL, (long) ACR_SIZEOF_LONG, acr_memdup((size_t) ACR_SIZEOF_LONG, &group_length)); /* Assign fields */ group->group_id = group_id; group->nelements = 1; group->implicit_total_length = acr_get_element_total_length(length_element, ACR_IMPLICIT_VR); group->explicit_total_length = acr_get_element_total_length(length_element, ACR_EXPLICIT_VR); group->list_head = length_element; group->list_tail = length_element; group->next = NULL; return group; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_delete_group @INPUT : group @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Deletes an acr-nema group structure (freeing the element list) @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_delete_group(Acr_Group group) { acr_delete_element_list(group->list_head); FREE(group); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_delete_group_list @INPUT : group_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Delete a list of acr-nema group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_delete_group_list(Acr_Group group_list) { Acr_Group next, cur; /* Check for null group */ if (group_list == NULL) return; /* Loop through the list, deleting groups */ next = group_list; do { cur = next; next = cur->next; acr_delete_group(cur); } while (next != NULL); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_copy_group @INPUT : group @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Makes a copy of an acr-nema group structure @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group acr_copy_group(Acr_Group group) { Acr_Group copy; Acr_Element cur; /* Create the group */ copy = acr_create_group(acr_get_group_group(group)); /* Get the second element (first element is always there) */ cur = acr_get_element_next(group->list_head); while (cur != NULL) { acr_group_add_element(copy, acr_copy_element(cur)); cur = acr_get_element_next(cur); } return copy; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_copy_group_list @INPUT : group_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Make a copy of a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group acr_copy_group_list(Acr_Group group_list) { Acr_Group copy_list; Acr_Group copy_group; Acr_Group cur; /* Create first group */ copy_list = copy_group = acr_copy_group(group_list); /* Loop through groups */ cur = acr_get_group_next(group_list); while (cur != NULL) { acr_set_group_next(copy_group, acr_copy_group(cur)); copy_group = acr_get_group_next(copy_group); cur = acr_get_group_next(cur); } return copy_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : steal_element @INPUT : group element - element to remove previous - pointer to previous element or NULL if beginning of group element list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Steal an element from a group (remove it without deleting it). The caller must free the element themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void steal_element(Acr_Group group, Acr_Element element, Acr_Element previous) { Acr_Element next; /* Get pointer to next element */ next = acr_get_element_next(element); /* Update the previous element or list head */ if (previous != NULL) acr_set_element_next(previous, next); else group->list_head = next; /* Check for an element at the tail */ if (next == NULL) group->list_tail = previous; /* Update the group fields */ group->nelements--; group->implicit_total_length -= acr_get_element_total_length(element, ACR_IMPLICIT_VR); group->explicit_total_length -= acr_get_element_total_length(element, ACR_EXPLICIT_VR); /* Update the group length element */ update_group_length_element(group, acr_get_element_vr_encoding(group->list_head)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : remove_element @INPUT : group element - element to remove previous - pointer to previous element or NULL if beginning of group element list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Remove an element from a group. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void remove_element(Acr_Group group, Acr_Element element, Acr_Element previous) { /* Get rid of the old element from the group */ steal_element(group, element, previous); /* Delete the old element */ acr_delete_element(element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : insert_element @INPUT : group element - element to insert previous - pointer to previous element or NULL if beginning of group element list @OUTPUT : (none) @RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status insert_element(Acr_Group group, Acr_Element element, Acr_Element previous) { Acr_Element next; long length; /* Update the pointers */ if (previous != NULL) { /* Middle or tail of list */ next = acr_get_element_next(previous); acr_set_element_next(previous, element); } else { /* Head of list */ next = group->list_head; group->list_head = element; } acr_set_element_next(element, next); /* Check for the tail */ if (next == NULL) { group->list_tail = element; } /* Update the group fields */ group->nelements++; length = acr_get_element_total_length(element, ACR_IMPLICIT_VR); if (length <= 0) { return (ACR_OTHER_ERROR); } group->implicit_total_length += length; length = acr_get_element_total_length(element, ACR_EXPLICIT_VR); if (length <= 0) { return (ACR_OTHER_ERROR); } group->explicit_total_length += length; /* Update the length element */ update_group_length_element(group, acr_get_element_vr_encoding(group->list_head)); return (ACR_OK); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_group_insert_element @INPUT : group element @OUTPUT : (none) @RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group. If an element of the same id already exists in the list, it is removed and deleted. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_group_insert_element(Acr_Group group, Acr_Element element) { Acr_Element next_element, prev_element, cur_element; int element_id; /* Check that the element belongs in this group */ if (group->group_id != acr_get_element_group(element)) { (void) fprintf(stderr, "ACR error: Cannot add element %d (group %d) to group %d\n", acr_get_element_element(element), acr_get_element_group(element), group->group_id); exit(EXIT_FAILURE); } /* Get element id */ element_id = acr_get_element_element(element); /* Check that new element has id > group length element id */ if (element_id < ACR_EID_GRPLEN) { (void) fprintf(stderr, "ACR error: Cannot add element id %d <= length id (%d) to group %d\n", element_id, ACR_EID_GRPLEN, group->group_id); exit(EXIT_FAILURE); } /* Check whether the the element should be added after the last element */ if (acr_get_element_element(group->list_tail) < element_id) { prev_element = group->list_tail; next_element = NULL; } /* Otherwise, search for the appropriate location */ else { prev_element = NULL; next_element = group->list_head; while ((next_element != NULL) && (acr_get_element_element(next_element) < element_id)) { prev_element = next_element; next_element = acr_get_element_next(next_element); } } /* Check for an existing element and get rid of it */ if ((next_element != NULL) && (acr_get_element_element(next_element) == element_id)) { /* Set pointers and get rid of the old element */ cur_element = next_element; next_element = acr_get_element_next(cur_element); remove_element(group, cur_element, prev_element); } /* Insert the new element */ return insert_element(group, element, prev_element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_group_add_element @INPUT : group element @OUTPUT : (none) @RETURNS : Acr_Status @DESCRIPTION: Add an element to an acr-nema group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_group_add_element(Acr_Group group, Acr_Element element) { /* Check that the element belongs in this group */ if (group->group_id != acr_get_element_group(element)) { (void) fprintf(stderr, "ACR error: Cannot add element %d (group %d) to group %d\n", acr_get_element_element(element), acr_get_element_group(element), group->group_id); exit(EXIT_FAILURE); } /* Insert the element at the tail */ return insert_element(group, element, group->list_tail); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_group_remove_element @INPUT : group element_id @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Remove an element from a group. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_group_remove_element(Acr_Group group, int element_id) { Acr_Element next_element, prev_element; /* Search for the appropriate location */ prev_element = NULL; next_element = group->list_head; while ((next_element != NULL) && (acr_get_element_element(next_element) != element_id)) { prev_element = next_element; next_element = acr_get_element_next(next_element); } /* Check for an existing element and get rid of it */ if ((next_element != NULL) && (acr_get_element_element(next_element) == element_id)) { /* Set pointers and get rid of the old element */ remove_element(group, next_element, prev_element); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_group_steal_element @INPUT : group element - the caller must pass an element so that we know that they have a handle to it. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Remove an element from a group without deleting it. @METHOD : @GLOBALS : @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_group_steal_element(Acr_Group group, Acr_Element element) { int element_id; Acr_Element next_element, prev_element; /* Get element id from element */ element_id = acr_get_element_element(element); /* Search for the appropriate location */ prev_element = NULL; next_element = group->list_head; while ((next_element != NULL) && (acr_get_element_element(next_element) != element_id)) { prev_element = next_element; next_element = acr_get_element_next(next_element); } /* Check for an existing element and get rid of it */ if ((next_element != NULL) && (acr_get_element_element(next_element) == element_id)) { /* Set pointers and get rid of the old element */ steal_element(group, next_element, prev_element); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_group_length_element @INPUT : group vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Update the length element of the group according to the VR type @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void update_group_length_element(Acr_Group group, Acr_VR_encoding_type vr_encoding) { Acr_Long group_length; Acr_Element length_element; void *group_length_data; /* Get the element */ length_element = group->list_head; if (length_element == NULL) return; if (acr_get_element_element(length_element) != ACR_EID_GRPLEN) return; /* Calculate the appropriate length */ if (vr_encoding == ACR_IMPLICIT_VR) { group_length = group->implicit_total_length - acr_get_element_total_length(length_element, ACR_IMPLICIT_VR); } else { group_length = group->explicit_total_length - acr_get_element_total_length(length_element, ACR_EXPLICIT_VR); } /* Update the element */ group_length_data = acr_get_element_data(length_element); acr_put_long(acr_get_element_byte_order(length_element), 1, &group_length, group_length_data); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_group_next @INPUT : group next @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set pointer to next group for an acr-nema group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_group_next(Acr_Group group, Acr_Group next) { group->next = next; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_group_group @INPUT : group @OUTPUT : (none) @RETURNS : group id @DESCRIPTION: Get group id for group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_group_group(Acr_Group group) { return group->group_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_group_element_list @INPUT : group @OUTPUT : (none) @RETURNS : element list @DESCRIPTION: Get element list for group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_get_group_element_list(Acr_Group group) { return group->list_head; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_group_total_length @INPUT : group vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR @OUTPUT : (none) @RETURNS : total length of group @DESCRIPTION: Get total length of group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_group_total_length(Acr_Group group, Acr_VR_encoding_type vr_encoding) { if (vr_encoding == ACR_IMPLICIT_VR) return group->implicit_total_length; else return group->explicit_total_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_group_nelements @INPUT : group @OUTPUT : (none) @RETURNS : number of elements in group @DESCRIPTION: Get number of elements in group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_group_nelements(Acr_Group group) { return group->nelements; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_group_next @INPUT : group @OUTPUT : (none) @RETURNS : next group @DESCRIPTION: Get next group for group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group acr_get_group_next(Acr_Group group) { return group->next; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_group_with_max @INPUT : afp - acr file pointer max_group_id - maximum group id to read in. If <= 0 then any group is read in. @OUTPUT : group @RETURNS : status @DESCRIPTION: Read in an acr-nema group with an optional maximum group id. If group id exceeds max, then *group is set to NULL. If an error occurs, then a group may still be returned. This routine will stop reading when it reaches a watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status acr_input_group_with_max(Acr_File *afp, Acr_Group *group, int max_group_id) { int group_id, element_id, next_group_id; long group_length; Acr_Status status; Acr_Element element; int have_length_element; int get_more_elements; Acr_VR_encoding_type vr_encoding; /* Initialize the group pointer */ *group = NULL; /* Look ahead at the next element */ status = acr_peek_at_next_element_id(afp, &group_id, &element_id); if (status != ACR_OK) return status; /* Check for a group past the limit */ if ((max_group_id > 0) && (group_id > max_group_id)) { return status; } /* Check for a length element */ have_length_element = (element_id == ACR_EID_GRPLEN); /* Read the length element and check it */ if (have_length_element) { status = acr_input_element(afp, &element); if (status != ACR_OK) { acr_delete_element(element); return status; } if ((acr_get_element_element(element) != ACR_EID_GRPLEN) || (acr_get_element_length(element) != ACR_SIZEOF_LONG)) { if (acr_ignore_protocol_errors(afp)) { group_length = 0; } else { acr_delete_element(element); status = ACR_PROTOCOL_ERROR; return status; } } else { group_length = acr_get_element_long(element); } acr_delete_element(element); } /* Create the group */ *group = acr_create_group(group_id); /* Set the VR encoding and the byte ordering for the length element according to the input stream. If the vr_encoding is implicit, then make the VR unknown. Note that the group will always have a length element even if the input stream does not. */ element = acr_get_group_element_list(*group); vr_encoding = acr_get_vr_encoding(afp); acr_set_element_vr_encoding(element, vr_encoding); acr_set_element_byte_order(element, acr_get_byte_order(afp)); if (vr_encoding == ACR_IMPLICIT_VR) { acr_set_element_vr(element, ACR_VR_UNKNOWN); } /* Loop through elements, adding them to the list */ get_more_elements = (have_length_element ? (group_length > 0) : TRUE); while (get_more_elements) { /* Check for a watchpoint */ if (acr_get_io_watchpoint(afp) <= 0) { get_more_elements = FALSE; break; } /* Look ahead at next element */ status = acr_peek_at_next_element_id(afp, &next_group_id, &element_id); if ((status != ACR_OK) || (next_group_id != group_id)) { if ( status == ACR_REACHED_WATCHPOINT ) status = ACR_OK; get_more_elements = FALSE; break; } /* Read in the next element */ status = acr_input_element(afp, &element); if (status != ACR_OK) { get_more_elements = FALSE; } /* Add it to the group */ if (element != NULL) { acr_group_add_element(*group, element); if (have_length_element) { group_length -= acr_get_element_total_length(element, acr_get_vr_encoding(afp)); } } /* Check group length */ if (have_length_element && !acr_ignore_protocol_errors(afp)) { get_more_elements = (group_length > 0); } } /* Check that we got a full group */ if (have_length_element && (group_length != 0) && !acr_ignore_protocol_errors(afp)) { switch (status) { case ACR_OK: status = ACR_PROTOCOL_ERROR; break; case ACR_END_OF_INPUT: status = ACR_ABNORMAL_END_OF_INPUT; break; default: /* Other status */ status = ACR_OTHER_ERROR; break; } } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_group @INPUT : afp - acr file pointer @OUTPUT : group @RETURNS : status @DESCRIPTION: Read in an acr-nema group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_input_group(Acr_File *afp, Acr_Group *group) { return acr_input_group_with_max(afp, group, 0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_output_group @INPUT : afp - acr file pointer group @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Write out an acr-nema group @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_output_group(Acr_File *afp, Acr_Group group) { long ielement, nelements; Acr_Element cur, next; Acr_Status status; /* Update the length element */ update_group_length_element(group, acr_get_vr_encoding(afp)); /* Loop through the elements of the group, writing them out */ nelements = acr_get_group_nelements(group); next = acr_get_group_element_list(group); for (ielement=0; ielement < nelements && next != NULL; ielement++) { cur = next; next = cur->next; status = acr_output_element(afp, cur); if (status != ACR_OK) { return status; } } /* Check for a bogus group (the true number of elements is different from nelements) */ if ((ielement < nelements) || (next != NULL)) { status = ACR_OTHER_ERROR; return status; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_group_list @INPUT : afp - acr file pointer max_group_id - maximum group id to read in. If <= 0 then all groups are read in. If max_group_id is not a group id in the input stream, then the input stream is left with the first element of the next group missing (it gets read here and is not put back), so no more groups can be read in. @OUTPUT : group_list @RETURNS : status @DESCRIPTION: Read in a list of acr-nema groups. If a watchpoint is set on the input handle, then group reading will stop when it is reached, although at least one group will be read in. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_input_group_list(Acr_File *afp, Acr_Group *group_list, int max_group_id) { Acr_Group cur_group, next_group; Acr_Status status; /* Initialize the group list */ *group_list = NULL; /* Read in the first group */ status = acr_input_group_with_max(afp, &next_group, max_group_id); /* Set up pointers */ *group_list = cur_group = next_group; /* Loop, reading groups */ while ((status == ACR_OK) && (cur_group != NULL) && ((max_group_id <= 0) || (acr_get_group_group(cur_group) < max_group_id))) { /* Check for a watchpoint */ if (acr_get_io_watchpoint(afp) <= 0) { break; } /* Read in the next group */ status = acr_input_group_with_max(afp, &next_group, max_group_id); /* Add it to the list */ acr_set_group_next(cur_group, next_group); cur_group = next_group; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_group @INPUT : group_list group_id @OUTPUT : (none) @RETURNS : gropu pointer @DESCRIPTION: Find a group in a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group acr_find_group(Acr_Group group_list, int group_id) { Acr_Group group; int next_id; /* Search through groups for group id */ group = group_list; if (group != NULL) next_id = acr_get_group_group(group); else next_id = 0; while ((next_id != group_id) && (group != NULL)) { group = acr_get_group_next(group); if (group != NULL) next_id = acr_get_group_group(group); } return group; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_group_element @INPUT : group_list elid @OUTPUT : (none) @RETURNS : element pointer @DESCRIPTION: Find an element in a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_find_group_element(Acr_Group group_list, Acr_Element_Id elid) { Acr_Group group; /* Find the group */ group = acr_find_group(group_list, elid->group_id); /* If not found return NULL */ if (group == NULL) return NULL; /* Search through element list for element */ return acr_find_element_id(acr_get_group_element_list(group), elid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dump_group_list @INPUT : file_pointer - where output should go group_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Dump information from an acr-nema group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dump_group_list(FILE *file_pointer, Acr_Group group_list) { Acr_Group cur_group; /* Check for empty list */ cur_group = group_list; if (cur_group == NULL) { (void) fprintf(file_pointer,"\nEmpty group list\n\n"); return; } /* Loop over groups */ while (cur_group != NULL) { /* Print the group id */ (void) fprintf(file_pointer, "\nGroup 0x%04x :\n\n", acr_get_group_group(cur_group)); /* Print the elements */ acr_dump_element_list(file_pointer, acr_get_group_element_list(cur_group)); /* Go to the next group */ cur_group = acr_get_group_next(cur_group); } /* Print a blank line after dump */ (void) fprintf(file_pointer, "\n"); /* Flush the buffer */ (void) fflush(file_pointer); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_short @INPUT : group_list elid default_value @OUTPUT : (none) @RETURNS : Element value or default_value if element not found @DESCRIPTION: Find an element in a group list and return its value (assuming that it is stored as a binary short). @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Short acr_find_short(Acr_Group group_list, Acr_Element_Id elid, Acr_Short default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); if (element != NULL && acr_get_element_length(element) > 0) return acr_get_element_short(element); else return default_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_long @INPUT : group_list elid default_value @OUTPUT : (none) @RETURNS : Element value or default_value if element not found @DESCRIPTION: Find an element in a group list and return its value (assuming that it is stored as a binary long). @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Long acr_find_long(Acr_Group group_list, Acr_Element_Id elid, Acr_Long default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); if (element != NULL && acr_get_element_length(element) > 0) return acr_get_element_long(element); else return default_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_int @INPUT : group_list elid default_value @OUTPUT : (none) @RETURNS : Element value or default_value if element not found @DESCRIPTION: Find an element in a group list and return its value (assuming that it is stored as an ascii integer). @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_find_int(Acr_Group group_list, Acr_Element_Id elid, int default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); if (element != NULL && acr_get_element_length(element) > 0) return (int) acr_get_element_numeric(element); else return default_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_double @INPUT : group_list elid default_value @OUTPUT : (none) @RETURNS : Element value or default_value if element not found @DESCRIPTION: Find an element in a group list and return its value (assuming that it is stored as an ascii double). @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Double acr_find_double(Acr_Group group_list, Acr_Element_Id elid, Acr_Double default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); if (element != NULL && acr_get_element_length(element) > 0) return (Acr_Double)acr_get_element_numeric(element); else return default_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_string @INPUT : group_list elid default_value @OUTPUT : (none) @RETURNS : Element value or default_value if element not found @DESCRIPTION: Find an element in a group list and return its value (assuming that it is stored as an ascii string). @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_String acr_find_string(Acr_Group group_list, Acr_Element_Id elid, Acr_String default_value) { Acr_Element element; element = acr_find_group_element(group_list, elid); if (element != NULL) /* Allow zero-length strings */ return acr_get_element_string(element); else return default_value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_element_into_group_list @INPUT : group_list - list in which element should be inserted (can be NULL) element - element to insert @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Insert an element into a group list. If the group_list is NULL, then it is created. Note that the element is not copied, it is just inserted into the list, so it should not be modified after insertion into the list. If an element of the same id already exists in the list, it is removed and deleted. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_element_into_group_list(Acr_Group *group_list, Acr_Element element) { Acr_Group group, next_group, prev_group; int group_id; /* Get group and element id */ group_id = acr_get_element_group(element); /* Search for the appropriate group */ prev_group = NULL; next_group = *group_list; while ((next_group != NULL) && (acr_get_group_group(next_group) < group_id)) { prev_group = next_group; next_group = acr_get_group_next(next_group); } /* Check if we have the right group */ if ((next_group != NULL) && (acr_get_group_group(next_group) == group_id)) { group = next_group; } /* If not, create a new group and insert it in the list */ else { /* Create a group */ group = acr_create_group(group_id); /* Insert it in the list */ acr_set_group_next(group, next_group); if (prev_group != NULL) { acr_set_group_next(prev_group, group); } else { *group_list = group; } } /* Insert the element into the appropriate group */ return acr_group_insert_element(group, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_short @INPUT : group_list - may be NULL if list empty elid value @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_short(Acr_Group *group_list, Acr_Element_Id elid, Acr_Short value) { Acr_Element element; element = acr_create_element_short(elid, value); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_long @INPUT : group_list - may be NULL if list empty elid value @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_long(Acr_Group *group_list, Acr_Element_Id elid, Acr_Long value) { Acr_Element element; element = acr_create_element_long(elid, value); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_double @INPUT : group_list - may be NULL if list empty elid nvalues values @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : April 8, 2006 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_double(Acr_Group *group_list, Acr_Element_Id elid, int nvalues, Acr_Double *values) { Acr_Element element; element = acr_create_element_double(elid, nvalues, values); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_numeric @INPUT : group_list - may be NULL if list empty elid value @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_numeric(Acr_Group *group_list, Acr_Element_Id elid, double value) { Acr_Element element; element = acr_create_element_numeric(elid, value); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_string @INPUT : group_list - may be NULL if list empty elid value @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_string(Acr_Group *group_list, Acr_Element_Id elid, Acr_String value) { Acr_Element element; element = acr_create_element_string(elid, value); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_insert_sequence @INPUT : group_list - may be NULL if list empty elid itemlist @OUTPUT : group_list - modified group list @RETURNS : Acr_Status @DESCRIPTION: Creates and inserts an element into a group list. @METHOD : @GLOBALS : @CALLS : @CREATED : June 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_insert_sequence(Acr_Group *group_list, Acr_Element_Id elid, Acr_Element itemlist) { Acr_Element element; element = acr_create_element_sequence(elid, itemlist); return acr_insert_element_into_group_list(group_list, element); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_test_dicom_file @INPUT : afp @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Tests for a dicom file input, as well as setting the byte order and VR encoding. @METHOD : Check the byte order and t This function is in this file because it does not really have a better place to live: It depends on the group reading code (and so should not live in acr_io.c) and is independent of the other dicom code (and so should not live there). @GLOBALS : @CALLS : acr_test_byte_order, acr_input_group_list @CREATED : November 8, 2001 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_test_dicom_file(Acr_File *afp) { #define DICOM_FILE_MAGIC_OFFSET 128 #define DICOM_MAGIC_STRING "DICM" #define DICOM_MAGIC_LEN 4 #define DICOM_FILE_GROUP 0x2 unsigned char buffer[DICOM_FILE_MAGIC_OFFSET+DICOM_MAGIC_LEN]; Acr_Status status; long buflen; Acr_Group group_list; /* Read in up to magic */ status = acr_read_buffer(afp, buffer, sizeof(buffer), &buflen); if (status != ACR_OK) return status; /* Check for the magic. If it is not there, put the data back and then test the byte order. */ if (strncmp((char *) &buffer[DICOM_FILE_MAGIC_OFFSET], DICOM_MAGIC_STRING, DICOM_MAGIC_LEN) != 0) { status = acr_unget_buffer(afp, buffer, buflen); if (status != ACR_OK) return status; return acr_test_byte_order(afp); } /* Read in group 2. We could get the transfer syntax in the group at this point, or just use the test heuristic from acr_test_byte_order. It seems safer to trust the heuristic than the file writer, so we will ignore the contents of the group. */ status = acr_test_byte_order(afp); if (status != ACR_OK) return status; status = acr_input_group_list(afp, &group_list, DICOM_FILE_GROUP); acr_delete_group_list(group_list); if (status != ACR_OK) return status; /* Test the byte order for the remainder of the file */ status = acr_test_byte_order(afp); if (status != ACR_OK) return status; return status; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/message.c0000644000175000000620000004737412574624760021323 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : message.c @DESCRIPTION: Routines for doing acr_nema message operations. @METHOD : @GLOBALS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : * $Log: message.c,v $ * Revision 6.8 2008-08-12 05:00:22 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.7 2004/10/29 13:08:42 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.6 2002/12/08 21:43:08 neelin * Fixed excessive memory freeing on error when reading message (seen in linux) * * Revision 6.5 2002/11/13 03:00:27 neelin * Fixed an unterminated comment * * Revision 6.4 1999/10/29 17:51:53 neelin * Fixed Log keyword * * Revision 6.3 1998/03/10 17:06:29 neelin * Added code to acr_input_message so that if we reach the watchpoint and * more message is expected, we keep on reading. * * Revision 6.2 1998/03/09 19:30:23 neelin * Fixed bug in acr_input_message where the last group added to the input * message was being deleted followed by the message itself when a * message length error occurred. When an input error occurs the message * should not be deleted. * * Revision 6.1 1998/02/18 20:27:13 neelin * Minor bug fix. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/06/13 21:27:16 neelin * Made use of message length to figure out how much to read in - * previously was not checking it, so if watchpoint was not set, we would * read indefinitely. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:17 neelin * Release of minc version 0.2 * * Revision 1.6 94/09/28 10:35:49 neelin * Pre-release * * Revision 1.5 94/05/18 08:48:12 neelin * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT. * * Revision 1.4 94/04/07 10:05:06 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.3 93/11/24 11:25:59 neelin * Added dump_message. * * Revision 1.2 93/11/22 13:12:09 neelin * Changed to use new Acr_Element_Id stuff * * Revision 1.1 93/11/19 12:49: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. ---------------------------------------------------------------------------- */ #include #include #include #include /* Message length group and element id */ #define ACR_GID_MESSLEN 0 #define ACR_EID_MESSLEN 1 DEFINE_ELEMENT(static, ACR_Message_length, ACR_GID_MESSLEN, ACR_EID_MESSLEN, UL); /* Private functions */ static void update_message_length_element(Acr_Message message, Acr_VR_encoding_type vr_encoding); /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_message @INPUT : (none) @OUTPUT : (none) @RETURNS : Pointer to message structure @DESCRIPTION: Creates an acr-nema message structure. @METHOD : No groups are created here. Message length is checked when groups are added. @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Message acr_create_message(void) { Acr_Message message; /* Allocate the message */ message = MALLOC(sizeof(*message)); /* Assign fields */ acr_message_reset(message); return message; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_delete_message @INPUT : message @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Deletes an acr-nema message structure (freeing the group list) @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_delete_message(Acr_Message message) { if (message->list_head != NULL) { acr_delete_group_list(message->list_head); } FREE(message); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_message_reset @INPUT : message @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Resets the message to empty without freeing any existing group list. Thus the group list can be used elsewhere without being deleted when the message is deleted. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_message_reset(Acr_Message message) { message->ngroups = 0; message->implicit_total_length = 0; message->explicit_total_length = 0; message->message_implicit_offset = 0; message->message_explicit_offset = 0; message->message_length_element = NULL; message->list_head = NULL; message->list_tail = NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_message_add_group @INPUT : message group @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Add a group to an acr-nema message @METHOD : If this is the first group, then look for a message length element. If none is found, then report an error. @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : February 7, 1997 (P.N.) ---------------------------------------------------------------------------- */ void acr_message_add_group(Acr_Message message, Acr_Group group) { Acr_Element length_element; int element_id; long message_implicit_offset, message_explicit_offset; /* If first message, then check for message length element */ if (message->ngroups == 0) { length_element = NULL; message_implicit_offset = 0; message_explicit_offset = 0; if (acr_get_group_group(group) == ACR_GID_MESSLEN) { length_element = acr_get_group_element_list(group); if (length_element != NULL) { element_id = acr_get_element_element(length_element); message_implicit_offset = acr_get_element_total_length(length_element, ACR_IMPLICIT_VR); message_explicit_offset = acr_get_element_total_length(length_element, ACR_EXPLICIT_VR); } else { element_id = 0; message_implicit_offset = 0; message_explicit_offset = 0; } while ((element_id != ACR_EID_MESSLEN) && (length_element != NULL)) { length_element = acr_get_element_next(length_element); if (length_element != NULL) { element_id = acr_get_element_element(length_element); message_implicit_offset += acr_get_element_total_length(length_element, ACR_IMPLICIT_VR); message_explicit_offset += acr_get_element_total_length(length_element, ACR_EXPLICIT_VR); } } } /* Check for length element found but not of correct length */ if ((length_element != NULL) && (acr_get_element_length(length_element) != ACR_SIZEOF_LONG)) { (void) fprintf(stderr, "ACR error: First message group contains length element of wrong size\n"); exit(EXIT_FAILURE); } /* Set up the message length info */ message->message_implicit_offset = message_implicit_offset; message->message_explicit_offset = message_explicit_offset; message->message_length_element = length_element; } /* Add group (check for empty list) */ if (message->ngroups == 0) { message->list_head = group; message->list_tail = group; } else { acr_set_group_next(message->list_tail, group); message->list_tail = group; acr_set_group_next(group, NULL); } message->ngroups++; message->implicit_total_length += acr_get_group_total_length(group, ACR_IMPLICIT_VR); message->explicit_total_length += acr_get_group_total_length(group, ACR_EXPLICIT_VR); /* Update the length element */ if (message->message_length_element != NULL) { update_message_length_element(message, acr_get_element_vr_encoding(message->message_length_element)); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_message_length_element @INPUT : message vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Update the length element of the message according to the VR type @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void update_message_length_element(Acr_Message message, Acr_VR_encoding_type vr_encoding) { Acr_Long message_length; Acr_Element length_element; void *message_length_data; /* Get the element */ length_element = message->message_length_element; if (length_element == NULL) return; /* Calculate the appropriate length */ if (vr_encoding == ACR_IMPLICIT_VR) { message_length = message->implicit_total_length - message->message_implicit_offset; } else { message_length = message->explicit_total_length - message->message_explicit_offset; } /* Update the element */ message_length_data = acr_get_element_data(length_element); acr_put_long(acr_get_element_byte_order(length_element), 1, &message_length, message_length_data); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_message_add_group_list @INPUT : message group_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Add a group list to an acr-nema message @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_message_add_group_list(Acr_Message message, Acr_Group group_list) { Acr_Group next_group, group; /* Loop through groups, adding them to the message */ group = group_list; while (group != NULL) { next_group = acr_get_group_next(group); acr_set_group_next(group, NULL); acr_message_add_group(message, group); group = next_group; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_message_group_list @INPUT : message @OUTPUT : (none) @RETURNS : group list @DESCRIPTION: Get group list for message @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Group acr_get_message_group_list(Acr_Message message) { return message->list_head; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_message_total_length @INPUT : message vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR @OUTPUT : (none) @RETURNS : total length of message @DESCRIPTION: Get total length of message depending on type of VR encoding @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_message_total_length(Acr_Message message, Acr_VR_encoding_type vr_encoding) { if (vr_encoding == ACR_IMPLICIT_VR) return message->implicit_total_length; else return message->explicit_total_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_message_ngroups @INPUT : message @OUTPUT : (none) @RETURNS : number of groups in message @DESCRIPTION: Get number of groups in message @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_message_ngroups(Acr_Message message) { return message->ngroups; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_message @INPUT : afp - acr file pointer @OUTPUT : message @RETURNS : status @DESCRIPTION: Read in an acr-nema message. The amount of input to read is determined either by setting a watchpoint or by having messages that contain their own length. One cannot rely on finding an EOF (as for a file) since messages usually come over a connection. @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : February 7, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Status acr_input_message(Acr_File *afp, Acr_Message *message) { Acr_Status status; Acr_Group group; Acr_Element length_element; Acr_Long lvalue; long message_length; long group_length; long watchpoint; int get_more_groups; /* Initialize the message pointer */ *message = NULL; /* Read in the first group */ status = acr_input_group(afp, &group); /* Check status */ if (status != ACR_OK) { if (group != NULL) acr_delete_group(group); return status; } /* Check whether it contains the message length. If there is no watchpoint, then it must contain the length, otherwise we don't know when to stop reading (we would if the input was from a file, but messages don't usually come from a file) */ length_element = acr_find_group_element(group, ACR_Message_length); if ((acr_get_io_watchpoint(afp) == ACR_NO_WATCHPOINT) && ((length_element == NULL) || (acr_get_element_length(length_element) != ACR_SIZEOF_LONG))) { acr_delete_group(group); status = ACR_PROTOCOL_ERROR; return status; } /* Get the message length from the element */ if (length_element != NULL) { acr_get_long(acr_get_element_byte_order(length_element), (long) 1, acr_get_element_data(length_element), &lvalue); message_length = (long)lvalue; } /* Create the message and add the group (this will modify the message length element value to include only the first group) */ *message = acr_create_message(); acr_message_add_group(*message, group); /* Correct message_length for the length of the first group. Note that adding the group had the side effect of changing the message length to include only the first group */ if (length_element != NULL) { acr_get_long(acr_get_element_byte_order(length_element), (long) 1, acr_get_element_data(length_element), &lvalue); group_length = (long)lvalue; message_length -= group_length; } /* Loop through elements, adding them to the list */ get_more_groups = ((length_element != NULL) ? (message_length > 0) : TRUE); while (get_more_groups) { /* Check for a watchpoint. If we have reached it, but the message length indicates that there is more to come, then move it along so that we can keep reading. */ watchpoint = acr_get_io_watchpoint(afp); if ((watchpoint == 0) && (length_element != NULL) && (message_length > 0)) { acr_set_io_watchpoint(afp, LONG_MAX-1); } else if (watchpoint <= 0) { get_more_groups = FALSE; break; } /* If we reach the end of a fragment and PDU, but we need more data */ /* Read in the next group and check for an error */ status = acr_input_group(afp, &group); /* Add the group to the message */ if (group != NULL) { acr_message_add_group(*message, group); } /* Check the status */ if (status != ACR_OK) { if (status == ACR_END_OF_INPUT) status = ACR_ABNORMAL_END_OF_INPUT; return status; } /* Keep track of remaining bytes to read, if necessary */ if (length_element != NULL) { message_length -= acr_get_group_total_length(group, acr_get_vr_encoding(afp)); get_more_groups = (message_length > 0); } } /* Check that we got a full message */ if ((length_element != NULL) && (message_length != 0)) { status = ACR_PROTOCOL_ERROR; return status; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_output_message @INPUT : afp - acr file pointer message @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Write out an acr-nema message @METHOD : @GLOBALS : @CALLS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_output_message(Acr_File *afp, Acr_Message message) { long igroup, ngroups; Acr_Group cur, next; Acr_Status status; /* Update the length element */ update_message_length_element(message, acr_get_vr_encoding(afp)); /* Loop through the groups of the message, writing them out */ ngroups = acr_get_message_ngroups(message); next = acr_get_message_group_list(message); for (igroup=0; igroup < ngroups && next != NULL; igroup++) { cur = next; next = cur->next; status = acr_output_group(afp, cur); if (status != ACR_OK) { return status; } } /* Flush the buffer */ if (acr_file_flush(afp) == EOF) { status = ACR_ABNORMAL_END_OF_OUTPUT; return status; } /* Check for a bogus message (the true number of groups is different from ngroups) */ if ((igroup < ngroups) || (next != NULL)) { status = ACR_OTHER_ERROR; return status; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dump_message @INPUT : file_pointer - where output should go group_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Dump information from an acr-nema message @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dump_message(FILE *file_pointer, Acr_Message message) { acr_dump_group_list(file_pointer, acr_get_message_group_list(message)); return; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/sample_dicom_client.c0000644000175000000620000001345012574624760023655 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : sample_dicom_client.c @DESCRIPTION: Sample program to send dicom images to a remote server @GLOBALS : @CREATED : May 6, 1997 (Peter Neelin) @MODIFIED : * $Log: sample_dicom_client.c,v $ * Revision 6.5 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.4 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.3 2004/10/29 13:08:42 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.2 2001/11/08 14:17:06 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.1 1999/10/29 17:51:54 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 1.2 1997/09/11 16:29:34 neelin * Modified list of files included. * * Revision 1.1 1997/09/08 21:52:55 neelin * Initial revision * * Revision 5.0 1997/08/21 13:25:30 neelin * Release of minc version 0.5 * * Revision 1.5 1997/07/11 17:35:58 neelin * Changed around send and receive routines for data once again. * * Revision 1.4 1997/07/11 13:55:41 neelin * Fixed handling of message ids. * * Revision 1.3 1997/07/11 13:22:16 neelin * Separated receive_reply from send_group_list. * * Revision 1.2 1997/07/10 17:39:51 neelin * Modified to use new dicom_client_routines. * * Revision 1.1 1997/05/13 23:53:40 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #ifndef public # define public #endif #ifndef private # define private static #endif /* Dicom definitions */ #define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" /* Function prototypes */ public int send_file(Acr_File *afpin, Acr_File *afpout, char *filename); /* Main program */ int main(int argc, char *argv[]) { char *pname; char *host; char *port; char *AEtitle; char **file_list; int ifile, num_files; Acr_File *afpin, *afpout; /* Check arguments */ pname = argv[0]; if (argc < 5) { (void) fprintf(stderr, "Usage: %s host port AEtitle files ...\n", pname); return EXIT_FAILURE; } host = argv[1]; port = argv[2]; AEtitle = argv[3]; file_list = &argv[4]; num_files = argc - 4; /* Make dicom connection */ if (!acr_open_dicom_connection(host, port, AEtitle, "test", ACR_MR_IMAGE_STORAGE_UID, ACR_IMPLICIT_VR_LITTLE_END_UID, &afpin, &afpout)) { return EXIT_FAILURE; } /* Loop over the input files, sending them one at a time */ for (ifile = 0; ifile < num_files; ifile++) { (void) printf("Sending file %s\n", file_list[ifile]); if (!send_file(afpin, afpout, file_list[ifile])) { break; } } /* Release the association */ acr_close_dicom_connection(afpin, afpout); return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : send_file @INPUT : afpin - input stream afpout - output stream filename - name of file to send @OUTPUT : (none) @RETURNS : TRUE if send went smoothly. @DESCRIPTION: Routine to read in an ACR-NEMA format file and send it over a dicom connection. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int send_file(Acr_File *afpin, Acr_File *afpout, char *filename) { FILE *fp; Acr_File *file_afp; Acr_Group group; Acr_Status status; int result; /* Open input file */ fp = fopen(filename, "r"); if (fp == NULL) { (void) fprintf(stderr, "Error opening file %s\n", filename); return FALSE; } /* Connect to input stream */ file_afp = acr_file_initialize(fp, 0, acr_stdio_read); (void) acr_test_dicom_file(file_afp); /* Read in group list up to group */ status = acr_input_group_list(file_afp, &group, 0); if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { (void) fprintf(stderr, "Error reading file \"%s\"", filename); acr_dicom_error(status, ""); return FALSE; } /* Free the afp */ acr_file_free(file_afp); /* Send the group list */ result = acr_send_group_list(afpin, afpout, group, ACR_MR_IMAGE_STORAGE_UID); /* Delete the group list */ acr_delete_group_list(group); return result; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/INSTALL0000644000175000000620000002203012574624760020542 0ustar stevestaffCopyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. minc-tools-2.3.00+dfsg/conversion/Acr_nema/dicom.txt0000644000175000000620000011601512574624760021354 0ustar stevestaff// Group 8 Specific_Character_Set 0x0008 0x0005 CS Image_Type 0x0008 0x0008 CS Instance_Creation_Date 0x0008 0x0012 DA Instance_Creation_Time 0x0008 0x0013 TM Instance_Creator_UID 0x0008 0x0014 UI SOP_Class_UID 0x0008 0x0016 UI SOP_Instance_UID 0x0008 0x0018 UI Related_General_SOP_Class_UID 0x0008 0x001A UI Original_Specialized_SOP_Class_UID 0x0008 0x001B UI Study_Date 0x0008 0x0020 DA Series_Date 0x0008 0x0021 DA Acquisition_Date 0x0008 0x0022 DA Content_Date 0x0008 0x0023 DA Overlay_Date 0x0008 0x0024 DA Curve_Date 0x0008 0x0025 DA Acquisition_Datetime 0x0008 0x002A DT Study_Time 0x0008 0x0030 TM Series_Time 0x0008 0x0031 TM Acquisition_Time 0x0008 0x0032 TM Content_Time 0x0008 0x0033 TM Overlay_Time 0x0008 0x0034 TM Curve_Time 0x0008 0x0035 TM Data_Set_Type 0x0008 0x0040 RET Data_Set_Subtype 0x0008 0x0041 RET Nuclear_Medicine_Series_Type 0x0008 0x0042 CS Accession_Number 0x0008 0x0050 SH Query/Retrieve_Level 0x0008 0x0052 CS Retrieve_AE_Title 0x0008 0x0054 AE Instance_Availability 0x0008 0x0056 CS Failed_SOP_Instance_UID_List 0x0008 0x0058 UI Modality 0x0008 0x0060 CS Modalities_in_Study 0x0008 0x0061 CS SOP_Classes_in_Study 0x0008 0x0062 UI Conversion_Type 0x0008 0x0064 CS Presentation_Intent_Type 0x0008 0x0068 CS Manufacturer 0x0008 0x0070 LO Institution_Name 0x0008 0x0080 LO Institution_Address 0x0008 0x0081 ST Institution_Code_Sequence 0x0008 0x0082 SQ Referring_Physician's_Name 0x0008 0x0090 PN Referring_Physician's_Address 0x0008 0x0092 ST Referring_Physician's_Telephone_Numbers 0x0008 0x0094 SH Referring_Physician_Identificiation_Sequence 0x0008 0x0096 SQ Code_Value 0x0008 0x0100 SH Coding_Scheme_Designator 0x0008 0x0102 SH Coding_Scheme_Version 0x0008 0x0103 SH Code_Meaning 0x0008 0x0104 LO Mapping_Resource 0x0008 0x0105 CS Context_Group_Version 0x0008 0x0106 DT Context_Group_Local_Version 0x0008 0x0107 DT // Some omitted fields Timezone_Offset_From_UTC 0x0008 0x0201 SH // Some fields omitted here... Network_ID 0x0008 0x1000 RET Station_Name 0x0008 0x1010 SH Study_Description 0x0008 0x1030 LO Procedure_Code_Sequence 0x0008 0x1032 SQ Series_Description 0x0008 0x103E LO Institutional_Department_Name 0x0008 0x1040 LO Physician(s)_of_Record 0x0008 0x1048 PN Physician(s)_of_Record_Identification_Sequence 0x0008 0x1049 SQ Performing_Physician's_Name 0x0008 0x1050 PN Performing_Physician_Idntification_Sequence 0x0008 0x1052 SQ Name_of_Physician(s)_Reading_Study 0x0008 0x1060 PN Physician(s)_Reading_Study_Identification_Sequence 0x0008 0x1062 SQ Operator's_Name 0x0008 0x1070 PN Operator_Identification_Sequence 0x0008 0x1072 SQ Admitting_Diagnoses_Description 0x0008 0x1080 LO Admitting_Diagnoses_Code_Sequence 0x0008 0x1084 SQ Manufacturer's_Model_Name 0x0008 0x1090 LO Referenced_Results_Sequence 0x0008 0x1100 SQ Referenced_Study_Sequence 0x0008 0x1110 SQ Referenced_Performed_Procedure_Step_Sequence 0x0008 0x1111 SQ Referenced_Series_Sequence 0x0008 0x1115 SQ Referenced_Patient_Sequence 0x0008 0x1120 SQ Referenced_Visit_Sequence 0x0008 0x1125 SQ Referenced_Overlay_Sequence 0x0008 0x1130 SQ Referenced_Waveform_Sequence 0x0008 0x113A SQ Referenced_Image_Sequence 0x0008 0x1140 SQ Referenced_Curve_Sequence 0x0008 0x1145 SQ Referenced_Instance_Sequence 0x0008 0x114A SQ Referenced_SOP_Class_UID 0x0008 0x1150 UI Referenced_SOP_Instance_UID 0x0008 0x1155 UI SOP_Classes_Supported 0x0008 0x115A UI Referenced_Frame_Number 0x0008 0x1160 IS Transaction_UID 0x0008 0x1195 UI Failure_Reason 0x0008 0x1197 US Failed_SOP_Sequence 0x0008 0x1198 SQ Referenced_SOP_Sequence 0x0008 0x1199 SQ Source_Image_Sequence 0x0008 0x2112 SQ Anatomic_Region_Sequence 0x0008 0x2218 SQ Frame_Type 0x0008 0x9007 CS Referenced_Image_Evidence_Sequence 0x0008 0x9092 SQ Referenced_Raw_Data_Sequence 0x0008 0x9121 SQ Creator-Version_UID 0x0008 0x9123 UI Derivation_Image_Sequence 0x0008 0x9124 SQ Source_Image_Evidence_Sequence 0x0008 0x9154 SQ Pixel_Presentation 0x0008 0x9205 CS Volumetric_Properties 0x0008 0x9206 CS Volume_Based_Calculation_Technique 0x0008 0x9207 CS Complex_Image_Component 0x0008 0x9208 CS Acquisition_Contrast 0x0008 0x9209 CS Derivation_Code_Sequence 0x0008 0x9215 SQ Referenced_Grayscale_Presentation_State_Sequence 0x0008 0x9237 SQ // More stuff omitted.. // Group 10 Patient's_Name 0x0010 0x0010 PN Patient_ID 0x0010 0x0020 LO Issuer_of_Patient_ID 0x0010 0x0021 LO Patient's_Birth_Date 0x0010 0x0030 DA Patient's_Birth_Time 0x0010 0x0032 TM Patient's_Sex 0x0010 0x0040 CS Patient's_Insurance_Plan_Code_Sequence 0x0010 0x0050 SQ Patient's_Primary_Language_Code_Sequence 0x0010 0x0101 SQ Patient's_Primary_Language_Code_Modifier_Sequence 0x0010 0x0102 SQ Other_Patient_IDs 0x0010 0x1000 LO Other_Patient_Names 0x0010 0x1001 PN Patient's_Birth_Name 0x0010 0x1005 PN Patient's_Age 0x0010 0x1010 AS Patient's_Size 0x0010 0x1020 DS Patient's_Weight 0x0010 0x1030 DS Patient's_Address 0x0010 0x1040 LO Insurance_Plan_Identification 0x0010 0x1050 RET Patient's_Mother's_Birth_Name 0x0010 0x1060 PN Military_Rank 0x0010 0x1080 LO Branch_of_Service 0x0010 0x1081 LO Medical_Record_Locator 0x0010 0x1090 LO Medical_Alerts 0x0010 0x2000 LO Contrast_Allergies 0x0010 0x2110 LO Country_of_Residence 0x0010 0x2150 LO Region_of_Residence 0x0010 0x2152 LO Patient's_Telephone_Numbers 0x0010 0x2154 SH Ethnic_Group 0x0010 0x2160 SH Occupation 0x0010 0x2180 SH Smoking_Status 0x0010 0x21A0 CS Additional_Patient_History 0x0010 0x21B0 LT Pregnancy_Status 0x0010 0x21C0 US Last_Menstrual_Date 0x0010 0x21D0 DA Patient's_Religious_Preference 0x0010 0x21F0 LO Patient_Comments 0x0010 0x4000 LT // Group 12 (clinical trial) Clinical_Trial_Sponsor_Name 0x0012 0x0010 LO Clinical_Trial_Protocol_ID 0x0012 0x0020 LO Clinical_Trial_Protocol_Name 0x0012 0x0021 LO Clinical_Trial_Site_ID 0x0012 0x0030 LO Clinical_Trial_Site_Name 0x0012 0x0031 LO Clinical_Trial_Subject_ID 0x0012 0x0040 LO Clinical_Trial_Subject_Reading_ID 0x0012 0x0042 LO Clinical_Trial_Time_Point_ID 0x0012 0x0050 LO Clinical_Trial_Time_Point_Description 0x0012 0x0051 ST Clinical_Trial_Coordinating_Center_Name 0x0012 0x0060 LO // Group 18 Contrast/Bolus_Agent 0x0018 0x0010 LO Contrast/Bolus_Agent_Sequence 0x0018 0x0012 SQ Contrast/Bolus_Agent_Administration_Route_Sequence 0x0018 0x0014 SQ Body_Part_Examined 0x0018 0x0015 CS Scanning_Sequence 0x0018 0x0020 CS Sequence_Variant 0x0018 0x0021 CS Scan_Options 0x0018 0x0022 CS MR_Acquisition_Type 0x0018 0x0023 CS Sequence_Name 0x0018 0x0024 SH Angio_Flag 0x0018 0x0025 CS Intervention_Drug_Information_Sequence 0x0018 0x0026 SQ Intervention_Drug_Stop_Time 0x0018 0x0027 TM Intervention_Drug_Dose 0x0018 0x0028 DS Intervention_Drug_Sequence 0x0018 0x0029 SQ Additional_Drug_Sequence 0x0018 0x002A SQ Radionuclide 0x0018 0x0030 LO Radiopharmaceutical 0x0018 0x0031 LO Energy_Window_Centerline 0x0018 0x0032 DS Energy_Window_Total_Width 0x0018 0x0033 DS Intervention_Drug_Name 0x0018 0x0034 LO Intervention_Drug_Start_Time 0x0018 0x0035 TM Intervention_Sequence 0x0018 0x0036 SQ Therapy_Type 0x0018 0x0037 CS Intervention_Status 0x0018 0x0038 CS Therapy_Description 0x0018 0x0039 CS Intervention_Description 0x0018 0x003A ST Cine_Rate 0x0018 0x0040 IS Slice_Thickness 0x0018 0x0050 DS KVP 0x0018 0x0060 DS Counts_Accumulated 0x0018 0x0070 IS Acquisition_Termination_Condition 0x0018 0x0071 CS Effective_Duration 0x0018 0x0072 DS Acquisition_Start_Condition 0x0018 0x0073 CS Acquisition_Start_Condition_Data 0x0018 0x0074 IS Acquisition_Termination_Condition_Data 0x0018 0x0075 IS Repetition_Time 0x0018 0x0080 DS Echo_Time 0x0018 0x0081 DS Inversion_Time 0x0018 0x0082 DS Number_of_Averages 0x0018 0x0083 DS Imaging_Frequency 0x0018 0x0084 DS Imaged_Nucleus 0x0018 0x0085 SH Echo_Number(s) 0x0018 0x0086 IS Magnetic_Field_Strength 0x0018 0x0087 DS Spacing_Between_Slices 0x0018 0x0088 DS Number_of_Phase_Encoding_Steps 0x0018 0x0089 IS Data_Collection_Diameter 0x0018 0x0090 DS Echo_Train_Length 0x0018 0x0091 IS Percent_Sampling 0x0018 0x0093 DS Percent_Phase_Field_of_View 0x0018 0x0094 DS Pixel_Bandwidth 0x0018 0x0095 DS Device_Serial_Number 0x0018 0x1000 LO Plate_ID 0x0018 0x1004 LO Secondary_Capture_Device_ID 0x0018 0x1010 LO Hardcopy_Creation_Device_ID 0x0018 0x1011 LO Date_of_Secondary_Capture 0x0018 0x1012 DA Time_of_Secondary_Capture 0x0018 0x1014 TM Secondary_Capture_Device_Manufacturer 0x0018 0x1016 LO Hardcopy_Device_Manufacturer 0x0018 0x1017 LO Secondary_Capture_Device_Manufacturer's_Model_name 0x0018 0x1018 LO Secondary_Capture_Device_Software_Version(s) 0x0018 0x1019 LO Hardcopy_Device_Software_Version 0x0018 0x101A LO Hardcopy_Device_Manufacturer's_Model_Name 0x0018 0x101B LO Software_Version 0x0018 0x1020 LO Video_Image_Format_Acquired 0x0018 0x1022 SH Digital_Image_Format_Acquired 0x0018 0x1022 LO Protocol_Name 0x0018 0x1030 LO Contrast/Bolus_Route 0x0018 0x1040 LO Contrast/Bolus_Volume 0x0018 0x1041 DS Contrast/Bolus_Start_Time 0x0018 0x1042 TM Contrast/Bolus_Stop_Time 0x0018 0x1043 TM Contrast/Bolus_Total_Dose 0x0018 0x1044 DS Syringe_Counts 0x0018 0x1045 IS Contrast_Flow_Rate 0x0018 0x1046 DS Contrast_Flow_Duration 0x0018 0x1047 DS Contrast/Bolus_Ingredient 0x0018 0x1048 CS Contrast/Bolus_Ingredient_Concentration 0x0018 0x1049 DS Spatial_Resolution 0x0018 0x1050 DS Trigger_Time 0x0018 0x1060 DS Trigger_Source_or_Type 0x0018 0x1061 LO Nominal_Interval 0x0018 0x1062 IS Frame_Time 0x0018 0x1063 DS Framing_Type 0x0018 0x1064 LO Frame_Time_Vector 0x0018 0x1065 DS Frame_Delay 0x0018 0x1066 DS Image_Trigger_Delay 0x0018 0x1067 DS Multiplex_Group_Time_Offset 0x0018 0x1068 DS Trigger_Time_Offset 0x0018 0x1069 DS Synchronization_Trigger 0x0018 0x106A CS Synchronization_Channel 0x0018 0x106C US Trigger_Sample_Position 0x0018 0x106E UL Radiopharmaceutical_Route 0x0018 0x1070 LO Radiopharmaceutical_Volume 0x0018 0x1071 DS Radiopharmaceutical_Start_Time 0x0018 0x1072 TM Radiopharmaceutical_Stop_Time 0x0018 0x1073 TM Radionuclide_Total_Dose 0x0018 0x1074 DS Radionuclide_Half_Life 0x0018 0x1075 DS Radionuclide_Positron_Fraction 0x0018 0x1076 DS Radiopharmaceutical_Specific_Activity 0x0018 0x1077 DS Beat_Rejection_Flag 0x0018 0x1080 CS Low_R-R_Value 0x0018 0x1081 IS High_R-R_Value 0x0018 0x1082 IS Intervals_Acquired 0x0018 0x1083 IS Intervals_Rejected 0x0018 0x1084 IS PVC_Rejection 0x0018 0x1085 LO Skip_Beats 0x0018 0x1086 IS Heart_Rate 0x0018 0x1088 IS Cardiac_Number_of_Images 0x0018 0x1090 IS // ... Reconstruction_Diameter 0x0018 0x1100 DS Distance_Source_to_Detector 0x0018 0x1110 DS Distance_Source_to_Patient 0x0018 0x1111 DS Estimated_Radiographic_Magnification_Factor 0x0018 0x1114 DS Gantry/Detector_Tilt 0x0018 0x1120 DS Gantry/Detector_Slew 0x0018 0x1121 DS Table_Height 0x0018 0x1130 DS Table_Traverse 0x0018 0x1131 DS Table_Motion 0x0018 0x1134 CS Table_Vertical_Increment 0x0018 0x1135 DS Table_Lateral_Increment 0x0018 0x1136 DS Table_Longitudinal_Increment 0x0018 0x1137 DS Table_Angle 0x0018 0x1138 DS Table_Type 0x0018 0x113A CS Rotation_Direction 0x0018 0x1140 CS Angular_Position 0x0018 0x1141 DS Radial_Position 0x0018 0x1142 DS Scan_Arc 0x0018 0x1143 DS Angular_Step 0x0018 0x1144 DS Center_of_Rotation_Offset 0x0018 0x1145 DS // ... Collimator/Grid_Name 0x0018 0x1180 SH Collimator_Type 0x0018 0x1181 CS // ... Date_of_Last_Calibration 0x0018 0x1200 DA Time_of_Last_Calibration 0x0018 0x1201 TM Convolution_Kernel 0x0018 0x1210 SH Upper/Lower_Pixel_Values 0x0018 0x1240 RET Actual_Frame_Duration 0x0018 0x1242 IS Count_Rate 0x0018 0x1243 IS Preferred_Playback_Sequencing 0x0018 0x1244 US Receive_Coil_Name 0x0018 0x1250 SH Transmit_Coil_Name 0x0018 0x1251 SH Plate_Type 0x0018 0x1260 SH Phosphor_Type 0x0018 0x1261 LO Scan_Velocity 0x0018 0x1300 DS Whole_Body_Technique 0x0018 0x1301 CS Scan_Length 0x0018 0x1302 IS Acquisition_Matrix 0x0018 0x1310 US In-plane_Phase_Encoding_Direction 0x0018 0x1312 CS Flip_Angle 0x0018 0x1314 DS Variable_Flip_Angle_Flag 0x0018 0x1315 CS SAR 0x0018 0x1316 DS dB/dt 0x0018 0x1318 DS Acquisition_Device_Processing_Description 0x0018 0x1400 LO Acquisition_Device_Processing_Code 0x0018 0x1401 LO Cassette_Orientation 0x0018 0x1402 CS Cassette_Size 0x0018 0x1403 CS Exposures_on_Plate 0x0018 0x1404 US //... Patient_Position 0x0018 0x5100 CS //... Content_Qualification 0x0018 0x9004 CS Pulse_Sequence_Name 0x0018 0x9005 SH MR_Imaging_Modifier_Sequence 0x0018 0x9006 SQ Echo_Pulse_Sequence 0x0018 0x9008 CS Inversion_Recovery 0x0018 0x9009 CS Flow_Compensation 0x0018 0x9010 CS Multiple_Spin_Echo 0x0018 0x9011 CS Multi-planar_Excitation 0x0018 0x9012 CS Phase_Contrast 0x0018 0x9014 CS Time_of_Flight_Contrast 0x0018 0x9015 CS Spoiling 0x0018 0x9016 CS Steady_State_Pulse_Sequence 0x0018 0x9017 CS Echo_Planar_Pulse_Sequence 0x0018 0x9018 CS Tag_Angle_First_Axis 0x0018 0x9019 FD Magnetization_Transfer 0x0018 0x9020 CS T2_Preparation 0x0018 0x9021 CS Blood_Signal_Nulling 0x0018 0x9022 CS Saturation_Recovery 0x0018 0x9024 CS Spectrally_Selected_Suppression 0x0018 0x9025 CS Spectrally_Selected_Excitation 0x0018 0x9026 CS Spatial_Pre-saturation 0x0018 0x9027 CS Tagging 0x0018 0x9028 CS Oversampled_Phase 0x0018 0x9029 CS Tag_Spacing_First_Dimension 0x0018 0x9030 FD Geometry_of_k-Space_Traversal 0x0018 0x9032 CS Segmented_k-Space_Traversal 0x0018 0x9033 CS Rectilinear_Phase_Encode_Reordering 0x0018 0x9034 CS Tag_Thickness 0x0018 0x9035 FD Partial_Fourier_Direction 0x0018 0x9036 CS Cardiac_Synchronization_Technique 0x0018 0x9037 CS Receive_Coil_Manufacturer_Name 0x0018 0x9041 LO MR_Receive_Coil_Sequence 0x0018 0x9042 SQ Receive_Coil_Type 0x0018 0x9043 CS Quadrature_Receive_Coil 0x0018 0x9044 CS Multi-Coil_Definition_Sequence 0x0018 0x9045 SQ Multi-Coil_Configuration 0x0018 0x9046 LO Multi-Coil_Element_Name 0x0018 0x9047 SH Multi-Coil_Element_Used 0x0018 0x9048 CS MR_Transmit_Coil_Sequence 0x0018 0x9049 SQ Transmit_Coil_Manufacturer_Name 0x0018 0x9050 LO Transmit_Coil_Type 0x0018 0x9051 CS Spectral_Width 0x0018 0x9052 FD Chemical_Shift_Reference 0x0018 0x9053 FD Volume_Localization_Technique 0x0018 0x9054 CS MR_Acquisition_Frequency_Encoding_Steps 0x0018 0x9058 US De-coupling 0x0018 0x9059 CS De-coupled_Nucleus 0x0018 0x9060 CS De-coupling_Frequency 0x0018 0x9061 FD De-coupling_Method 0x0018 0x9062 CS De-coupling_Chemical_Shift_Reference 0x0018 0x9063 FD k-space_Filtering 0x0018 0x9064 CS Time_Domain_Filtering 0x0018 0x9065 CS Number_of_Zero_fills 0x0018 0x9066 US Baseline_Correction 0x0018 0x9067 CS Parallel_Reduction_Factor_In-plane 0x0018 0x9069 FD Cardiac_R-R_Interval_Specified 0x0018 0x9070 FD Acquisition_Duration 0x0018 0x9073 FD Frame_Acquisition_Datetime 0x0018 0x9074 DT Diffusion_Directionality 0x0018 0x9075 CS Diffusion_Gradient_Direction_Sequence 0x0018 0x9076 SQ Parallel_Acquisition 0x0018 0x9077 CS Parallel_Acquisition_Technique 0x0018 0x9078 CS Inversion_Times 0x0018 0x9079 FD Metabolite_Map_Description 0x0018 0x9080 ST Partial_Fourier 0x0018 0x9081 CS Effective_Echo_Time 0x0018 0x9082 FD Metabolite_Map_Code_Sequence 0x0018 0x9083 SQ Chemical_Shift_Sequence 0x0018 0x9084 SQ Cardiac_Signal_Source 0x0018 0x9085 CS Diffusion_b-value 0x0018 0x9087 FD Diffusion_Gradient_Orientation 0x0018 0x9089 FD Velocity_Encoding_Direction 0x0018 0x9090 FD Velocity_Encoding_Minimum_Value 0x0018 0x9091 FD Number_of_k-Space_Trajectories 0x0018 0x9093 US Coverage_of_k-Space 0x0018 0x9094 CS Spectroscopy_Acquisition_Phase_Rows 0x0018 0x9095 UL Transmitter_Frequency 0x0018 0x9098 FD Resonant_Nucleus 0x0018 0x9100 CS Frequency_Correction 0x0018 0x9101 CS MR_Spectroscopy_FOV/Geometry_Sequence 0x0018 0x9103 SQ Slab_Thickness 0x0018 0x9104 FD Slab_Orientation 0x0018 0x9105 FD Mid_Slab_Position 0x0018 0x9106 FD MR_Spatial_Saturation_Sequence 0x0018 0x9107 SQ MR_Timing_and_Related_Parameters_Sequence 0x0018 0x9112 SQ MR_Echo_Sequence 0x0018 0x9114 SQ MR_Modifier_Sequence 0x0018 0x9115 SQ MR_Diffusion_Sequence 0x0018 0x9117 SQ Cardiac_Trigger_Sequence 0x0018 0x9118 SQ MR_Averages_Sequence 0x0018 0x9119 SQ MR_FOV/Geometry_Sequence 0x0018 0x9125 SQ Volume_Localization_Sequence 0x0018 0x9126 SQ Spectroscopy_Acquisition_Data_Columns 0x0018 0x9127 UL Diffusion_Anisotropy_Type 0x0018 0x9147 CS Frame_Reference_Datetime 0x0018 0x9151 DT MR_Metabolite_Map_Sequence 0x0018 0x9152 SQ Parallel_Reduction_Factor_Out-of-plane 0x0018 0x9155 FD Spectroscopy_Acquisition_Out-of-plane_Phase_Steps 0x0018 0x9159 UL Bulk_Motion_Status 0x0018 0x9166 CS Parallel_Reduction_Factor_Second_In-plane 0x0018 0x9168 FD Cardiac_Beat_Rejection_Technique 0x0018 0x9169 CS Respiratory_Motion_Compensation_Technique 0x0018 0x9170 CS Respiratory_Signal_Source 0x0018 0x9171 CS Bulk_Motion_Compensation_Technique 0x0018 0x9172 CS Bulk_Motion_Signal_Source 0x0018 0x9173 CS Applicable_Safety_Standard_Agency 0x0018 0x9174 CS Applicable_Safety_Standard_Description 0x0018 0x9175 LO Operating_Mode_Sequence 0x0018 0x9176 SQ Operating_Mode_Type 0x0018 0x9177 CS Operating_Mode 0x0018 0x9178 CS Specific_Absorption_Rate_Definition 0x0018 0x9179 CS Gradient_Output_Type 0x0018 0x9180 CS Specific_Absorption_Rate_Value 0x0018 0x9181 FD Gradient_Output 0x0018 0x9182 FD Flow_Compensation_Direction 0x0018 0x9183 CS Tagging_Delay 0x0018 0x9184 FD MR_Velocity_Encoding_Sequence 0x0018 0x9197 SQ First_Order_Phase_Correction 0x0018 0x9198 CS Water_Referenced_Phase_Correction 0x0018 0x9199 CS MR_Spectroscopy_Acquisition_Type 0x0018 0x9200 CS Respiratory_Cycle_Position 0x0018 0x9214 CS Velocity_Encoding_Maximum_Value 0x0018 0x9217 FD Tag_Spacing_Second_Dimension 0x0018 0x9218 FD Tag_Angle_Second_Axis 0x0018 0x9219 SS Frame_Acquisition_Duration 0x0018 0x9220 FD MR_Image_Frame_Type_Sequence 0x0018 0x9226 SQ MR_Spectroscopy_Frame_Type_Sequence 0x0018 0x9227 SQ MR_Acq_Phase_Encoding_Steps_in-plane 0x0018 0x9231 US MR_Acq_Phase_Encoding_Steps_out-of-plane 0x0018 0x9232 US Spectroscopy_Acquistion_Phase_Columns 0x0018 0x9234 UL Cardiac_Cycle_Position 0x0018 0x9236 CS Specific_Absorption_Rate_Sequence 0x0018 0x9239 SQ Chemical_Shifts_Minimum_Integration_Limit 0x0018 0x9295 FD Chemical_Shifts_Maximum_Integration_Limit 0x0018 0x9296 FD RF_Echo_Train_Length 0x0018 0x9240 US Gradient_Echo_Train_Length 0x0018 0x9241 US //... Contrast/Bolus_Agent_Number 0x0018 0x9337 US Contrast/Bolus_Ingredient_Code_Sequence 0x0018 0x9338 SQ Contrast/Bolus_Usage_Sequence 0x0018 0x9341 SQ Contrast/Bolus_Agent_Administered 0x0018 0x9342 CS Contrast/Bolus_Agent_Detected 0x0018 0x9343 CS Contrast/Bolus_Agent_Phase 0x0018 0x9344 CS //... // Group 20 (study) Study_Instance_UID 0x0020 0x000D UI Series_Instance_UID 0x0020 0x000E UI Study_ID 0x0020 0x0010 SH Series_Number 0x0020 0x0011 IS Acquisition_Number 0x0020 0x0012 IS Instance_Number 0x0020 0x0013 IS Isotope_Number 0x0020 0x0014 IS Phase_Number 0x0020 0x0015 IS Interval_Number 0x0020 0x0016 IS Time_Slot_Number 0x0020 0x0017 IS Angle_Number 0x0020 0x0018 IS Item_Number 0x0020 0x0019 IS Patient_Orientation 0x0020 0x0020 CS Overlay_Number 0x0020 0x0022 IS Curve_Number 0x0020 0x0024 IS Lookup_Table_Number 0x0020 0x0026 IS Image_Position 0x0020 0x0030 RET Image_Position_(Patient) 0x0020 0x0032 DS Image_Orientation 0x0020 0x0035 RET Image_Orientation_(Patient) 0x0020 0x0037 DS Location 0x0020 0x0050 RET Frame_of_Reference_UID 0x0020 0x0052 UI Laterality 0x0020 0x0060 CS Image_Laterality 0x0020 0x0062 CS Image_Geometry_Type 0x0020 0x0070 RET Masking_Image 0x0020 0x0080 RET Temporal_Position_Identifier 0x0020 0x0100 IS Number_of_Temporal_Positions 0x0020 0x0105 IS Temporal_Resolution 0x0020 0x0110 DS Synchronization_Frame_of_Refernence_UID 0x0020 0x0200 UI Series_in_Study 0x0020 0x1000 IS Acquisitions_in_Series 0x0020 0x1001 RET Images_in_Acquisition 0x0020 0x1002 IS Acquisitions_in_Study 0x0020 0x1004 IS Reference 0x0020 0x1020 RET Position_Reference_Indicator 0x0020 0x1040 LO Slice_Location 0x0020 0x1041 DS Other_Study_Numbers 0x0020 0x1070 IS Number_of_Patient_Related_Studies 0x0020 0x1200 IS Number_of_Patient_Related_Series 0x0020 0x1202 IS Number_of_Patient_Related_Instances 0x0020 0x1204 IS Number_of_Study_Related_Series 0x0020 0x1206 IS Number_of_Study_Related_Instances 0x0020 0x1208 IS //... Image_Comments 0x0020 0x4000 LT //... Stack_ID 0x0020 0x9056 SH In-Stack_Position_Number 0x0020 0x9057 UL Frame_Anatomy_Sequence 0x0020 0x9071 SQ Frame_Laterality 0x0020 0x9072 CS Frame_Content_Sequence 0x0020 0x9111 SQ Plane_Position_Sequence 0x0020 0x9113 SQ Plane_Orientation_Sequence 0x0020 0x9116 SQ Temporal_Position_Index 0x0020 0x9128 UL Trigger_Delay_Time 0x0020 0x9153 FD Frame_Acquisition_Number 0x0020 0x9156 US Dimension_Index_Values 0x0020 0x9157 UL Frame_Comments 0x0020 0x9158 LT Concatenation_UID 0x0020 0x9161 UI In-concatenation_Number 0x0020 0x9162 US In-concatenation_Total_Number 0x0020 0x9163 US Dimension_Organization_UID 0x0020 0x9164 UI Dimension_Index_Pointer 0x0020 0x9165 AT Functional_Group_Pointer 0x0020 0x9167 AT Dimension_Index_Private_Creator 0x0020 0x9213 LO Dimension_Organization_Sequence 0x0020 0x9221 SQ Dimension_Index_Sequence 0x0020 0x9222 SQ //... //Group 28 Samples_per_Pixel 0x0028 0x0002 US Samples_per_Pixel_Used 0x0028 0x0003 US Photometric_Interpretation 0x0028 0x0004 CS Image_Dimensions 0x0028 0x0005 RET Planar_Configuration 0x0028 0x0006 US Number_of_Frames 0x0028 0x0008 IS Frame_Increment_Pointer 0x0028 0x0009 AT Frame_Dimension_Pointer 0x0028 0x000A AT Rows 0x0028 0x0010 US Columns 0x0028 0x0011 US Planes 0x0028 0x0012 US Ultrasound_Color_Data_Present 0x0028 0x0014 US Pixel_Spacing 0x0028 0x0030 DS Zoom_Factor 0x0028 0x0031 DS Zoom_Center 0x0028 0x0032 DS Pixel_Aspect_Ratio 0x0028 0x0034 IS Image_Format 0x0028 0x0040 RET Manipulated_Image 0x0028 0x0050 RET Corrected_Image 0x0028 0x0051 CS Compression_Code 0x0028 0x0060 RET Bits_Allocated 0x0028 0x0100 US Bits_Stored 0x0028 0x0101 US High_Bit 0x0028 0x0102 US Pixel_Representation 0x0028 0x0103 US Smallest_Valid_Pixel_Value 0x0028 0x0104 RET Largest_Valid_Pixel_Value 0x0028 0x0105 RET Smallest_Image_Pixel_Value 0x0028 0x0106 US Largest_Image_Pixel_Value 0x0028 0x0107 US Smallest_Pixel_Value_in_Series 0x0028 0x0108 US Largest_Pixel_Value_in_Series 0x0028 0x0109 US Smallest_Pixel_Value_in_Plane 0x0028 0x0110 US Largest_Pixel_Value_in_Plane 0x0028 0x0111 US Pixel_Padding_Value 0x0028 0x0120 US Image_Location 0x0028 0x0200 RET Quality_Control_Image 0x0028 0x0300 CS Burned_In_Annotation 0x0028 0x0301 CS Pixel_Intensity_Relationship 0x0028 0x1040 CS Pixel_Intensity_Relationship_Sign 0x0028 0x1041 SS Window_Center 0x0028 0x1050 DS Window_Width 0x0028 0x1051 DS Rescale_Intercept 0x0028 0x1052 DS Rescale_Slope 0x0028 0x1053 DS Rescale_Type 0x0028 0x1054 LO Window_Center&Width_Explanation 0x0028 0x1055 LO Gray_Scale 0x0028 0x1080 RET Recommended_Viewing_Mode 0x0028 0x1090 CS Gray_Lookup_Table_Descriptor 0x0028 0x1100 RET Red_Palette_Lookup_Table_Descriptor 0x0028 0x1101 US Green_Palette_Lookup_Table_Descriptor 0x0028 0x1102 US Blue_Palette_Lookup_Table_Descriptor 0x0028 0x1103 US Palette_Color_Lookup_Table_UID 0x0028 0x1199 UI Gray_Lookup_Table_Data 0x0028 0x1200 RET Red_Palette_Color_Lookup_Table_Data 0x0028 0x1201 OW Green_Palette_Color_Lookup_Table_Data 0x0028 0x1202 OW Blue_Palette_Color_Lookup_Table_Data 0x0028 0x1203 OW //... Lossy_Image_Compression 0x0028 0x2110 CS Lossy_Image_Compression_Ratio 0x0028 0x2112 DS Lossy_Image_Compression_Method 0x0028 0x2114 CS LUT_Explanation 0x0028 0x3003 LO Pixel_Measures_Sequence 0x0028 0x9110 SQ Frame_VOI_LUT_Sequence 0x0028 0x9132 SQ Pixel_Value_Transformation_Sequence 0x0028 0x9145 SQ //... //Group 32 Requested_Procedure_Description 0x0032 0x1060 LO Study_Comments 0x0032 0x4000 LT //Group 40 Performed_Procedure_Step_Start_Date 0x0040 0x0244 DA Performed_Procedure_Step_Start_Time 0x0040 0x0245 TM Performed_Procedure_Step_ID 0x0040 0x0253 SH Acquisition_Context_Sequence 0x0040 0x0555 SQ Measurement_Units_Code_Sequence 0x0040 0x08EA SQ Real_World_Value_Mapping_Sequence 0x0040 0x9096 SQ LUT_Label 0x0040 0x9210 SH Real_World_Value_Last_Value_Mapped 0x0040 0x9211 US Real_World_Value_LUT_Data 0x0040 0x9212 FD Real_World_Value_First_Value_Mapped 0x0040 0x9216 US Real_World_Value_Intercept 0x0040 0x9224 FD Real_World_Value_Slope 0x0040 0x9225 FD Purpose_of_Reference_Code_Sequence 0x0040 0xA170 SQ //Group 54 Energy_Window_Vector 0x0054 0x0010 US Number_of_Energy_Windows 0x0054 0x0011 US Energy_Window_Information_Sequence 0x0054 0x0012 SQ Energy_Window_Range_Sequence 0x0054 0x0013 SQ Energy_Window_Lower_Limit 0x0054 0x0014 DS Energy_Window_Upper_Limit 0x0054 0x0015 DS Radiopharmaceutical_Information_Sequence 0x0054 0x0016 SQ Residual_Syringe_Counts 0x0054 0x0017 IS Energy_Window_Name 0x0054 0x0018 SH Detector_Vector 0x0054 0x0020 US Number_of_Detectors 0x0054 0x0021 US Detector_Information_Sequence 0x0054 0x0022 SQ Phase_Vector 0x0054 0x0030 US Number_of_Phases 0x0054 0x0031 US Phase_Information_Sequence 0x0054 0x0032 SQ Number_of_Frames_in_Phase 0x0054 0x0033 US Phase_Delay 0x0054 0x0036 IS Pause_Between_Frames 0x0054 0x0038 IS Phase_Description 0x0054 0x0039 CS Rotation_Vector 0x0054 0x0050 US Number_of_Rotations 0x0054 0x0051 US Rotation_Information_Sequence 0x0054 0x0052 SQ Number_of_Frames_in_Rotation 0x0054 0x0053 US R-R_Interval_Vector 0x0054 0x0060 US Number_of_R-R_Intervals 0x0054 0x0061 US Gated_Information_Sequence 0x0054 0x0062 SQ Data_Information_Sequence 0x0054 0x0063 SQ Time_Slot_Vector 0x0054 0x0070 US Number_of_Time_Slots 0x0054 0x0071 US Time_Slot_Information_Sequence 0x0054 0x0072 SQ Time_Slot_Time 0x0054 0x0073 DS Slice_Vector 0x0054 0x0080 US Number_of_Slices 0x0054 0x0081 US Angular_View_Vector 0x0054 0x0090 US Time_Slice_Vector 0x0054 0x0100 US Number_of_Time_Slices 0x0054 0x0101 US Start_Angle 0x0054 0x0200 DS Type_of_Detector_Motion 0x0054 0x0202 CS Trigger_Vector 0x0054 0x0210 IS Number_of_Triggers_in_Phase 0x0054 0x0211 US View_Code_Sequence 0x0054 0x0220 SQ View_Modifier_Code_Sequence 0x0054 0x0222 SQ Radionuclide_Code_Sequence 0x0054 0x0300 SQ Administration_Route_Code_Sequence 0x0054 0x0302 SQ Radiopharmaceutical_Code_Sequence 0x0054 0x0304 SQ Calibration_Data_Sequence 0x0054 0x0306 SQ Energy_Window_Number 0x0054 0x0308 US Image_ID 0x0054 0x0400 SH Patient_Orientation_Code_Sequence 0x0054 0x0410 SQ Patient_Orientation_Modifier_Code_Sequence 0x0054 0x0412 SQ Patient_Gantry_Relationship_Code_Sequence 0x0054 0x0414 SQ Slice_Progression_Direction 0x0054 0x0500 CS Series_Type 0x0054 0x1000 CS Units 0x0054 0x1001 CS Counts_Source 0x0054 0x1002 CS Reprojection_Method 0x0054 0x1004 CS Randoms_Correction_Method 0x0054 0x1100 CS Attenuation_Correction_Method 0x0054 0x1101 LO Decay_Correction 0x0054 0x1102 CS Reconstruction_Method 0x0054 0x1103 LO Detector_Lines_of_Response_Used 0x0054 0x1104 LO Scatter_Correction_Method 0x0054 0x1105 LO Axial_Acceptance 0x0054 0x1200 DS Axial_Mash 0x0054 0x1201 IS Transverse_Mash 0x0054 0x1202 IS Detector_Element_Size 0x0054 0x1203 DS Concidence_Window_Width 0x0054 0x1210 DS Secondary_Counts_Type 0x0054 0x1220 CS Frame_Reference_Time 0x0054 0x1300 DS Primary_(Prompts)_Counts_Accumulated 0x0054 0x1310 IS Secondary_Counts_Accumulated 0x0054 0x1311 IS Slice_Sensitivity_Factor 0x0054 0x1320 DS Decay_Factor 0x0054 0x1321 DS Dose_Calibration_Factor 0x0054 0x1322 DS Scatter_Fraction_Factor 0x0054 0x1323 DS Dead_Time_Factor 0x0054 0x1324 DS Image_Index 0x0054 0x1330 US Counts_Included 0x0054 0x1400 CS Dead_Time_Correction_Flag 0x0054 0x1401 CS //Group 0x2050 Presentation_LUT_Sequence 0x2050 0x0010 SQ Presentation_LUT_Shape 0x2050 0x0020 CS Referenced_Presentation_LUT_Sequence 0x2050 0x0500 SQ //Group 0x5200 Shared_Functional_Groups_Sequence 0x5200 0x9229 SQ Per_Frame_Functional_Groups_Sequence 0x5200 0x9230 SQ //Group 0x7fe0 Pixel_Data 0x7fe0 0x0010 OW minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/0002755000175000000620000000000012574624760021263 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/dicom_client_routines.h0000644000175000000620000000741612574624760026023 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_client_routines.h @DESCRIPTION: Header for dicom_client_routines.c @GLOBALS : @CREATED : July 9, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_client_routines.h,v $ * Revision 6.6 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.5 1999/10/29 17:51:50 neelin * Fixed Log keyword * * Revision 6.4 1998/11/13 15:55:27 neelin * Modifications to support asynchronous transfers. * * Revision 6.3 1998/03/23 20:17:17 neelin * Removed some functions. * * Revision 6.2 1997/10/20 23:22:38 neelin * Added routine acr_dicom_close_no_release to close a connection that does * not have an association. * * Revision 6.1 1997/09/15 16:50:59 neelin * Separated out connection timeouts from i/o timeouts and added functions * to change them. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 1.1 1997/09/08 21:52:21 neelin * Initial revision * * Revision 1.3 1997/07/11 17:35:58 neelin * Changed around send and receive routines for data once again. * * Revision 1.2 1997/07/11 13:23:37 neelin * Made changes so that code will compile on sun OS. * Separated out receive_reply from send_group_list. * * Revision 1.1 1997/07/10 17:39:51 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ /* Function prototypes */ public int acr_open_dicom_connection(char *host, char *port, char *called_ae, char *calling_ae, char *abstract_syntax, char *transfer_syntax, Acr_File **afpin, Acr_File **afpout); public void acr_close_dicom_no_release(Acr_File *afpin, Acr_File *afpout); public void acr_close_dicom_connection(Acr_File *afpin, Acr_File *afpout); public int acr_connect_to_host(char *host, char *port, FILE **fpin, FILE **fpout); public char *acr_make_dicom_association(Acr_File *afpin, Acr_File *afpout, char *called_ae, char *calling_ae, char *abstract_syntax_list[], char *transfer_syntax_list[]); public void acr_set_client_timeout(Acr_File *afp, double seconds); public void acr_set_client_initial_timeout(double seconds); public void acr_set_client_max_outstanding(Acr_File *afp, int max); public int acr_get_client_max_outstanding(Acr_File *afp); public void acr_dicom_error(Acr_Status status, char *string); public int acr_release_dicom_association(Acr_File *afpin, Acr_File *afpout); public int acr_send_group_list(Acr_File *afpin, Acr_File *afpout, Acr_Group group_list, char *sop_class_uid); public int acr_transmit_group_list(Acr_File *afpout, Acr_Group group_list, char *sop_class_uid, int message_id); public int acr_receive_reply(Acr_File *afpin); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/dicom_network.h0000644000175000000620000001404112574624760024276 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_network.h @DESCRIPTION: Header file for dicom network code @METHOD : @GLOBALS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_network.h,v $ * Revision 6.7 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.6 2001/03/19 18:30:33 neelin * Added function to set implementation uid and changed name of function * that gets it. * * Revision 6.5 2000/05/17 20:17:48 neelin * Added mechanism to allow testing of input streams for more data through * function acr_file_ismore. * This is used in dicom_client_routines to allow asynchronous transfer * of data, with testing for more input done before sending new messages. * Previous use of select for this was misguided, since select may report that * no data is waiting on the file descriptor while data is store in the file * pointer buffer (or Acr file pointer buffer). * * Revision 6.4 1999/10/29 17:51:51 neelin * Fixed Log keyword * * Revision 6.3 1998/11/11 17:05:03 neelin * Added pointer for client data to dicom structure. * * Revision 6.2 1998/03/23 20:16:37 neelin * Added functions. * * Revision 6.1 1997/10/20 22:52:46 neelin * Added support for implementation user information in association request. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/09 20:01:40 neelin * Added function acr_dicom_get_io_data. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 1.2 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 1.1 1997/02/20 16:38:17 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ /* PDU types */ #define ACR_PDU_ASSOC_RQ 0x01 #define ACR_PDU_ASSOC_AC 0x02 #define ACR_PDU_ASSOC_RJ 0x03 #define ACR_PDU_DATA_TF 0x04 #define ACR_PDU_REL_RQ 0x05 #define ACR_PDU_REL_RP 0x06 #define ACR_PDU_ABORT_RQ 0x07 /* Offset for unknown PDU item types */ #define ACR_UNKNOWN_PDU_ITEM_OFFSET 0xFF00 /* Element ids for PDU messages. These are artificial and are for internal use only, so they have negative group numbers. Unrecognized PDU items are stored with element id 0xffxx, where xx is the PDU item type */ #define DCM_PDU_GRPID (-1) #define DCM_PDU_ELEMENT(name, elid, vr) \ ACRLIB_GLOBAL_ELEMENT(name, DCM_PDU_GRPID, elid, vr) DCM_PDU_ELEMENT(DCM_PDU_Type, 0x0010, US); DCM_PDU_ELEMENT(DCM_PDU_Protocol_Version, 0x0020, US); DCM_PDU_ELEMENT(DCM_PDU_Called_Ap_title, 0x0030, UI); DCM_PDU_ELEMENT(DCM_PDU_Calling_Ap_title, 0x0040, UI); DCM_PDU_ELEMENT(DCM_PDU_Application_context, 0x0050, UI); DCM_PDU_ELEMENT(DCM_PDU_Presentation_context, 0x0060, SQ); DCM_PDU_ELEMENT(DCM_PDU_Presentation_context_reply, 0x0070, SQ); DCM_PDU_ELEMENT(DCM_PDU_Presentation_context_list, 0x0080, SQ); DCM_PDU_ELEMENT(DCM_PDU_Presentation_context_reply_list, 0x0090, SQ); DCM_PDU_ELEMENT(DCM_PDU_Presentation_context_id, 0x0100, US); DCM_PDU_ELEMENT(DCM_PDU_Abstract_syntax, 0x0120, UI); DCM_PDU_ELEMENT(DCM_PDU_Transfer_syntax, 0x0130, UI); DCM_PDU_ELEMENT(DCM_PDU_Maximum_length, 0x0140, UL); DCM_PDU_ELEMENT(DCM_PDU_Implementation_class_uid, 0x0141, UI); DCM_PDU_ELEMENT(DCM_PDU_Implementation_version_name, 0x0142, UI); DCM_PDU_ELEMENT(DCM_PDU_Result, 0x0150, US); DCM_PDU_ELEMENT(DCM_PDU_Source, 0x0160, US); DCM_PDU_ELEMENT(DCM_PDU_Reason, 0x0170, US); /* Function prototypes */ public int acr_uid_equal(char *uid1, char *uid2); public char *acr_create_uid(void); public void acr_set_implementation_uid(char *uid); public char *acr_get_implementation_uid(void); public Acr_Status acr_input_dicom_message(Acr_File *dicom_afp, Acr_Message *message); public Acr_Status acr_output_dicom_message(Acr_File *dicom_afp, Acr_Message message); public Acr_File *acr_initialize_dicom_input(void *io_data, int maxlength, Acr_Io_Routine io_routine); public void acr_dicom_set_ismore_function(Acr_File *afp, Acr_Ismore_Function ismore_function); public Acr_File *acr_initialize_dicom_output(void *io_data, int maxlength, Acr_Io_Routine io_routine); public void acr_close_dicom_file(Acr_File *afp); public void *acr_dicom_get_io_data(Acr_File *afp); public void acr_dicom_enable_trace(Acr_File *afp); public void acr_dicom_disable_trace(Acr_File *afp); public void acr_dicom_set_eof(Acr_File *afp); public void acr_set_dicom_maximum_length(Acr_File *afp, long maximum_length); public void acr_set_dicom_pres_context_id(Acr_File *afp, int presentation_context_id); public int acr_get_dicom_pres_context_id(Acr_File *afp); public void acr_set_dicom_client_data(Acr_File *afp, void *client_data); public void *acr_get_dicom_client_data(Acr_File *afp); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/message.h0000644000175000000620000000601312574624760023056 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : message.h @DESCRIPTION: Header file for acr-nema message code @METHOD : @GLOBALS : @CREATED : November 16, 1993 (Peter Neelin) @MODIFIED : * $Log: message.h,v $ * Revision 6.2 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.1 1999/10/29 17:51:54 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:18 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:35:53 neelin * Pre-release * * Revision 1.3 93/11/24 11:27:10 neelin * Added dump message routine./ * * Revision 1.2 93/11/22 13:12:55 neelin * Changed to use new Acr_Element_Id stuff. * * Revision 1.1 93/11/19 12:50:37 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. ---------------------------------------------------------------------------- */ /* Group type */ typedef struct Acr_Message { int ngroups; long implicit_total_length; long explicit_total_length; long message_implicit_offset; long message_explicit_offset; Acr_Element message_length_element; Acr_Group list_head; Acr_Group list_tail; } *Acr_Message; /* Functions */ public Acr_Message acr_create_message(void); public void acr_delete_message(Acr_Message message); public void acr_message_reset(Acr_Message message); public void acr_message_add_group(Acr_Message message, Acr_Group group); public void acr_message_add_group_list(Acr_Message message, Acr_Group group_list); public Acr_Group acr_get_message_group_list(Acr_Message message); public long acr_get_message_total_length(Acr_Message message, Acr_VR_encoding_type vr_encoding); public int acr_get_message_ngroups(Acr_Message message); public Acr_Status acr_input_message(Acr_File *afp, Acr_Message *message); public Acr_Status acr_output_message(Acr_File *afp, Acr_Message message); public void acr_dump_message(FILE *file_pointer, Acr_Message message); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/file_io.h0000644000175000000620000001252312574624760023043 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : file_io.h @DESCRIPTION: Header file for doing io from acr_nema code. @METHOD : @GLOBALS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : * $Log: file_io.h,v $ * Revision 6.4 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.3 2005/03/04 00:08:08 bert * Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * * Revision 6.2 2000/05/17 20:17:48 neelin * Added mechanism to allow testing of input streams for more data through * function acr_file_ismore. * This is used in dicom_client_routines to allow asynchronous transfer * of data, with testing for more input done before sending new messages. * Previous use of select for this was misguided, since select may report that * no data is waiting on the file descriptor while data is store in the file * pointer buffer (or Acr file pointer buffer). * * Revision 6.1 1999/10/29 17:51:53 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:14 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:35:43 neelin * Pre-release * * Revision 1.3 94/04/08 10:32:10 neelin * Fixed io tracing. * * Revision 1.2 93/11/25 10:37:43 neelin * Added file free and ungetc and trace. * * Revision 1.1 93/11/10 10:33:33 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. ---------------------------------------------------------------------------- */ /* Define constants */ #define ACR_NO_WATCHPOINT LONG_MAX /* Define io routine prototype */ typedef int (*Acr_Io_Routine) (void *io_data, void *buffer, int nbytes); /* Define prototype for io routine that tests for more input */ typedef int (*Acr_Ismore_Function) (void *io_data); /* Structure used for reading and writing in acr_nema routines */ typedef struct { void *io_data; Acr_Io_Routine io_routine; Acr_Ismore_Function ismore_function; int maxlength; /* Maximum length of read request */ unsigned char *start; unsigned char *end; unsigned char *ptr; int length; /* Length of actual data in buffer */ int buffer_length; /* Length of allocated buffer */ int stream_type; int reached_eof; /* TRUE if we have reached end of file */ int do_trace; /* TRUE if file should be traced */ FILE *tracefp; int watchpoint_set; /* TRUE if a watchpoint is set */ long bytes_to_watchpoint; /* number of bytes from start of buffer to watchpoint */ void *client_data; /* Data that can be set by calling routines */ } Acr_File; /* Macros for getting and putting a character */ #define acr_getc(afp) ( ((afp)->ptr < (afp)->end) ? \ *((afp)->ptr++) : \ acr_file_read_more(afp) ) #define acr_putc(c,afp) ( ((afp)->ptr < (afp)->end) ? \ (int) (*((afp)->ptr++) = (unsigned char) (c)) : \ acr_file_write_more(afp, c) ) /* Function definitions */ extern void acr_file_enable_trace(Acr_File *afp); extern void acr_file_disable_trace(Acr_File *afp); extern Acr_File *acr_file_initialize(void *io_data, int maxlength, Acr_Io_Routine io_routine); extern void acr_file_free(Acr_File *afp); extern void acr_file_reset(Acr_File *afp); extern void acr_file_set_ismore_function(Acr_File *afp, Acr_Ismore_Function ismore_function); extern void acr_file_set_eof(Acr_File *afp); extern void acr_file_set_client_data(Acr_File *afp, void *client_data); extern void *acr_file_get_client_data(Acr_File *afp); extern int acr_file_read_more(Acr_File *afp); extern int acr_file_write_more(Acr_File *afp, int character); extern int acr_file_flush(Acr_File *afp); extern int acr_ungetc(int c, Acr_File *afp); extern void *acr_file_get_io_data(Acr_File *afp); extern void acr_set_io_watchpoint(Acr_File *afp, long bytes_to_watchpoint); extern long acr_get_io_watchpoint(Acr_File *afp); extern int acr_file_ismore(Acr_File *afp); extern int acr_stdio_read(void *io_data, void *buffer, int nbytes); extern int acr_stdio_write(void *io_data, void *buffer, int nbytes); extern int acr_stdio_ismore(void *io_data); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/group.h0000644000175000000620000001547012574624760022575 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : group.h @DESCRIPTION: Header file for acr-nema group code @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: group.h,v $ * Revision 6.7 2008-08-12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.6 2006/04/09 15:29:43 bert * Add acr_insert_double() * * Revision 6.5 2005/03/11 22:05:29 bert * Implement _acr_name_proc to allow printing of field names in dump_acr_nema * * Revision 6.4 2005/03/04 00:08:08 bert * Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * * Revision 6.3 2001/11/08 14:17:06 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.2 1999/10/29 17:51:53 neelin * Fixed Log keyword * * Revision 6.1 1998/11/06 19:41:06 neelin * Added functions acr_group_steal_element and acr_find_group. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.2 1997/08/21 13:24:55 neelin * Pre-release * * Revision 4.1 1997/06/17 23:49:08 neelin * Added routines for inserting elements into a group list. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:17 neelin * Release of minc version 0.2 * * Revision 1.6 94/09/28 10:35:52 neelin * Pre-release * * Revision 1.5 93/12/10 09:20:44 neelin * Added acr_find_ routines. * * Revision 1.4 93/11/26 18:48:04 neelin * Added group and group list copy routines. * * Revision 1.3 93/11/24 11:26:38 neelin * Added group list stuff (dump and input). * * Revision 1.2 93/11/22 13:12:41 neelin * Changed to use new Acr_Element_Id stuff. * * Revision 1.1 93/11/19 12:50:32 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. ---------------------------------------------------------------------------- */ #ifndef _ACR_GROUP_H_ #define _ACR_GROUP_H_ 1 /* Group type */ typedef struct Acr_Group { int group_id; int nelements; long implicit_total_length; long explicit_total_length; Acr_Element list_head; Acr_Element list_tail; struct Acr_Group *next; } *Acr_Group; /* Group length element id */ #define ACR_EID_GRPLEN 0 /* Functions */ extern Acr_Group acr_create_group(int group_id); extern void acr_delete_group(Acr_Group group); extern void acr_delete_group_list(Acr_Group group_list); extern Acr_Group acr_copy_group(Acr_Group group); extern Acr_Group acr_copy_group_list(Acr_Group group_list); extern Acr_Status acr_group_insert_element(Acr_Group group, Acr_Element element); extern Acr_Status acr_group_add_element(Acr_Group group, Acr_Element element); extern void acr_group_remove_element(Acr_Group group, int element_id); extern void acr_group_steal_element(Acr_Group group, Acr_Element element); extern void acr_set_group_next(Acr_Group group, Acr_Group next); extern int acr_get_group_group(Acr_Group group); extern Acr_Element acr_get_group_element_list(Acr_Group group); extern long acr_get_group_total_length(Acr_Group group, Acr_VR_encoding_type vr_encoding); extern int acr_get_group_nelements(Acr_Group group); extern Acr_Group acr_get_group_next(Acr_Group group); extern Acr_Status acr_input_group(Acr_File *afp, Acr_Group *group); extern Acr_Status acr_output_group(Acr_File *afp, Acr_Group group); extern Acr_Status acr_input_group_list(Acr_File *afp, Acr_Group *group_list, int max_group_id); extern Acr_Group acr_find_group(Acr_Group group_list, int group_id); extern Acr_Element acr_find_group_element(Acr_Group group_list, Acr_Element_Id elid); extern void acr_dump_group_list(FILE *file_pointer, Acr_Group group_list); extern Acr_Short acr_find_short(Acr_Group group_list, Acr_Element_Id elid, Acr_Short default_value); extern Acr_Long acr_find_long(Acr_Group group_list, Acr_Element_Id elid, Acr_Long default_value); extern int acr_find_int(Acr_Group group_list, Acr_Element_Id elid, int default_value); extern Acr_Double acr_find_double(Acr_Group group_list, Acr_Element_Id elid, Acr_Double default_value); extern Acr_String acr_find_string(Acr_Group group_list, Acr_Element_Id elid, Acr_String default_value); extern Acr_Status acr_insert_element_into_group_list(Acr_Group *group_list, Acr_Element element); extern Acr_Status acr_insert_short(Acr_Group *group_list, Acr_Element_Id elid, Acr_Short value); extern Acr_Status acr_insert_long(Acr_Group *group_list, Acr_Element_Id elid, Acr_Long value); extern Acr_Status acr_insert_double(Acr_Group *group_list, Acr_Element_Id elid, int nvalues, Acr_Double *values); extern Acr_Status acr_insert_numeric(Acr_Group *group_list, Acr_Element_Id elid, double value); extern Acr_Status acr_insert_string(Acr_Group *group_list, Acr_Element_Id elid, Acr_String value); extern Acr_Status acr_insert_sequence(Acr_Group *group_list, Acr_Element_Id elid, Acr_Element itemlist); extern Acr_Status acr_test_dicom_file(Acr_File *afp); typedef char *(*acr_name_proc_t)(unsigned int grp_id, unsigned int el_id); extern acr_name_proc_t _acr_name_proc; #endif /* _ACR_GROUP_H_ */ minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/acr_io.h0000644000175000000620000001731612574624760022676 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : acr_nema_io.h @DESCRIPTION: Header file for acr_nema_io code. @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: acr_io.h,v $ * Revision 6.4 2008-08-12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.3 2005/03/04 00:08:08 bert * Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * * Revision 6.2 2000/04/28 15:03:11 neelin * Added support for ignoring non-fatal protocol errors (cases where redundant * information is inconsistent). In particular, it is possible to ignore * differences between the group length element and the true group length. * * Revision 6.1 1999/10/29 17:51:50 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/08 21:53:31 neelin * Added status ACR_CONNECTION_TIMEDOUT. * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/10 17:14:38 neelin * Added more status codes and function to return status string. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:07 neelin * Release of minc version 0.2 * * Revision 1.8 94/09/28 10:35:40 neelin * Pre-release * * Revision 1.7 94/05/18 08:48:17 neelin * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT. * * Revision 1.6 94/04/07 10:23:28 neelin * Added ACR_HIGH_LEVEL_ERROR. * * Revision 1.5 94/04/07 10:05:10 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.4 94/01/06 13:31:30 neelin * Changed acr_need_invert to a public function. * * Revision 1.3 93/11/25 10:37:26 neelin * Added byte-ordering test. * * Revision 1.2 93/11/24 11:26:22 neelin * Changed short to unsigned short. * * Revision 1.1 93/11/19 12:50:15 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. ---------------------------------------------------------------------------- */ /* Length of short and long for ACR-NEMA protocol */ #define ACR_SIZEOF_SHORT 2 #define ACR_SIZEOF_LONG 4 #define ACR_SIZEOF_FLOAT 4 #define ACR_SIZEOF_DOUBLE 8 /* Define the C data types for ACR-NEMA protocol. Note: "numeric" functions use the C double datatype directly. */ typedef unsigned short Acr_Short; typedef unsigned int Acr_Long; /* 4 bytes in both 32 and 64 bits */ typedef float Acr_Float; typedef double Acr_Double; typedef char * Acr_String; /* Undefined element length value for VRs of SQ, OB, OW */ #define ACR_UNDEFINED_ELEMENT_LENGTH 0xFFFFFFFFU /* Tags for sequence items */ #define ACR_ITEM_GROUP 0xfffe #define ACR_ITEM_TAG 0xe000 #define ACR_ITEM_DELIMITER 0xe00d #define ACR_SEQ_DELIMITER 0xe0dd /* Flag for data length to indicate variable length elements */ #define ACR_VARIABLE_LENGTH ((Acr_Long) -1) /* Byte-ordering options */ typedef enum { ACR_UNKNOWN_ENDIAN = 0, ACR_LITTLE_ENDIAN, ACR_BIG_ENDIAN } Acr_byte_order; /* VR encoding options */ typedef enum { ACR_UNKNOWN_VR = 0, ACR_EXPLICIT_VR, ACR_IMPLICIT_VR } Acr_VR_encoding_type; /* Status for io */ typedef enum { ACR_OK, ACR_END_OF_INPUT, ACR_PROTOCOL_ERROR, ACR_OTHER_ERROR, ACR_ABNORMAL_END_OF_INPUT, ACR_HIGH_LEVEL_ERROR, ACR_ABNORMAL_END_OF_OUTPUT, ACR_REACHED_WATCHPOINT, ACR_IO_ERROR, ACR_NO_VR_SPECIFIED, ACR_PDU_UID_TOO_LONG, ACR_CONNECTION_TIMEDOUT } Acr_Status; /* Functions */ extern void acr_set_byte_order(Acr_File *afp, Acr_byte_order byte_order); extern Acr_byte_order acr_get_byte_order(Acr_File *afp); extern Acr_byte_order acr_get_machine_byte_order(void); extern int acr_need_invert(Acr_byte_order byte_order); extern void acr_set_vr_encoding(Acr_File *afp, Acr_VR_encoding_type vr_encoding); extern Acr_VR_encoding_type acr_get_vr_encoding(Acr_File *afp); extern void acr_set_ignore_errors(Acr_File *afp, int ignore_nonfatal_protocol_errors); extern int acr_ignore_protocol_errors(Acr_File *afp); extern void acr_reverse_byte_order(long nvals, size_t value_size, void *input_values, void *output_values); extern void acr_get_short(Acr_byte_order byte_order, long nvals, void *input_value, Acr_Short *mach_value); extern void acr_get_long(Acr_byte_order byte_order, long nvals, void *input_value, Acr_Long *mach_value); extern void acr_get_float(Acr_byte_order byte_order, long nvals, void *input_value, Acr_Float *mach_value); extern void acr_get_double(Acr_byte_order byte_order, long nvals, void *input_value, Acr_Double *mach_value); extern void acr_put_short(Acr_byte_order byte_order, long nvals, Acr_Short *mach_value, void *output_value); extern void acr_put_long(Acr_byte_order byte_order, long nvals, Acr_Long *mach_value, void *output_value); extern void acr_put_float(Acr_byte_order byte_order, long nvals, Acr_Float *mach_value, void *output_value); extern void acr_put_double(Acr_byte_order byte_order, long nvals, Acr_Double *mach_value, void *output_value); extern Acr_Status acr_skip_input_data(Acr_File *afp, long nbytes_to_skip); extern Acr_Status acr_read_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_read, long *nbytes_read); extern Acr_Status acr_unget_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_unget); extern Acr_Status acr_write_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_write, long *nbytes_written); extern Acr_Status acr_test_byte_order(Acr_File *afp); extern void acr_copy_file_encoding(Acr_File *afp1, Acr_File *afp2); extern int acr_get_element_header_size(char vr_name[2], Acr_VR_encoding_type vr_encoding); extern Acr_Status acr_peek_at_next_element_id(Acr_File *afp, int *group_id, int *element_id); extern Acr_Status acr_read_one_element(Acr_File *afp, int *group_id, int *element_id, char vr_name[], long *data_length, char **data_pointer); extern Acr_Status acr_write_one_element(Acr_File *afp, int group_id, int element_id, char vr_name[], long data_length, char *data_pointer); extern char *acr_status_string(Acr_Status status); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/value_repr.h0000644000175000000620000000532212574624760023600 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : value_repr.h @DESCRIPTION: Header file for acr-nema VR code @METHOD : @GLOBALS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : * $Log: value_repr.h,v $ * Revision 6.4 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.3 2005/03/04 00:08:08 bert * Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * * Revision 6.2 2000/08/16 15:53:46 neelin * Added VR type UN (unknown) which has a length field similar to OB. * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 1.2 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 1.1 1997/02/11 16:23:43 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ typedef enum { ACR_VR_UNKNOWN, ACR_VR_AE, ACR_VR_AS, ACR_VR_AT, ACR_VR_CS, ACR_VR_DA, ACR_VR_DS, ACR_VR_DT, ACR_VR_FL, ACR_VR_FD, ACR_VR_IS, ACR_VR_LO, ACR_VR_LT, ACR_VR_OB, ACR_VR_OW, ACR_VR_PN, ACR_VR_SH, ACR_VR_SL, ACR_VR_SQ, ACR_VR_SS, ACR_VR_ST, ACR_VR_TM, ACR_VR_UI, ACR_VR_UL, ACR_VR_US, ACR_VR_UN, ACR_VR_UT, ACR_VR_NUM_TYPES } Acr_VR_Type; /* Function prototypes */ extern char *acr_get_vr_name(Acr_VR_Type vr_code); extern int acr_test_vr_name(char *vr_name); extern Acr_VR_Type acr_lookup_vr_name(char *vr_name); extern double acr_get_numeric_vr(Acr_VR_Type vr_code, Acr_byte_order byte_order, char *data, long data_length); extern char *acr_get_string_vr(Acr_VR_Type vr_code, Acr_byte_order byte_order, char *data, long data_length); minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema/element.h0000644000175000000620000002243212574624760023066 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : element.h @DESCRIPTION: Header file for acr-nema element code @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: element.h,v $ * Revision 6.5 2008-08-12 05:00:23 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.4 2006/04/09 15:28:40 bert * Add functions acr_get_element_double_array and acr_create_element_double * * Revision 6.3 2005/03/04 00:08:08 bert * Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * * Revision 6.2 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.1 1999/10/29 17:51:52 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.2 1997/09/02 22:51:52 neelin * Fixed padding of of UI strings and got rid of acr_string_pad_char * function. * * Revision 5.1 1997/08/22 15:08:34 neelin * Added routine acr_string_pad_char to set character used for padding * strings to an even number of bytes. * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:11 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:35:51 neelin * Pre-release * * Revision 1.6 94/04/07 10:05:11 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.5 93/11/26 18:47:48 neelin * Added element copy routine. * * Revision 1.4 93/11/24 11:26:35 neelin * Changed short to unsigned short. * * Revision 1.3 93/11/23 11:36:21 neelin * Changed GLOBAL_ELEMENT macro to allow definition or declaration of * global variables. * * Revision 1.2 93/11/22 13:12:21 neelin * Added Acr_Element_Id code. * * Revision 1.1 93/11/19 12:50:24 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. ---------------------------------------------------------------------------- */ #ifndef _ACR_ELEMENT_H_ #define _ACR_ELEMENT_H_ /* Element type */ typedef struct Acr_Element { int group_id; int element_id; long data_length; char *data_pointer; struct Acr_Element *next; short vr_code; unsigned int is_sequence:1; unsigned int has_variable_length:1; unsigned int has_little_endian_order:1; unsigned int uses_explicit_vr:1; } *Acr_Element; /* Structure for specifying element id's */ typedef struct Acr_Element_Id { int group_id; int element_id; Acr_VR_Type vr_code; } *Acr_Element_Id; /* Macros for creating element id's (class should be nothing or static) */ #ifndef lint #define DEFINE_ELEMENT(class, name, group, element, vr) \ static struct Acr_Element_Id name##_struct = \ {group, element, ACR_VR_##vr}; \ class Acr_Element_Id name = &name##_struct #else #define DEFINE_ELEMENT(class, name, group, element, vr) \ class Acr_Element_Id name = (void *) 0 #endif /* Macro for creating global elements. If GLOBAL_ELEMENT_DEFINITION is defined then we define the variables, otherwise we just declare them */ #ifdef GLOBAL_ELEMENT_DEFINITION # define GLOBAL_ELEMENT(name, group, element, vr) \ DEFINE_ELEMENT(,name, group, element, vr) #else # define GLOBAL_ELEMENT(name, group, element, vr) \ extern Acr_Element_Id name #endif /* Macro for creating global elements for the ACR-NEMA library. If ACR_LIBRARY_GLOBAL_ELEMENT_DEFINITION is defined then we define the variables, otherwise we just declare them */ #ifdef ACR_LIBRARY_GLOBAL_ELEMENT_DEFINITION # define ACRLIB_GLOBAL_ELEMENT(name, group, element, vr) \ DEFINE_ELEMENT(,name, group, element, vr) #else # define ACRLIB_GLOBAL_ELEMENT(name, group, element, vr) \ extern Acr_Element_Id name #endif /* Global element definition for items */ ACRLIB_GLOBAL_ELEMENT(ACR_Sequence_Item, ACR_ITEM_GROUP, ACR_ITEM_TAG, UNKNOWN); /* Functions */ extern Acr_Element acr_create_element(int group_id, int element_id, Acr_VR_Type vr_code, long data_length, char *data_pointer); extern void acr_delete_element(Acr_Element element); extern void acr_delete_element_list(Acr_Element element_list); extern Acr_Element acr_element_list_add(Acr_Element element_list, Acr_Element element); extern void acr_set_element_id(Acr_Element element, int group_id, int element_id); extern void acr_set_element_vr(Acr_Element element, Acr_VR_Type vr_code); extern void acr_set_element_vr_encoding(Acr_Element element, Acr_VR_encoding_type vr_encoding); extern void acr_set_element_byte_order(Acr_Element element, Acr_byte_order byte_order); extern void acr_set_element_variable_length(Acr_Element element, int has_variable_length); extern void acr_set_element_data(Acr_Element element, long data_length, char *data_pointer); extern void acr_set_element_next(Acr_Element element, Acr_Element next); extern int acr_get_element_group(Acr_Element element); extern int acr_get_element_element(Acr_Element element); extern Acr_VR_Type acr_get_element_vr(Acr_Element element); extern Acr_VR_encoding_type acr_get_element_vr_encoding(Acr_Element element); extern int acr_element_is_sequence(Acr_Element element); extern Acr_byte_order acr_get_element_byte_order(Acr_Element element); extern int acr_element_has_variable_length(Acr_Element element); extern long acr_get_element_length(Acr_Element element); extern char *acr_get_element_data(Acr_Element element); extern long acr_get_element_total_length(Acr_Element element, Acr_VR_encoding_type vr_encoding); extern Acr_Element acr_get_element_next(Acr_Element element); extern Acr_Element acr_copy_element(Acr_Element element); extern Acr_Status acr_input_element(Acr_File *afp, Acr_Element *element); extern Acr_Status acr_output_element(Acr_File *afp, Acr_Element element); extern void acr_convert_element_byte_order(Acr_Element element, Acr_byte_order byte_order); extern int acr_match_element_id(Acr_Element_Id elid, Acr_Element element); extern Acr_Element acr_find_element_id(Acr_Element element_list, Acr_Element_Id elid); extern void *acr_memdup(size_t value_size, void *value); extern Acr_Element acr_create_element_short(Acr_Element_Id elid, Acr_Short value); extern Acr_Element acr_create_element_long(Acr_Element_Id elid, Acr_Long value); extern Acr_Element acr_create_element_double(Acr_Element_Id elid, int nvalues, Acr_Double *values); extern Acr_Element acr_create_element_numeric(Acr_Element_Id elid, double value); extern Acr_Element acr_create_element_string(Acr_Element_Id elid, Acr_String value); extern Acr_Element acr_create_element_sequence(Acr_Element_Id elid, Acr_Element itemlist); extern Acr_Short acr_get_element_short(Acr_Element element); extern Acr_Long acr_get_element_long(Acr_Element element); extern double acr_get_element_numeric(Acr_Element element); extern Acr_String acr_get_element_string(Acr_Element element); extern long acr_get_element_short_array(Acr_Element element, long max_values, Acr_Short values[]); extern long acr_get_element_double_array(Acr_Element element, long max_values, Acr_Double values[]); extern int *acr_element_numeric_array_separator(int character); extern int acr_get_element_numeric_array(Acr_Element element, int max_values, double values[]); extern void acr_dump_element_list(FILE *file_pointer, Acr_Element element_list); #endif /* _ACR_ELEMENT_H_ */ minc-tools-2.3.00+dfsg/conversion/Acr_nema/ChangeLog0000644000175000000620000000607512574624760021276 0ustar stevestaff2008-01-13 Steve M. Robbins * file_io.c: Include "config.h" for access to symbols HAVE_MKSTEMP and HAVE_TMPNAM. Ensure that only one or the other is used and add #error directives for the case that neither is defined. Conditionalize definitions of Input_trace_file and Output_trace_file to avoid compiler warning. 2006-09-01 Jonathan Harlap * Fix bug in element.c: modified maybe_print_as_string to only skip element 0x7fe0,0x0010 rather than all elements with element number 0x0010 2005-05-09 Robert Vincent * Add and update dicom.txt, a list of DICOM field names. * Fix bug in group.c so that acr_find_short, acr_find_int, acr_find_long, and acr_find_double will return the default value if the element is present but contains no data. 2005-03-11 Robert Vincent * dump_acr_nema.c: add '-t' option to parse files which contain lists of field names with corresponding group and element id's. * group.c, element.c, acr_nema/group.h - define _acr_name_proc as a global that defines the naming procedure to use to determine the symbolic name of a group/element pair. 2005-03-04 Robert VINCENT * ChangeLog: Latest changes * group.c: Change several functions to return Acr_Status instead of void; lose public and private; Make insert_element() check the return value of acr_get_element_total_length() * element.c: Avoid memory leak by freeing unused elements in a sequence. Fix order of initialization in acr_create_element() to set variable length property correctly. Don't change VR encoding when parsing a sequence, rely on the new handling of 0xfffe group items by the acr_io functions * acr_io.c: Lose public and private keywords; change acr_read_one_element to assume implicit VR format for special sequence delimiters (group 0xfffe) * file_io.c, value_repr.c: Lose private and public * acr_nema/acr_io.h, acr_nema/element.h, acr_nema/file_io.h, acr_nema/group.h, acr_nema/value_repr.h: Cleanup headers, mostly by getting rid of the infernal 'public' and using extern instead * Makefile.am: Move most of the headers into an acr_nema subdirectory to avoid naming conflicts 2005-03-03 Bert Vincent * Modify sequence handling to better conform to the DICOM specification. This means handling group 0xfffe items as magically conforming to implicit VR format. See DICOM Part 5, section 7.5 for more details. * Fix bug that prevented any data element from ever being tagged as "variable length". This bug prevented us from properly parsing files with sequences with unspecified lengths. * Modify acr_get_element_total_length() to return zero if the vr_name is NULL. This avoids a core dump when erroneously parsing non-DICOM files. * Also got rid of small memory leak in sequence reading. * Other random tweaks and cleanup. 2005-02-16 Bert Vincent * Autoconfiscation * Move headers into acr_nema subdirectory to avoid naming conflicts * Get rid of mktemp() warnings by using mkstemp(). minc-tools-2.3.00+dfsg/conversion/Acr_nema/configure.ac0000644000175000000620000000050412574624760022001 0ustar stevestaffAC_INIT AC_CONFIG_SRCDIR([./acr_nema.h]) AC_CONFIG_AUX_DIR(ac_config_aux) AM_INIT_AUTOMAKE(acr_nema, 1.0) AC_CONFIG_HEADERS([config.h]) AC_REVISION($Revision: 6.1 $) AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AM_PROG_CC_C_O AC_PROG_RANLIB AC_DISABLE_SHARED AC_PROG_LIBTOOL AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT minc-tools-2.3.00+dfsg/conversion/Acr_nema/copy_acr_nema.c0000644000175000000620000001454212574624760022465 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : cat_acr_nema.c @DESCRIPTION: Program to read in an ACR-NEMA file and then dump it out. This can fix a buggy file. @METHOD : @GLOBALS : @CREATED : November 9, 2000 (Peter Neelin) @MODIFIED : * $Log: copy_acr_nema.c,v $ * Revision 6.6 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.5 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.4 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.3 2000/11/09 17:00:03 neelin * Minor lint fix. * * Revision 6.2 2000/11/09 16:57:23 neelin * Fixed bug in writing to stdout. * * Revision 6.1 2000/11/09 15:56:49 neelin * Added new program copy_acr_nema to read in acr-nema file, ignoring errors, * and then write it out again. This will fix any errors in group lengths, * etc. * @COPYRIGHT : Copyright 2000 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 #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1) int main(int argc, char *argv[]) { char *pname; char *infile = NULL; char *outfile = NULL; int ignore_errors = TRUE; Acr_byte_order byte_order = ACR_UNKNOWN_ENDIAN; Acr_VR_encoding_type vr_encoding = UNKNOWN_VR_ENCODING; FILE *fp; Acr_File *afp; Acr_Group group_list, cur_group; Acr_Status status, tmpstatus, outstatus; char *status_string; int iarg, argcounter; char *arg; char *usage = "Usage: %s [-h] [-b] [-l] [-e] \n"; /* Check arguments */ pname = argv[0]; argcounter = 0; for (iarg=1; iarg < argc; iarg++) { arg = argv[iarg]; if ((arg[0] == '-') && (arg[1] != '\0')) { if (arg[2] != '\0') { (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } switch (arg[1]) { case 'h': (void) fprintf(stderr, "Options:\n"); (void) fprintf(stderr, " -h:\tPrint this message\n"); (void) fprintf(stderr, " -b:\tAssume big-endian data\n"); (void) fprintf(stderr, " -l:\tAssume little-endian data\n"); (void) fprintf(stderr, " -e:\tAssume explicit VR encoding\n\n"); (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); break; case 'l': byte_order = ACR_LITTLE_ENDIAN; break; case 'b': byte_order = ACR_BIG_ENDIAN; break; case 'e': vr_encoding = ACR_EXPLICIT_VR; break; default: (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } } else { switch (argcounter) { case 0: infile = arg; break; case 1: outfile = arg; break; default: (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); } argcounter++; } } /* Open input file */ if ((infile != NULL) && (strcmp(infile, "-") != 0)) { fp = fopen(infile, "r"); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening file %s\n", pname, infile); exit(EXIT_FAILURE); } } else { fp = stdin; } /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); acr_set_ignore_errors(afp, ignore_errors); (void) acr_test_dicom_file(afp); if (byte_order != ACR_UNKNOWN_ENDIAN) { acr_set_byte_order(afp, byte_order); } byte_order = acr_get_byte_order(afp); if (vr_encoding != UNKNOWN_VR_ENCODING) { acr_set_vr_encoding(afp, vr_encoding); } vr_encoding = acr_get_vr_encoding(afp); /* Read in group list */ status = acr_input_group_list(afp, &group_list, 0); /* Free the afp */ acr_file_free(afp); /* Open the output file */ if ((outfile != NULL) && (strcmp(outfile, "-") != 0)) { fp = fopen(outfile, "w"); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening output file %s\n", pname, outfile); exit(EXIT_FAILURE); } } else { fp = stdout; } /* Connect to output stream */ afp=acr_file_initialize(fp, 0, acr_stdio_write); acr_set_ignore_errors(afp, ignore_errors); acr_set_byte_order(afp, byte_order); acr_set_vr_encoding(afp, vr_encoding); /* Write out the groups */ outstatus = ACR_OK; for (cur_group=group_list; cur_group != NULL; cur_group = acr_get_group_next(cur_group)) { /* Read in the next group */ tmpstatus = acr_output_group(afp, cur_group); if (tmpstatus != ACR_OK) outstatus = tmpstatus; } /* Free the afp */ acr_file_free(afp); /* Print status information */ if ((status != ACR_END_OF_INPUT) && (status != ACR_OK)) { status_string = acr_status_string(status); (void) fprintf(stderr, "Finished with input status '%s'\n", status_string); } if (outstatus != ACR_OK) { status_string = acr_status_string(outstatus); (void) fprintf(stderr, "Finished with output status '%s'\n", status_string); } exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_io.c0000644000175000000620000012247012574624760021122 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : acr_io.c @DESCRIPTION: Routines for doing basic acr_nema operations (reading and writing an element). @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: acr_io.c,v $ * Revision 6.8 2008-08-12 05:00:22 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.7 2005/03/04 00:15:14 bert * Lose and static keywords; change acr_read_one_element to assume implicit VR format for special sequence delimiters (group 0xfffe) * * Revision 6.6 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.5 2000/08/16 15:53:46 neelin * Added VR type UN (unknown) which has a length field similar to OB. * * Revision 6.4 2000/05/01 17:54:02 neelin * Improved testing of input stream to figure out byte order for both * implicit and expicit VR. * * Revision 6.3 2000/04/28 15:03:10 neelin * Added support for ignoring non-fatal protocol errors (cases where redundant * information is inconsistent). In particular, it is possible to ignore * differences between the group length element and the true group length. * * Revision 6.2 1999/10/29 17:51:49 neelin * Fixed Log keyword * * Revision 6.1 1999/10/27 20:13:15 neelin * Generalized acr_test_byte_order to recognize groups without a length element. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/08 21:53:31 neelin * Added status ACR_CONNECTION_TIMEDOUT. * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/10 17:14:38 neelin * Added more status codes and function to return status string. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.1 1995/02/08 21:16:06 neelin * Changes to make irix 5 lint happy. * * Revision 2.0 1994/09/28 10:36:06 neelin * Release of minc version 0.2 * * Revision 1.9 94/09/28 10:35:39 neelin * Pre-release * * Revision 1.8 94/09/23 16:42:35 neelin * Changed acr_nema_io to acr_io and acr_nema_test to acr_test. * * Revision 1.7 94/05/18 08:47:43 neelin * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT. * * Revision 1.6 94/04/07 10:03:40 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.5 94/01/06 13:30:57 neelin * Changed acr_need_invert to a function. * * Revision 1.4 93/11/30 12:18:34 neelin * Handle MALLOC returning NULL because of extremely large data element length. * * Revision 1.3 93/11/25 10:34:34 neelin * Added routine to test byte-ordering of input. * * Revision 1.2 93/11/24 11:24:48 neelin * Changed short to unsigned short. * * Revision 1.1 93/11/19 12:47:35 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 #include #include #include #include /* Define constants */ #if (!defined(TRUE) || !defined(FALSE)) # define TRUE 1 # define FALSE 0 #endif #define ACR_BYTE_ORDER_DEFAULT ACR_LITTLE_ENDIAN #define ACR_VR_ENCODING_DEFAULT ACR_IMPLICIT_VR /* Define types */ typedef struct { Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; int ignore_nonfatal_protocol_errors; } *Data_Info; /* Private functions */ static int test_vr(const char vr_to_test[2], const char *vr_list[]); static int is_sequence_vr(const char vr_to_test[2]); static int is_special_vr(const char vr_to_test[2]); static int is_vr(const char vr_to_test[2]); static Data_Info get_data_info(Acr_File *afp); static void invert_values(Acr_byte_order byte_order, long nvals, size_t value_size, void *input_value, void *mach_value); /* Macros */ #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0])) /* ----------------------------- MNI Header ----------------------------------- @NAME : is_sequence_vr is_special_vr is_vr @INPUT : vr_to_test - Two character array containing value representation @OUTPUT : (none) @RETURNS : TRUE if vr is in appropriate list @DESCRIPTION: These routines test VR against various lists. is_sequence_vr checks for a sequence, is_special_vr checks for a VR with different fields, and is_vr tests for ANY legal VR. @METHOD : @GLOBALS : @CALLS : @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int test_vr(const char vr_to_test[2], const char *vr_list[]) { int found_special, i; found_special = FALSE; for (i=0; vr_list[i] != NULL; i++) { if ((vr_to_test[0] == vr_list[i][0]) && (vr_to_test[1] == vr_list[i][1])) { found_special = TRUE; break; } } return found_special; } static int is_sequence_vr(const char vr_to_test[2]) { static const char *sequence_vrs[] = { "SQ", NULL }; return test_vr(vr_to_test, sequence_vrs); } static int is_special_vr(const char vr_to_test[2]) { static const char *special_vrs[] = { "OB", "OW", "SQ", "UN", "UT", NULL }; return test_vr(vr_to_test, special_vrs); } static int is_vr(const char vr_to_test[2]) { static const char *all_vrs[] = { "AE", "AS", "AT", "CS", "DA", "DS", "DT", "FD", "FL", "IS", "LO", "LT", "OB", "OD", "OF", "OW", "PN", "SH", "SL", "SQ", "SS", "ST", "TM", "UI", "UL", "UN", "US", "UT", NULL }; return test_vr(vr_to_test, all_vrs); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_data_info @INPUT : afp - i/o stream @OUTPUT : (none) @RETURNS : Pointer to data info @DESCRIPTION: Checks that the i/o stream has the appropriate structure as client data and returns a pointer to the structure. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Data_Info get_data_info(Acr_File *afp) { Data_Info data_info; data_info = acr_file_get_client_data(afp); if (data_info == NULL) { data_info = MALLOC(sizeof(*data_info)); data_info->byte_order = ACR_BYTE_ORDER_DEFAULT; data_info->vr_encoding = ACR_VR_ENCODING_DEFAULT; data_info->ignore_nonfatal_protocol_errors = FALSE; acr_file_set_client_data(afp, (void *) data_info); } return data_info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_byte_order @INPUT : afp - i/o stream byte_order - ACR_LITTLE_ENDIAN or ACR_BIG_ENDIAN. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Allows a user to set the byte ordering for an i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_byte_order(Acr_File *afp, Acr_byte_order byte_order) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Set the byte ordering */ data_info->byte_order = byte_order; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_byte_order @INPUT : afp - i/o stream @OUTPUT : (none) @RETURNS : Byte ordering of stream @DESCRIPTION: Allows one to get the byte ordering for an i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_byte_order acr_get_byte_order(Acr_File *afp) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Return the byte ordering */ return data_info->byte_order; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_machine_byte_order @INPUT : (none) @OUTPUT : (none) @RETURNS : Byte ordering for this machine. @DESCRIPTION: Gets the byte ordering for the machine on which the program is running. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_byte_order acr_get_machine_byte_order(void) { int dummy = 1; char *ptr = (char *) &dummy; if ((int) ptr[0] == 1) return ACR_LITTLE_ENDIAN; else if ((int) ptr[sizeof(int)-1] == 1) return ACR_BIG_ENDIAN; else { (void) fprintf(stderr, "Internal error: Cannot figure out machine byte order!\n"); exit(EXIT_FAILURE); return ACR_BIG_ENDIAN; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_need_invert @INPUT : byte_order - byte_order of foreign data @OUTPUT : (none) @RETURNS : TRUE if need to invert shorts and longs @DESCRIPTION: Indicates whether we need to swap bytes for shorts and longs to convert between the given byte ordering and the machine byte ordering. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ int acr_need_invert(Acr_byte_order byte_order) { return (acr_get_machine_byte_order() != byte_order); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_vr_encoding @INPUT : afp - i/o stream vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Allows a user to set the vr encoding type for an i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_vr_encoding(Acr_File *afp, Acr_VR_encoding_type vr_encoding) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Set the VR encoding */ data_info->vr_encoding = vr_encoding; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_vr_encoding @INPUT : afp - i/o stream @OUTPUT : (none) @RETURNS : VR encoding of stream @DESCRIPTION: Allows one to get the vr encoding for an i/o stream @METHOD : @GLOBALS : @CALLS : @CREATED : January 29, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_VR_encoding_type acr_get_vr_encoding(Acr_File *afp) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Return the VR encoding */ return data_info->vr_encoding; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_ignore_errors @INPUT : afp - i/o stream ignore_nonfatal_protocol_errors - if TRUE then non-fatal protocol errors will be ignored @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Allows a user to indicate whether to ignore protocol errors that can be ignored. @METHOD : @GLOBALS : @CALLS : @CREATED : April 28, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_ignore_errors(Acr_File *afp, int ignore_nonfatal_protocol_errors) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Set the flag */ data_info->ignore_nonfatal_protocol_errors = ignore_nonfatal_protocol_errors; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_ignore_protocol_errors @INPUT : afp - i/o stream @OUTPUT : (none) @RETURNS : TRUE if stream is set to ignore nonfatal protocol errors @DESCRIPTION: Allows one to get the ignore errors flag for a stream @METHOD : @GLOBALS : @CALLS : @CREATED : April 28, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_ignore_protocol_errors(Acr_File *afp) { Data_Info data_info; /* Get data info pointer */ data_info = get_data_info(afp); /* Return the VR encoding */ return data_info->ignore_nonfatal_protocol_errors; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_reverse_byte_order @INPUT : nvals - number of values to invert value_size - length of each value input_values - pointer to array of input values @OUTPUT : output_values - pointer to array of inverted values or NULL @RETURNS : (nothing) @DESCRIPTION: Reverses byte-ordering of an array of values. Will reverse an array in place if input_values and output_values point to the same array or if output_values is NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_reverse_byte_order(long nvals, size_t value_size, void *input_values, void *output_values) { long i, jlow, jhigh; char *ptr1, *ptr2, v0, v1; int nbytes; /* Get data pointers and check whether output_values is NULL */ ptr1 = (char *) input_values; ptr2 = (char *) output_values; if (ptr2 == NULL) ptr2 = ptr1; /* Copy values from both ends at the same time and stop in the middle */ nbytes = (value_size+1)/2; for (i=0; i= nbytes_to_skip) { return ACR_OK; } else if (acr_get_io_watchpoint(afp) <= 0) { return ACR_REACHED_WATCHPOINT; } else if (i == 0) { return ACR_END_OF_INPUT; } else { return ACR_ABNORMAL_END_OF_INPUT; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_read_buffer @INPUT : afp nbytes_to_read @OUTPUT : buffer nbytes_read - if NULL, then this value is ignored, otherwise the number of bytes actually read in is returned. @RETURNS : Input status. If an error occurs on the first byte, then ACR_END_OF_INPUT is returned, if an error occurs elsewhere, then ACR_ABNORMAL_END_OF_INPUT is returned, otherwise ACR_OK is returned. @DESCRIPTION: Reads in a buffer of data and optionally returns the number of bytes read @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_read_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_read, long *nbytes_read) { long i; int ch; for (i=0; i < nbytes_to_read; i++) { ch = acr_getc(afp); if (ch == EOF) { break; } buffer[i] = (unsigned char) ch; } /* Save the number of bytes read */ if (nbytes_read != NULL) { *nbytes_read = i; } /* Return the status */ if (i >= nbytes_to_read) { return ACR_OK; } else if (acr_get_io_watchpoint(afp) <= 0) { return ACR_REACHED_WATCHPOINT; } else if (i == 0) { return ACR_END_OF_INPUT; } else { return ACR_ABNORMAL_END_OF_INPUT; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_unget_buffer @INPUT : afp nbytes_to_unget buffer @OUTPUT : (none) @RETURNS : Unget status. @DESCRIPTION: Puts a buffer of data back into the input stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_unget_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_unget) { long i; for (i=nbytes_to_unget-1; i >= 0; i--) { if (acr_ungetc((int) buffer[i], afp) == EOF) { break; } } /* Return the status */ if (i >= 0) { return ACR_IO_ERROR; } else { return ACR_OK; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_write_buffer @INPUT : afp nbytes_to_write buffer @OUTPUT : nbytes_written @RETURNS : Output status. @DESCRIPTION: Writes out a buffer of data and optionally returns the number of bytes written @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_write_buffer(Acr_File *afp, unsigned char buffer[], long nbytes_to_write, long *nbytes_written) { long i; for (i=0; i < nbytes_to_write; i++) { if (acr_putc(buffer[i], afp) == EOF) { break; } } /* Save the number of bytes written */ if (nbytes_written != NULL) { *nbytes_written = i; } /* Return the status */ if (i < nbytes_to_write) { return ACR_ABNORMAL_END_OF_OUTPUT; } else { return ACR_OK; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_test_byte_order @INPUT : afp @OUTPUT : (none) @RETURNS : status. @DESCRIPTION: Tests input for byte ordering and VR encoding to use. Byte ordering is determined by reading the group identifier and checking whether its high byte is set or not. Because groups should always be sorted in ascending order, and because most groups have a zero upper byte, this test should be correct as long as this function is called near the beginning of the file. The VR encoding is tested by reading the two bytes immediately after the group and element numbers. If these bytes form a legal VR, we assume we have explicit VR throughout this group. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Status acr_test_byte_order(Acr_File *afp) { long buflen; unsigned char buffer[3*ACR_SIZEOF_SHORT]; Acr_Status status; Acr_byte_order byte_order; Acr_Short grp_id; char vr[2]; /* Read in group id, element id and length of data */ status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen); if (status != ACR_OK) return status; /* Put the characters back */ status = acr_unget_buffer(afp, buffer, buflen); if (status != ACR_OK) return status; byte_order = ACR_BIG_ENDIAN; acr_set_byte_order(afp, byte_order); acr_get_short(byte_order, 1, &buffer[0], &grp_id); /* If the high byte of the group id is non-zero, the encoding is * probably little-endian. */ if ((grp_id & 0xff00) != 0) { byte_order = ACR_LITTLE_ENDIAN; acr_set_byte_order(afp, byte_order); acr_get_short(byte_order, 1, &buffer[0], &grp_id); } /* See if we have an explicit VR. */ vr[0] = buffer[2*ACR_SIZEOF_SHORT+0]; vr[1] = buffer[2*ACR_SIZEOF_SHORT+1]; if (is_vr(vr)) { acr_set_vr_encoding(afp, ACR_EXPLICIT_VR); } else { acr_set_vr_encoding(afp, ACR_IMPLICIT_VR); } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_copy_file_encoding @INPUT : afp1 - source stream @OUTPUT : afp2 - target stream @RETURNS : (nothing) @DESCRIPTION: Copies the byte ordering and VR encoding from one i/o stream to another. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_copy_file_encoding(Acr_File *afp1, Acr_File *afp2) { acr_set_byte_order(afp2, acr_get_byte_order(afp1)); acr_set_vr_encoding(afp2, acr_get_vr_encoding(afp1)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_header_size @INPUT : vr_name - 2-letter name of Vr vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR @OUTPUT : (none) @RETURNS : length of header @DESCRIPTION: Calculates the length of the element header (excluding data) @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_element_header_size(char vr_name[2], Acr_VR_encoding_type vr_encoding) { int length; length = 2*ACR_SIZEOF_SHORT + ACR_SIZEOF_LONG; if ((vr_encoding == ACR_EXPLICIT_VR) && is_special_vr(vr_name)) { length += ACR_SIZEOF_LONG; } return length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_peek_at_next_element_id @INPUT : afp - Acr_File pointer from which to read @OUTPUT : group_id element_id @RETURNS : VIO_Status @DESCRIPTION: Peeks ahead to get the group and element ids of the next element. The file position is restored. If a read error occurs, then group_id and element_id are set to INT_MIN and the status is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : February 5, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_peek_at_next_element_id(Acr_File *afp, int *group_id, int *element_id) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT]; Acr_Short svalue; Acr_Status status, status2; Acr_byte_order byte_order; /* Set default values */ status = ACR_OK; *group_id = INT_MIN; *element_id = INT_MIN; /* Read in the values */ status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen); /* Put them back */ status2 = acr_unget_buffer(afp, buffer, buflen); if (status == ACR_OK) status = status2; /* Check for input error */ if (status != ACR_OK) return status; /* Get the id's */ byte_order = acr_get_byte_order(afp); acr_get_short(byte_order, 1, &buffer[0], &svalue); *group_id = (int)svalue; acr_get_short(byte_order, 1, &buffer[ACR_SIZEOF_SHORT], &svalue); *element_id = (int)svalue; return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_read_one_element @INPUT : afp - Acr_File pointer from which to read @OUTPUT : group_id - ACR-NEMA group id element_id - ACR-NEMA element id vr_name - 2 character string giving value representation. Two NULs are returned if VR is unknown. data_length - length of data to follow. Value ACR_VARIABLE_LENGTH is returned for undefined length elements in which case the data portion is not read in. data_pointer - pointer to data. Space is allocated by this routine. One additional byte is allocated and set to zero so that the data can be treated as a string. If a sequence is encountered, then NULL is returned. @RETURNS : VIO_Status. @DESCRIPTION: Routine to read in one ACR-NEMA element. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Status acr_read_one_element(Acr_File *afp, int *group_id, int *element_id, char vr_name[], long *data_length, char **data_pointer) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT+ACR_SIZEOF_LONG]; Acr_Short grpid, elid, sval; Acr_Long datalen; size_t size_allocated; int offset; Acr_byte_order byte_order; Acr_Status status; /* Get byte ordering */ byte_order = acr_get_byte_order(afp); /* Read in group id, element id and length of data */ status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen); if (status != ACR_OK) return status; offset = 0; acr_get_short(byte_order, 1, &buffer[offset], &grpid); offset += ACR_SIZEOF_SHORT; *group_id = (int)grpid; acr_get_short(byte_order, 1, &buffer[offset], &elid); offset += ACR_SIZEOF_SHORT; *element_id = (int)elid; /* Look for VR and length of data */ if (grpid == ACR_ITEM_GROUP || acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) { vr_name[0] = '\0'; vr_name[1] = '\0'; acr_get_long(byte_order, 1, &buffer[offset], &datalen); offset += ACR_SIZEOF_LONG; } else { vr_name[0] = buffer[offset++]; vr_name[1] = buffer[offset++]; acr_get_short(byte_order, 1, &buffer[offset], &sval); offset += ACR_SIZEOF_SHORT; datalen = (Acr_Long)sval; } /* Read in length for special VR's */ if (is_special_vr(vr_name)) { status = acr_read_buffer(afp, buffer, (long) ACR_SIZEOF_LONG, NULL); if (status != ACR_OK) return ACR_ABNORMAL_END_OF_INPUT; acr_get_long(byte_order, 1, &buffer[0], &datalen); } /* Check for undefined length */ if (datalen == ACR_UNDEFINED_ELEMENT_LENGTH) { *data_length = ACR_VARIABLE_LENGTH; *data_pointer = NULL; return ACR_OK; } *data_length = (long)datalen; /* Check for sequence VR */ if (is_sequence_vr(vr_name) || grpid == ACR_ITEM_GROUP) { *data_pointer = NULL; return ACR_OK; } /* Allocate space for the data and null-terminate it */ size_allocated = *data_length + 1; *data_pointer = MALLOC(size_allocated); if (*data_pointer == NULL) { *data_length = 0; size_allocated = *data_length + 1; *data_pointer = MALLOC(size_allocated); } (*data_pointer)[*data_length] = '\0'; /* Read in the data */ status = acr_read_buffer(afp, (unsigned char *) *data_pointer, *data_length, NULL); if (status != ACR_OK) { FREE(*data_pointer); return ACR_ABNORMAL_END_OF_INPUT; } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_write_one_element @INPUT : afp - Acr_File pointer from which to read group_id - ACR-NEMA group id element_id - ACR-NEMA element id vr_name - 2 character string giving value representation. It is an error to pass in two NULs if explicit VR is used (ACR_NO_VR_SPECIFIED is returned). data_length - length of data to follow. If set to ACR_VARIABLE_LENGTH, then the data portion is not written out. data_pointer - pointer to data. If NULL, then no data is written. @OUTPUT : (nothing) @RETURNS : VIO_Status. @DESCRIPTION: Routine to write out one ACR-NEMA element. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : January 29, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Status acr_write_one_element(Acr_File *afp, int group_id, int element_id, char vr_name[], long data_length, char *data_pointer) { long buflen; unsigned char buffer[2*ACR_SIZEOF_SHORT+2*ACR_SIZEOF_LONG]; Acr_Short grpid, elid, sval; Acr_Long datalen; int offset; Acr_byte_order byte_order; Acr_Status status; buflen = sizeof(buffer)/sizeof(buffer[0]) - ACR_SIZEOF_LONG; /* Get byte ordering */ byte_order = acr_get_byte_order(afp); /* Get the group id and element id */ offset = 0; grpid = (Acr_Short) group_id; acr_put_short(byte_order, 1, &grpid, &buffer[offset]); offset += ACR_SIZEOF_SHORT; elid = (Acr_Short) element_id; acr_put_short(byte_order, 1, &elid, &buffer[offset]); offset += ACR_SIZEOF_SHORT; /* Check data length */ if ((Acr_Long)data_length == ACR_VARIABLE_LENGTH) datalen = ACR_UNDEFINED_ELEMENT_LENGTH; else datalen = (Acr_Long)data_length; /* Check whether we need VR */ if (acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) { acr_put_long(byte_order, 1, &datalen, &buffer[offset]); offset += ACR_SIZEOF_LONG; } else { if (vr_name[0] == '\0') return ACR_NO_VR_SPECIFIED; buffer[offset++] = vr_name[0]; buffer[offset++] = vr_name[1]; if (!is_special_vr(vr_name)) { sval = (Acr_Short) datalen; acr_put_short(byte_order, 1, &sval, &buffer[offset]); offset += ACR_SIZEOF_SHORT; } else { sval = 0; acr_put_short(byte_order, 1, &sval, &buffer[offset]); offset += ACR_SIZEOF_SHORT; acr_put_long(byte_order, 1, &datalen, &buffer[offset]); offset += ACR_SIZEOF_LONG; buflen += ACR_SIZEOF_LONG; } } /* Write it out */ status = acr_write_buffer(afp, buffer, buflen, NULL); if (status != ACR_OK) return status; if ((data_length == ACR_VARIABLE_LENGTH) || (data_pointer == NULL)) { return ACR_OK; } /* Write out the data */ status = acr_write_buffer(afp, (unsigned char *) data_pointer, data_length, NULL); if (status != ACR_OK) return status; return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_status_string @INPUT : status - status code to look up @OUTPUT : (nothing) @RETURNS : Pointer to string describing status. @DESCRIPTION: Routine to get a string that describes a status value. @METHOD : @GLOBALS : @CALLS : @CREATED : July 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_status_string(Acr_Status status) { char *status_string; switch (status) { case ACR_OK: status_string = "No error"; break; case ACR_END_OF_INPUT: status_string = "End of input"; break; case ACR_PROTOCOL_ERROR: status_string = "Protocol error"; break; case ACR_OTHER_ERROR: status_string = "Other error"; break; case ACR_ABNORMAL_END_OF_INPUT: status_string = "Abnormal end of input"; break; case ACR_HIGH_LEVEL_ERROR: status_string = "High-level error"; break; case ACR_ABNORMAL_END_OF_OUTPUT: status_string = "Abnormal end of output"; break; case ACR_REACHED_WATCHPOINT: status_string = "Reached watchpoint"; break; case ACR_IO_ERROR: status_string = "I/O error"; break; case ACR_NO_VR_SPECIFIED: status_string = "VR not specified on output"; break; case ACR_PDU_UID_TOO_LONG: status_string = "Input PDU UID too long"; break; case ACR_CONNECTION_TIMEDOUT: status_string = "Connection timed out"; break; default: status_string = "Unknown status"; break; } return status_string; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/COPYING0000644000175000000620000000075412574624760020555 0ustar stevestaffCopyright 1993-2005 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. minc-tools-2.3.00+dfsg/conversion/Acr_nema/NEWS0000644000175000000620000000004612574624760020213 0ustar stevestaffNew in version 1.0 * autoconfiscation minc-tools-2.3.00+dfsg/conversion/Acr_nema/AUTHORS0000644000175000000620000000001512574624760020560 0ustar stevestaffPeter Neelin minc-tools-2.3.00+dfsg/conversion/Acr_nema/autogen.sh0000755000175000000620000000055312574624760021520 0ustar stevestaff#! /bin/sh cat < #include #include #include #include #include /* Private functions */ static void delete_element_data(Acr_Element element); static Acr_Element create_element_mem(Acr_Element_Id elid, Acr_VR_Type vr_code, size_t value_size, void *value); /* Macros */ #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0])) /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element @INPUT : group_id element_id vr_code - Internal VR code data_length - if < 0, then data is assumed to be a pointer to a list of elements in the sequence data_pointer - pointer to actual data or to list of elements @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure. If data_length is negative, then the element is a sequence and the data_pointer must point to a list of elements. In this case, the element is assumed to have variable length. If the element is an ACR_VR_SQ, then it is possible to change it to have fixed length by calling acr_set_element_variable_length with FALSE. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Element acr_create_element(int group_id, int element_id, Acr_VR_Type vr_code, long data_length, char *data_pointer) { Acr_Element element; /* Allocate the element. We set the data pointer to NULL so that acr_set_element_data does not try to free an unitialized pointer */ element = MALLOC(sizeof(*element)); element->data_pointer = NULL; /* Assign fields */ acr_set_element_id(element, group_id, element_id); acr_set_element_vr(element, vr_code); acr_set_element_vr_encoding(element, ACR_EXPLICIT_VR); acr_set_element_byte_order(element, acr_get_machine_byte_order()); acr_set_element_next(element, NULL); acr_set_element_data(element, data_length, data_pointer); acr_set_element_variable_length(element, (data_length < 0)); return element; } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_element_data @INPUT : element @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Deletes element data, but leaves element in an intermediate state with dangling pointers and incorrect lengths. @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void delete_element_data(Acr_Element element) { char *data_pointer; data_pointer = acr_get_element_data(element); if (data_pointer != NULL) { if (acr_element_is_sequence(element)) { acr_delete_element_list((Acr_Element) data_pointer); } else { FREE(data_pointer); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_delete_element @INPUT : element @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Deletes an acr-nema element structure (freeing the data pointer) @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ void acr_delete_element(Acr_Element element) { if (element == NULL) return; delete_element_data(element); FREE(element); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_delete_element_list @INPUT : element_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Delete a list of acr-nema elements @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_delete_element_list(Acr_Element element_list) { Acr_Element next, cur; if (element_list == NULL) return; /* Loop through the list, deleting elements */ next = element_list; while (next != NULL) { cur = next; next = acr_get_element_next(cur); acr_delete_element(cur); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_element_list_add @INPUT : element_list - pointer to element list or NULL element - element to add to list @OUTPUT : (none) @RETURNS : Pointer to new element list @DESCRIPTION: Adds an element to a list of elements. If element_list is NULL, then a new list is created. @METHOD : @GLOBALS : @CALLS : @CREATED : February 11, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_element_list_add(Acr_Element element_list, Acr_Element element) { Acr_Element current; /* Add the element to the list */ if (element_list == NULL) { element_list = element; } else { current = element_list; while (acr_get_element_next(current) != NULL) { current = acr_get_element_next(current); } acr_set_element_next(current, element); } return element_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_id @INPUT : element group_id element_id @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set group and element id of an acr-nema element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_id(Acr_Element element, int group_id, int element_id) { element->group_id = group_id; element->element_id = element_id; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_vr @INPUT : element group_id vr_code @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set VR code for an element @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_vr(Acr_Element element, Acr_VR_Type vr_code) { element->vr_code = (short) vr_code; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_vr_encoding @INPUT : element group_id vr_encoding - ACR_EXPLICIT_ENCODING or ACR_IMPLICIT_ENCODING @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set encoding type for element VR @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_vr_encoding(Acr_Element element, Acr_VR_encoding_type vr_encoding) { element->uses_explicit_vr = (vr_encoding == ACR_EXPLICIT_VR); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_byte_order @INPUT : element group_id byte_order - ACR_LITTLE_ENDIAN or ACR_BIG_ENDIAN @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set byte order for an element @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_byte_order(Acr_Element element, Acr_byte_order byte_order) { element->has_little_endian_order = (byte_order == ACR_LITTLE_ENDIAN); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_variable_length @INPUT : element group_id has_variable_length @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set flag indicating whether element has variable length or not. This can only be set if the element contains sequence data. @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_variable_length(Acr_Element element, int has_variable_length) { if (acr_element_is_sequence(element)) { element->has_variable_length = (has_variable_length != FALSE); } else { element->has_variable_length = FALSE; } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_data @INPUT : element data_length - if < 0, then data is treated as a list of elements data_pointer @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set data length and pointer of an acr-nema element. A negative length means that this element is a sequence and the data pointer points to a list of elements (items). @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ void acr_set_element_data(Acr_Element element, long data_length, char *data_pointer) { Acr_Element item, last, last2; long last_length; /* Free the old data if needed */ if (element->data_pointer != NULL) { delete_element_data(element); } /* Set the pointer and check for a sequence */ element->data_pointer = data_pointer; element->is_sequence = (data_length < 0); /* If we have a sequence, work out its length and set each item to not use explicit VR */ if (element->is_sequence) { data_length = 0; last = last2 = NULL; for (item = (Acr_Element) data_pointer; item != NULL; item=acr_get_element_next(item)) { last_length = acr_get_element_total_length(item, acr_get_element_vr_encoding(item)); data_length += last_length; last2 = last; last = item; } /* Check for a last delimiting item - remove it so that we don't have to worry about switching between defined and undefined lengths - it will always be added later */ if ((last != NULL) && (acr_get_element_group(last) == ACR_ITEM_GROUP) && ((acr_get_element_element(last) == ACR_ITEM_DELIMITER) || (acr_get_element_element(last) == ACR_SEQ_DELIMITER))) { if (last2 != NULL) { acr_set_element_next(last2, NULL); data_length -= last_length; } else { element->data_pointer = NULL; data_length = 0; } acr_delete_element(last); } } /* Save the length */ element->data_length = data_length; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_element_next @INPUT : element next @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Set pointer to next element for an acr-nema element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_element_next(Acr_Element element, Acr_Element next) { element->next = next; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_group @INPUT : element @OUTPUT : (none) @RETURNS : group id @DESCRIPTION: Get group id for element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_element_group(Acr_Element element) { return element->group_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_element @INPUT : element @OUTPUT : (none) @RETURNS : element id @DESCRIPTION: Get element id for element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_element_element(Acr_Element element) { return element->element_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_vr @INPUT : element @OUTPUT : (none) @RETURNS : element VR code @DESCRIPTION: Get VR code for element @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_VR_Type acr_get_element_vr(Acr_Element element) { return (Acr_VR_Type) element->vr_code; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_vr_encoding @INPUT : element @OUTPUT : (none) @RETURNS : ACR_EXPLICIT_ENCODING or ACR_IMPLICIT_ENCODING @DESCRIPTION: Get VR encoding type for element @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_VR_encoding_type acr_get_element_vr_encoding(Acr_Element element) { return (element->uses_explicit_vr ? ACR_EXPLICIT_VR : ACR_IMPLICIT_VR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_element_is_sequence @INPUT : element @OUTPUT : (none) @RETURNS : TRUE if element stores a sequence of items @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_element_is_sequence(Acr_Element element) { return element->is_sequence; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_byte_order @INPUT : element @OUTPUT : (none) @RETURNS : ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_byte_order acr_get_element_byte_order(Acr_Element element) { return (element->has_little_endian_order ? ACR_LITTLE_ENDIAN : ACR_BIG_ENDIAN); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_element_has_variable_length @INPUT : element @OUTPUT : (none) @RETURNS : TRUE if element has variable length representation @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : February 4, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_element_has_variable_length(Acr_Element element) { return element->has_variable_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_length @INPUT : element @OUTPUT : (none) @RETURNS : data_length @DESCRIPTION: Get data length for element. If we have a variable length sequence, then we add in the length of the sequence delimiter. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_element_length(Acr_Element element) { long data_length; data_length = element->data_length; if (acr_element_has_variable_length(element)) { data_length += 2 * ACR_SIZEOF_SHORT + ACR_SIZEOF_LONG; } return data_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_data @INPUT : element @OUTPUT : (none) @RETURNS : data_pointer @DESCRIPTION: Get data pointer for element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_get_element_data(Acr_Element element) { return element->data_pointer; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_total_length @INPUT : element vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR @OUTPUT : (none) @RETURNS : total length for element, or zero if error. @DESCRIPTION: Get total length for element in ACR-NEMA representation depending on VR @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_element_total_length(Acr_Element element, Acr_VR_encoding_type vr_encoding) { /* bert- verify that the VR name is non-null. This protects against * core dumps when reading improperly-formatted files. */ char *vr_name = acr_get_vr_name(acr_get_element_vr(element)); if (vr_name == NULL) { return (0); } return acr_get_element_length(element) + acr_get_element_header_size(vr_name, vr_encoding); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_next @INPUT : element @OUTPUT : (none) @RETURNS : next element @DESCRIPTION: Get next element for element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_get_element_next(Acr_Element element) { return element->next; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_copy_element @INPUT : element @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Copies an acr-nema element structure @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : February 4, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_Element acr_copy_element(Acr_Element element) { Acr_Element copy; long length; unsigned char *data; Acr_Element olditem, newitem, newlist; /* Check for NULL element */ if (element == NULL) return NULL; /* Copy a sequence */ if (acr_element_is_sequence(element)) { length = -1; olditem = (Acr_Element) acr_get_element_data(element); newitem = NULL; newlist = NULL; /* bert- initialize in case list is empty */ while (olditem != NULL) { if (newitem == NULL) { newlist = acr_copy_element(olditem); newitem = newlist; } else { acr_set_element_next(newitem, acr_copy_element(olditem)); newitem = acr_get_element_next(newitem); } olditem = olditem->next; } data = (unsigned char *) newlist; } /* Or copy a value */ else { length = acr_get_element_length(element); data = MALLOC(length+1); if (data == NULL) { length = 0; data = MALLOC(length+1); } data[length] = '\0'; if (length > 0) { (void) memcpy(data, acr_get_element_data(element), length); } } /* Create the new element */ copy = acr_create_element(acr_get_element_group(element), acr_get_element_element(element), acr_get_element_vr(element), length, (void *) data); acr_set_element_vr_encoding(copy, acr_get_element_vr_encoding(element)); acr_set_element_byte_order(copy, acr_get_element_byte_order(element)); acr_set_element_variable_length(copy, acr_element_has_variable_length(element)); return copy; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_element @INPUT : afp - acr file pointer @OUTPUT : element @RETURNS : status @DESCRIPTION: Read in an acr-nema element @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_input_element(Acr_File *afp, Acr_Element *element) { int group_id, element_id, item_gid, item_elid; long data_length; char vr_name[2]; char *data_pointer; Acr_Status status; int is_sequence, more_to_read, found_delimiter, has_variable_length; Acr_Element item, itemlist, previtem; Acr_VR_Type vr_code; Acr_VR_encoding_type vr_encoding; /* Set element in case of error */ *element = NULL; vr_encoding = acr_get_vr_encoding(afp); /* Read in the value */ status = acr_read_one_element(afp, &group_id, &element_id, vr_name, &data_length, &data_pointer); if (status != ACR_OK) { return status; } /* Get VR code */ vr_code = acr_lookup_vr_name(vr_name); /* Check for sequence */ is_sequence = (data_length < 0); if ((data_length > 0) && (data_pointer == NULL)) { is_sequence = TRUE; } has_variable_length = (data_length < 0); /* If we have a sequence, read in all the items and store them as a list of elements. */ if (is_sequence) { more_to_read = TRUE; itemlist = NULL; while (more_to_read) { /* Read in an item */ status = acr_input_element(afp, &item); if (item == NULL) break; /* If we know the length of the whole sequence, check it */ if ((status == ACR_OK) && (data_length > 0)) { data_length -= acr_get_element_total_length(item, vr_encoding); if (data_length < 0) status = ACR_PROTOCOL_ERROR; } /* Look for delimiter */ item_gid = acr_get_element_group(item); item_elid = acr_get_element_element(item); found_delimiter = ((item_gid == ACR_ITEM_GROUP) && ((item_elid == ACR_ITEM_DELIMITER) || (item_elid == ACR_SEQ_DELIMITER))); /* Add the item to the list if it is not a delimiter */ if (!found_delimiter) { if (itemlist == NULL) { itemlist = item; } else { acr_set_element_next(previtem, item); } previtem = item; } else { free(item); /* Avoid leaking memory */ } /* Check for end of items */ if ((data_length == 0) || found_delimiter || (status != ACR_OK)) { more_to_read = FALSE; } } /* End of loop over items */ /* Save the item list as the data */ data_pointer = (char *) itemlist; } /* Create the element */ *element = acr_create_element(group_id, element_id, vr_code, (is_sequence ? -1 : data_length), data_pointer); acr_set_element_vr_encoding(*element, acr_get_vr_encoding(afp)); acr_set_element_byte_order(*element, acr_get_byte_order(afp)); if (is_sequence && !has_variable_length) { acr_set_element_variable_length(*element, FALSE); } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_output_element @INPUT : afp - acr file pointer element @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Write out an acr-nema element. The byte ordering of the element data is changed to match that of the output stream @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_output_element(Acr_File *afp, Acr_Element element) { char *vr_name; Acr_VR_Type vr_code; Acr_Status status; long data_length; int is_sequence, has_variable_length; char *data_pointer; int item_id; Acr_Element item; Acr_VR_encoding_type old_vr_encoding; /* Get info from element */ vr_code = acr_get_element_vr(element); vr_name = acr_get_vr_name(vr_code); is_sequence = acr_element_is_sequence(element); has_variable_length = acr_element_has_variable_length(element); if (is_sequence && has_variable_length) { data_length = ACR_VARIABLE_LENGTH; } else { data_length = acr_get_element_length(element); } /* Get the data. */ if (is_sequence) { data_pointer = NULL; } else { data_pointer = acr_get_element_data(element); } /* Convert the element byte order */ acr_convert_element_byte_order(element, acr_get_byte_order(afp)); /* Write out the element */ status = acr_write_one_element(afp, acr_get_element_group(element), acr_get_element_element(element), vr_name, data_length, data_pointer); if (status != ACR_OK) return status; /* Write out items if we have a sequence */ if (is_sequence) { /* Set the encoding to implicit for items */ old_vr_encoding = acr_get_vr_encoding(afp); acr_set_vr_encoding(afp, ACR_IMPLICIT_VR); /* Loop over items */ item = (Acr_Element) acr_get_element_data(element); while (item != NULL) { status = acr_output_element(afp, item); if (status != ACR_OK) return status; item = acr_get_element_next(item); } /* Write out the delimiter if needed */ if (has_variable_length) { if (acr_get_element_group(element) == ACR_ITEM_GROUP) { item_id = ACR_ITEM_DELIMITER; } else { item_id = ACR_SEQ_DELIMITER; } status = acr_write_one_element(afp, ACR_ITEM_GROUP, item_id, ACR_VR_UNKNOWN, 0L, NULL); if (status != ACR_OK) return status; } /* Restore the VR encoding */ acr_set_vr_encoding(afp, old_vr_encoding); } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_convert_element_byte_order @INPUT : element byte_order - ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Converts the data within an element to a specified byte_order. @METHOD : @GLOBALS : @CALLS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_convert_element_byte_order(Acr_Element element, Acr_byte_order byte_order) { Acr_byte_order element_byte_order; long nvalues; size_t value_size; /* Get the element and stream byte orders and change the element byte order to match that of the stream */ element_byte_order = acr_get_element_byte_order(element); acr_set_element_byte_order(element, byte_order); /* Change byte order of data in place */ if (!acr_element_is_sequence(element)) { /* Look for types that might need byte swapping */ switch (acr_get_element_vr(element)) { case ACR_VR_SS: case ACR_VR_US: case ACR_VR_OW: value_size = ACR_SIZEOF_SHORT; break; case ACR_VR_SL: case ACR_VR_UL: value_size = ACR_SIZEOF_LONG; break; case ACR_VR_FL: value_size = ACR_SIZEOF_FLOAT; break; case ACR_VR_FD: value_size = ACR_SIZEOF_DOUBLE; break; default: value_size = 1; break; } /* Reverse byte order */ if (value_size > 1) { if (element_byte_order != byte_order) { nvalues = acr_get_element_length(element) / value_size; acr_reverse_byte_order(nvalues, value_size, acr_get_element_data(element), NULL); } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_match_element_id @INPUT : elid - element id to check element - element to check @OUTPUT : (none) @RETURNS : TRUE if the ids match, FALSE otherwise @DESCRIPTION: Compares the group and element id of an element id structure to that of an element. Returns TRUE if they are the same. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_match_element_id(Acr_Element_Id elid, Acr_Element element) { return ((elid->group_id == acr_get_element_group(element)) && (elid->element_id == acr_get_element_element(element))); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_find_element_id @INPUT : elid - element id to check element_list - element list to search @OUTPUT : (none) @RETURNS : Pointer to element found or NULL @DESCRIPTION: Searches an element list for an element id. As a side effect, the VR type of the element is set if it is previously unknown and it is defined in the element id structure. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_find_element_id(Acr_Element element_list, Acr_Element_Id elid) { Acr_Element element; /* Look for the element */ element = element_list; while ((element != NULL) && !acr_match_element_id(elid, element)) { element = acr_get_element_next(element); } /* Set the VR type if it is unknown */ if ((element != NULL) && (acr_get_element_vr(element) == ACR_VR_UNKNOWN) && (elid->vr_code != ACR_VR_UNKNOWN)) { acr_set_element_vr(element, elid->vr_code); } return element; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_memdup @INPUT : value_size - number of bytes to copy value - value to copy @OUTPUT : (none) @RETURNS : Pointer to copy of data @DESCRIPTION: Allocates memory and makes a copy of some memory @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *acr_memdup(size_t value_size, void *value) { char *copy, *original; size_t i; original = (char *) value; copy = (char *) MALLOC(value_size); for (i=0; igroup_id, elid->element_id, vr_code, value_size, acr_memdup(value_size, value)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_short @INPUT : elid value @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing one short. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_short(Acr_Element_Id elid, Acr_Short value) { return create_element_mem(elid, ACR_VR_US, sizeof(value), (void *) &value); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_long @INPUT : elid value @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing one long. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_long(Acr_Element_Id elid, Acr_Long value) { return create_element_mem(elid, ACR_VR_UL, sizeof(value), (void *) &value); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_double @INPUT : elid nvalues values @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing an array of doubles. @METHOD : @GLOBALS : @CALLS : @CREATED : April 8, 2006 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_double(Acr_Element_Id elid, int nvalues, Acr_Double *values) { return create_element_mem(elid, ACR_VR_FD, (ACR_SIZEOF_DOUBLE * nvalues), values); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_numeric @INPUT : elid value @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing one ascii numeric. Note that the VR type is taken from the elid structure unless it is not specified there (ACR_VR_UNKNOWN). @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_numeric(Acr_Element_Id elid, double value) { char string[256]; Acr_Element element; if (elid->vr_code == ACR_VR_FD) { element = create_element_mem(elid, ACR_VR_FD, sizeof(value), (void *) &value); } else { (void) sprintf(string, "%.15g", value); element = acr_create_element_string(elid, string); if (elid->vr_code == ACR_VR_UNKNOWN) acr_set_element_vr(element, ACR_VR_DS); } return element; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_string @INPUT : elid value @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing an ascii string. Note that the string is duplicated for the element structure. Note also that the VR type is taken from the elid structure unless it is not specified there (ACR_VR_UNKNOWN). @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_string(Acr_Element_Id elid, Acr_String value) { long data_length; long alloc_length; char *data; int pad; Acr_VR_Type vr_code; /* Get the appropriate vr code */ vr_code = ((elid->vr_code == ACR_VR_UNKNOWN) ? ACR_VR_ST : elid->vr_code); /* Get string length and check for an odd length */ data_length = strlen(value); if ((data_length % 2) == 0) pad = FALSE; else { pad = TRUE; data_length++; } /* Allocate the string and copy it */ alloc_length = data_length + 1; data = (char *) MALLOC(alloc_length); (void) strcpy(data, value); /* Pad the end with a blank or NUL if needed */ if (pad) { data[data_length - 1] = ((vr_code == ACR_VR_UI) ? '\0' : ' '); data[data_length] = '\0'; } /* Create the element and return it */ return acr_create_element(elid->group_id, elid->element_id, vr_code, data_length, data); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_element_sequence @INPUT : elid value @OUTPUT : (none) @RETURNS : Pointer to element structure @DESCRIPTION: Creates an acr-nema element structure containing a sequence of items. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Element acr_create_element_sequence(Acr_Element_Id elid, Acr_Element itemlist) { return acr_create_element(elid->group_id, elid->element_id, ACR_VR_SQ, -1L, (char *) itemlist); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_short @INPUT : element @OUTPUT : (none) @RETURNS : Value from element structure @DESCRIPTION: Gets a single short from an element structure. Returns zero if an error occurs. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Short acr_get_element_short(Acr_Element element) { Acr_Short value; value = (Acr_Short)acr_get_numeric_vr(acr_get_element_vr(element), acr_get_element_byte_order(element), acr_get_element_data(element), acr_get_element_length(element)); return value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_long @INPUT : element @OUTPUT : (none) @RETURNS : Value from element structure @DESCRIPTION: Gets a single long from an element structure. Returns zero if an error occurs. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Long acr_get_element_long(Acr_Element element) { Acr_Long value; value = (Acr_Long)acr_get_numeric_vr(acr_get_element_vr(element), acr_get_element_byte_order(element), acr_get_element_data(element), acr_get_element_length(element)); return value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_numeric @INPUT : element @OUTPUT : (none) @RETURNS : Value from element structure @DESCRIPTION: Gets a single ascii numeric from an element structure. Returns zero if an error occurs. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ double acr_get_element_numeric(Acr_Element element) { double value; value = acr_get_numeric_vr(acr_get_element_vr(element), acr_get_element_byte_order(element), acr_get_element_data(element), acr_get_element_length(element)); return value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_string @INPUT : element @OUTPUT : (none) @RETURNS : Value from element structure @DESCRIPTION: Gets an string from an element structure. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_String acr_get_element_string(Acr_Element element) { return acr_get_string_vr(acr_get_element_vr(element), acr_get_element_byte_order(element), acr_get_element_data(element), acr_get_element_length(element)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_short_array @INPUT : element max_values - maximum number of values to return @OUTPUT : values - array of values found @RETURNS : Number of values found @DESCRIPTION: Gets an array of shorts from an element structure. If the number of values in the element is greater than max_values, then only max_values values are extracted, but the total number of values in the element is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_element_short_array(Acr_Element element, long max_values, Acr_Short values[]) { long nvalues; /* Check VR of element */ switch (acr_get_element_vr(element)) { case ACR_VR_SS: case ACR_VR_US: case ACR_VR_OW: case ACR_VR_UNKNOWN: break; default: return (long) 0; } /* Get number of values in element */ nvalues = acr_get_element_length(element) / ACR_SIZEOF_SHORT; /* Check the maximum number of values */ if (max_values > nvalues) max_values = nvalues; /* Get the data */ acr_get_short(acr_get_element_byte_order(element), max_values, acr_get_element_data(element), values); /* Return the number of values in the structure */ return nvalues; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_double_array @INPUT : element max_values - maximum number of values to return @OUTPUT : values - array of values found @RETURNS : Number of values found @DESCRIPTION: Gets an array of doubles from an element structure. If the number of values in the element is greater than max_values, then only max_values values are extracted, but the total number of values in the element is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : April 8, 2006 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_element_double_array(Acr_Element element, long max_values, Acr_Double values[]) { long nvalues; /* Check VR of element */ switch (acr_get_element_vr(element)) { case ACR_VR_FD: case ACR_VR_UN: case ACR_VR_UNKNOWN: break; default: return (long) 0; } /* Get number of values in element */ nvalues = acr_get_element_length(element) / ACR_SIZEOF_DOUBLE; /* Check the maximum number of values */ if (max_values > nvalues) max_values = nvalues; /* Get the data */ acr_get_double(acr_get_element_byte_order(element), max_values, acr_get_element_data(element), values); /* Return the number of values in the structure */ return nvalues; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_element_numeric_array_separator @INPUT : character - character to add to list or EOF if not adding anything @OUTPUT : (none) @RETURNS : Pointer to array of separators, ending with an EOF @DESCRIPTION: Adds to the list of separators for a numeric array, or returns a pointer to the array which is terminated with an EOF. @METHOD : @GLOBALS : @CALLS : @CREATED : February 27, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int *acr_element_numeric_array_separator(int character) { static int *separator_list = NULL; static int nseparators = 0; static int default_list[] = {',', '\\', '/'}; int isep; /* Create the default list */ if (separator_list == NULL) { nseparators = SIZEOF_ARRAY(default_list); separator_list = MALLOC((size_t) (nseparators + 1) * sizeof(*separator_list)); for (isep = 0; isep < nseparators; isep++) { separator_list[isep] = default_list[isep]; } separator_list[nseparators] = EOF; } /* Add the character to the list if it is not already there */ if (character != EOF) { for (isep=0; isep < nseparators; isep++) { if (character == separator_list[isep]) break; } if (character != separator_list[isep]) { nseparators++; separator_list = REALLOC(separator_list, (size_t) (nseparators + 1) * sizeof(*separator_list)); separator_list[nseparators - 1] = character; separator_list[nseparators] = EOF; } } /* Return the list */ return separator_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_element_numeric_array @INPUT : element max_values - maximum number of values to return @OUTPUT : values - array of values found @RETURNS : Number of values found @DESCRIPTION: Gets an array of ascii numbers from an element structure. @METHOD : @GLOBALS : @CALLS : @CREATED : November 17, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_element_numeric_array(Acr_Element element, int max_values, double values[]) { char *start, *end, *cur, *prev; int *separator_list; int nvalues, isep; /* Get separator list */ separator_list = acr_element_numeric_array_separator(EOF); /* Set up pointers to end of string and first non-space character */ start = (char *) acr_get_element_data(element); end = start + acr_get_element_length(element); cur = start; while (isspace((int) *cur)) cur++; nvalues = 0; /* Loop through string looking for numbers */ while (cur 0) && (string[string_length-1] == '\0')) { string_length--; } /* Print string if short enough and is printable */ if (element_length > 0 && !(acr_get_element_group(cur_element) == 0x7fe0 && acr_get_element_element(cur_element) == 0x0010)) { copy = malloc(string_length + 1); printable = (string_length > 1); for (i=0; i < string_length; i++) { if (! isprint((int) string[i])) { printable = FALSE; copy[i] = ' '; } else if ((string[i] == '\n') || (string[i] == '\r') || (string[i] == '\f')) copy[i] = ' '; else copy[i] = string[i]; } copy[i] = '\0'; if (printable) { (void) fprintf(file_pointer, " string = \"%s\"", copy); } else if (!done_already) { /* If unknown length print as a series of bytes. */ string = acr_get_element_data(cur_element); fprintf(file_pointer, " byte = "); if (element_length < 16) { for (i = 0; i < element_length; i++) { fprintf(file_pointer, "%#x", (unsigned char)string[i]); if (i != element_length - 1) { fprintf(file_pointer, ", "); } } } else { fprintf(file_pointer, "\n"); for (i = 0; i < element_length; i += 16) { for (j = 0; j < 16; j++) { if (i + j < element_length) { fprintf(file_pointer, "%02x ", (unsigned char)string[i+j]); } else { fprintf(file_pointer, " "); } } fprintf(file_pointer, "| "); for (j = 0; j < 16; j++) { if (i + j < element_length) { int c = (unsigned char)string[i+j]; fprintf(file_pointer, "%c", isprint(c) ? c : '.'); } } fprintf(file_pointer, "\n"); } } } free(copy); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dump_element_list @INPUT : file_pointer - where output should go element_list @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Dump information from an acr-nema element list @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dump_element_list(FILE *file_pointer, Acr_Element element_list) { #define INDENT_AMOUNT 3 Acr_Element cur_element; long element_length; int i; static int current_indent_level = 0; Acr_VR_Type vr_code; /* Check that we have something to print */ if (element_list == NULL) return; /* Increment indent level */ current_indent_level += INDENT_AMOUNT; /* Loop over elements */ cur_element = element_list; while (cur_element != NULL) { /* Indent the line */ for (i=0; i < current_indent_level; i++) { (void) putc((int) ' ', file_pointer); } element_length = acr_get_element_length(cur_element); /* Print the element id */ (void) fprintf(file_pointer, "0x%04x 0x%04x length = %d ", acr_get_element_group(cur_element), acr_get_element_element(cur_element), (int) element_length); if (_acr_name_proc != NULL) { char *name_ptr; name_ptr = (*_acr_name_proc)(acr_get_element_group(cur_element), acr_get_element_element(cur_element)); if (name_ptr != NULL) { fprintf(file_pointer, "(%s)", name_ptr); } } fprintf(file_pointer, ":"); /* Print value if needed */ vr_code = acr_get_element_vr(cur_element); if (acr_element_is_sequence(cur_element)) { (void) fprintf(file_pointer, " VR=%s", acr_get_vr_name(vr_code)); if (acr_get_element_data(cur_element) == NULL) { (void) fprintf(file_pointer, " (empty sequence)"); } else { (void) fprintf(file_pointer, " (sequence)"); } (void) putc((int) '\n', file_pointer); acr_dump_element_list(file_pointer, (Acr_Element) acr_get_element_data(cur_element)); } else if (vr_code != ACR_VR_UNKNOWN) { (void) fprintf(file_pointer, " VR=%s, ", acr_get_vr_name(vr_code)); switch (vr_code) { case ACR_VR_SS: case ACR_VR_US: (void) fprintf(file_pointer, "short = %d (0x%04x)", (int) acr_get_element_short(cur_element), (int) acr_get_element_short(cur_element)); break; case ACR_VR_AT: case ACR_VR_SL: case ACR_VR_UL: (void) fprintf(file_pointer, "long = %d (0x%08x)", (int) acr_get_element_long(cur_element), (int) acr_get_element_long(cur_element)); break; case ACR_VR_OB: case ACR_VR_OW: maybe_print_as_string(file_pointer, cur_element, element_length, 0); break; case ACR_VR_FD: fprintf(file_pointer, "double = "); { double array[1000]; long n = acr_get_element_double_array(cur_element, 1000, array); long i; for (i = 0; i < n; i++) { fprintf(file_pointer, "%f", array[i]); if (i != n - 1) { fprintf(file_pointer, ", "); } } } break; default: (void) fprintf(file_pointer, "value = \"%s\"", acr_get_element_string(cur_element)); break; } (void) putc((int) '\n', file_pointer); } else { int done_already = 0; switch (element_length) { case ACR_SIZEOF_SHORT: (void) fprintf(file_pointer, " short = %d (0x%04x)", (int) acr_get_element_short(cur_element), (int) acr_get_element_short(cur_element)); done_already = 1; break; case ACR_SIZEOF_LONG: (void) fprintf(file_pointer, " long = %d (0x%08x)", (int) acr_get_element_long(cur_element), (int) acr_get_element_long(cur_element)); done_already = 1; break; } maybe_print_as_string(file_pointer, cur_element, element_length, done_already); /* End line */ (void) fprintf(file_pointer, "\n"); } /* if is_sequence ... else */ cur_element = acr_get_element_next(cur_element); } /* Decrement indent level */ current_indent_level -= INDENT_AMOUNT; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/extract_acr_nema.c0000644000175000000620000001617312574624760023167 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : extract_acr_nema.c @DESCRIPTION: Program to extract an element from an acr-nema file. @METHOD : @GLOBALS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : * $Log: extract_acr_nema.c,v $ * Revision 6.7 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.6 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.5 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.4 2000/05/01 17:54:45 neelin * Fixed handling of test for byte order. * * Revision 6.3 2000/05/01 13:59:55 neelin * Added -e option to allow reading data streams with explicit VR. * * Revision 6.2 2000/04/28 15:02:01 neelin * Added more general argument processing (but not with ParseArgv). * Added support for ignoring non-fatal protocol errors. * Added support for user-specified byte-order. * * Revision 6.1 1999/10/29 17:51:52 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:12 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:35:54 neelin * Pre-release * * Revision 1.3 94/03/14 16:13:04 neelin * Changed name in header. * * Revision 1.2 93/11/25 10:36:05 neelin * Added byte-order test and file free. * * Revision 1.1 93/11/24 11:25:23 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 #include #include #include #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1) int main(int argc, char *argv[]) { char *pname; char *filename = NULL; char *grpstr = NULL; char *elemstr = NULL; int ignore_errors = FALSE; Acr_byte_order byte_order = ACR_UNKNOWN_ENDIAN; Acr_VR_encoding_type vr_encoding = UNKNOWN_VR_ENCODING; FILE *fp; Acr_File *afp; Acr_Group group_list; Acr_Element element; long element_length; char *ptr; Acr_Element_Id element_id; int iarg, argcounter; char *arg; char *usage = "Usage: %s [-h] [-i] [-b] [-l] [-e] [] \n"; /* Check arguments */ pname = argv[0]; argcounter = 0; for (iarg=1; iarg < argc; iarg++) { arg = argv[iarg]; if ((arg[0] == '-') && (arg[1] != '\0')) { if (arg[2] != '\0') { (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } switch (arg[1]) { case 'h': (void) fprintf(stderr, "Options:\n"); (void) fprintf(stderr, " -h:\tPrint this message\n"); (void) fprintf(stderr, " -i:\tIgnore protocol errors\n"); (void) fprintf(stderr, " -b:\tAssume big-endian data\n"); (void) fprintf(stderr, " -l:\tAssume little-endian data\n"); (void) fprintf(stderr, " -e:\tAssume explicit VR encoding\n\n"); (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); break; case 'i': ignore_errors = TRUE; break; case 'l': byte_order = ACR_LITTLE_ENDIAN; break; case 'b': byte_order = ACR_BIG_ENDIAN; break; case 'e': vr_encoding = ACR_EXPLICIT_VR; break; default: (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } } else { switch (argcounter) { case 0: grpstr = arg; break; case 1: elemstr = arg; break; case 2: filename = grpstr; grpstr = elemstr; elemstr = arg; break; default: (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); } argcounter++; } } /* Get element id (group and element) */ if ((grpstr == NULL) || (elemstr == NULL)) { (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); } element_id = MALLOC(sizeof(*element_id)); element_id->group_id = strtol(grpstr, &ptr, 0); if (ptr == grpstr) { (void) fprintf(stderr, "%s: Error in group id (%s)\n", pname, grpstr); exit(EXIT_FAILURE); } element_id->element_id = strtol(elemstr, &ptr, 0); if (ptr == elemstr) { (void) fprintf(stderr, "%s: Error in element id (%s)\n", pname, elemstr); exit(EXIT_FAILURE); } /* Open input file */ if ((filename != NULL) && (strcmp(filename,"-") != 0)) { fp = fopen(filename, "r"); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening file %s\n", pname, filename); exit(EXIT_FAILURE); } } else { fp = stdin; } /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); acr_set_ignore_errors(afp, ignore_errors); (void) acr_test_dicom_file(afp); if (byte_order != ACR_UNKNOWN_ENDIAN) { acr_set_byte_order(afp, byte_order); } if (vr_encoding != UNKNOWN_VR_ENCODING) { acr_set_vr_encoding(afp, vr_encoding); } /* Read in group list up to group */ (void) acr_input_group_list(afp, &group_list, element_id->group_id); /* Free the afp */ acr_file_free(afp); /* Look for element */ element = acr_find_group_element(group_list, element_id); /* Print out value of element */ if (element != NULL) { element_length = acr_get_element_length(element); if (element_length > 0) { (void) fwrite(acr_get_element_data(element), sizeof(char), (size_t) element_length, stdout); } } exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/dicom_network.c0000644000175000000620000030706312574624760022535 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_network.c @DESCRIPTION: Routines for doing dicom network communications @METHOD : @GLOBALS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_network.c,v $ * Revision 6.12 2008-08-12 05:00:22 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.11 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.10 2001/03/19 18:30:32 neelin * Added function to set implementation uid and changed name of function * that gets it. * * Revision 6.9 2000/05/17 20:17:47 neelin * Added mechanism to allow testing of input streams for more data through * function acr_file_ismore. * This is used in dicom_client_routines to allow asynchronous transfer * of data, with testing for more input done before sending new messages. * Previous use of select for this was misguided, since select may report that * no data is waiting on the file descriptor while data is store in the file * pointer buffer (or Acr file pointer buffer). * * Revision 6.8 2000/02/03 13:30:30 neelin * Changed initial value of counter for acr_create_uid so that uid does not ever * contain a zero. * * Revision 6.7 1999/10/29 17:51:51 neelin * Fixed Log keyword * * Revision 6.6 1998/11/11 17:47:38 neelin * Fixed up freeing of data pointers on file close. * * Revision 6.5 1998/11/11 17:05:03 neelin * Added pointer for client data to dicom structure. * * Revision 6.4 1998/03/23 20:22:37 neelin * Added includes for new functions. * * Revision 6.3 1998/03/23 20:16:16 neelin * Moved some general-purpose functions from dicom_client_routines and * added one for implementation uid. * * Revision 6.2 1998/03/10 17:05:30 neelin * Fixed handling of PDV control header last fragment bit (it should be * set for both command and data parts of the message). Re-organized code * to use watchpoints differently: put PDU watchpoint on real afp * everywhere and store PDV watchpoint in dicom io structure. * * Revision 6.1 1997/10/20 22:52:46 neelin * Added support for implementation user information in association request. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.3 1997/08/21 13:24:56 neelin * Pre-release * * Revision 4.2 1997/07/10 17:14:38 neelin * Added more status codes and function to return status string. * * Revision 4.1 1997/07/09 17:38:55 neelin * Added function acr_dicom_get_io_data. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 1.2 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 1.1 1997/02/20 16:38:17 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include #include #include /* Constants */ #define ACR_COMMAND_GRPID 0x0 #define PDU_HEADER_LEN (2+ACR_SIZEOF_LONG) #define PDU_ITEM_HEADER_LEN (2+ACR_SIZEOF_SHORT) #define ASSOC_RQ_LEN 74 #define ASSOC_RJ_LEN 10 #define ABORT_RQ_LEN 10 #define DATA_TF_LEN 6 #define MAX_PDU_STRING_LENGTH 1024 #define DICOM_NETWORK_BYTE_ORDER ACR_BIG_ENDIAN /* PDU item types */ #define PDU_ITEM_APPLICATION_CONTEXT 0x10 #define PDU_ITEM_PRESENTATION_CONTEXT 0x20 #define PDU_ITEM_PRES_CONTEXT_REPLY 0x21 #define PDU_ITEM_ABSTRACT_SYNTAX 0x30 #define PDU_ITEM_TRANSFER_SYNTAX 0x40 #define PDU_ITEM_USER_INFORMATION 0x50 #define PDU_ITEM_MAXIMUM_LENGTH 0x51 #define PDU_ITEM_IMPLEMENTATION_CLASS_UID 0x52 #define PDU_ITEM_IMPLEMENTATION_VERSION_NAME 0x55 /* Mask for getting info out of PDV message control header */ #define PDV_COMMAND_PDV_MASK 0x1 #define PDV_LAST_FRAGMENT_MASK 0x2 /* Types to allow 2-level hierarchy of i/o streams */ typedef enum {DICOM_INPUT=0xbead, DICOM_OUTPUT} Dicom_IO_stream_type; typedef struct { Dicom_IO_stream_type stream_type; Acr_File *real_afp; /* Pointer to real input stream */ Acr_File *virtual_afp; /* Pointer to message stream to which this io data is attached */ int presentation_context_id; long pdv_watchpoint; /* Distance from end of current PDV to PDU watchpoint on real input stream. This should be a positive number. */ long maximum_length; /* Maximum PDU length (excluding header) */ int writing_command; /* True if writing command portion */ long data_length; /* Length of data portion of message */ void *client_data; /* Pointer to client data, if any */ } Acr_Dicom_IO; /* Private functions */ static Acr_Status read_pdu_header(Acr_File *afp, int *pdu_type, Acr_Long *pdu_length); static Acr_Status read_assoc_rq(Acr_File *afp, Acr_Group group); static Acr_Status read_assoc_rq_ac(Acr_File *afp, Acr_Group group, int is_request); static Acr_Status read_data_tf(Acr_File *dicom_afp, Acr_Group group); static Acr_Status read_rel_rq(Acr_File *afp, Acr_Group group); static Acr_Status read_abort_rq(Acr_File *afp, Acr_Group group); static Acr_Status read_assoc_ac(Acr_File *afp, Acr_Group group); static Acr_Status read_assoc_rj(Acr_File *afp, Acr_Group group); static Acr_Status read_rel_rp(Acr_File *afp, Acr_Group group); static Acr_Status read_pdu_item(Acr_File *afp, Acr_Element *item); static Acr_Status read_uid_item(Acr_File *afp, Acr_Element_Id elid, Acr_Element *item); static Acr_Status read_long_item(Acr_File *afp, Acr_Element_Id elid, Acr_Element *item); static Acr_Status read_unknown_item(Acr_File *afp, int item_type, Acr_Element *item); static Acr_Status read_pres_context_item(Acr_File *afp, Acr_Element *item); static Acr_Status read_pres_context_reply_item(Acr_File *afp, Acr_Element *item); static Acr_Status read_user_info_item(Acr_File *afp, Acr_Element *item); static char *get_uid_string(char *buffer, int length); static Acr_Element pdu_create_element_uid(Acr_Element_Id elid, char *value, int length); static Acr_Element pdu_create_element_short(Acr_Element_Id elid, void *input_value); static Acr_Status write_assoc_rq(Acr_File *afp, Acr_Group group); static Acr_Status write_assoc_ac(Acr_File *afp, Acr_Group group); static Acr_Status write_assoc_rq_ac(Acr_File *afp, Acr_Group group, int is_request, long *length); static Acr_Status write_assoc_rj(Acr_File *afp, Acr_Group group_list); static Acr_Status write_rel_rq(Acr_File *afp, Acr_Group group_list); static Acr_Status write_rel_rp(Acr_File *afp, Acr_Group group_list); static Acr_Status write_abort_rq(Acr_File *afp, Acr_Group group_list); static Acr_Status write_fixed_length_pdu(Acr_File *afp, int pdu_type, int result, int source, int reason); static Acr_Status write_data_tf(Acr_File *dicom_afp, Acr_Message message); static Acr_Status write_uid_item(Acr_File *afp, Acr_Element element, int item_type, long *length); static Acr_Status write_pres_context_item(Acr_File *afp, Acr_Element item, int is_request, long *length); static Acr_Status write_user_info_item(Acr_File *afp, Acr_Group group, long *length); static Acr_Status write_unknown_item(Acr_File *afp, int item_type, long data_length, char *data_pointer, long *length); static void pdu_copy_uid(char *string, char *buffer, int length); static Acr_File *initialize_dicom_stream(void *io_data, int maxlength, Acr_Io_Routine io_routine, Dicom_IO_stream_type stream_type); static Acr_Dicom_IO *get_dicom_io_pointer(Acr_File *afp); static Acr_File *get_dicom_real_afp(Acr_File *afp); static long get_dicom_pdv_watchpoint(Acr_File *afp); static void set_dicom_pdv_watchpoint(Acr_File *afp, long pdv_watchpoint); static void dicom_reset(Acr_File *afp); static void dicom_setup_output(Acr_File *afp, long command_length, long data_length); static int dicom_input_routine(void *io_data, void *buffer, int nbytes); static int dicom_output_routine(void *io_data, void *buffer, int nbytes); static int dicom_ismore(void *io_data); /* Macros */ #define EXTRACT_UID(group, elid, value, length) \ acr_group_add_element(group, pdu_create_element_uid(elid, value, (int)length)) #define EXTRACT_SHORT(group, elid, value) \ acr_group_add_element(group, pdu_create_element_short(elid, value)) #define SAVE_SHORT(group, elid, value) \ acr_group_add_element(group, \ acr_create_element_short(elid, (Acr_Short) (value))) #define GET_SHORT(input, output) \ acr_get_short(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output) #define GET_LONG(input, output) \ acr_get_long(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output) #define PUT_SHORT(input, output) \ acr_put_short(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output) #define PUT_LONG(input, output) \ acr_put_long(DICOM_NETWORK_BYTE_ORDER, (long) 1, input, output) #define COPY_UID(group, elid, buffer, length) \ pdu_copy_uid(acr_find_string(group, elid, ""), (char *) buffer, length) /*****************************************************************************/ /*************************** UTILITY ROUTINES ********************************/ /*****************************************************************************/ /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_uid_equal @INPUT : uid1 uid2 @OUTPUT : (nothing) @RETURNS : TRUE if uid's are equal, FALSE otherwise @DESCRIPTION: Responds to READYq message @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_uid_equal(char *uid1, char *uid2) { int len1, len2, i; /* Skip leading blanks */ while (isspace(*uid1)) {uid1++;} while (isspace(*uid2)) {uid2++;} /* Skip trailing blanks */ len1 = strlen(uid1); for (i=len1-1; (i >= 0) && isspace(uid1[i]); i--) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; len2 = strlen(uid2); for (i=len2-1; (i >= 0) && isspace(uid1[i]); i--) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; /* Compare the strings */ return (strcmp(uid1, uid2) == 0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_create_uid @INPUT : (none) @OUTPUT : (none) @RETURNS : pointer to internal buffer containing uid @DESCRIPTION: Routine to generate a unique uid. The string is returned in an internal buffer space and will be overwritten by subsequent calls. @METHOD : @GLOBALS : @CALLS : @CREATED : August 25, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_create_uid(void) { static char uid[64]; time_t current_time; int offset; int maxlen; union { unsigned char ch[4]; int ul; } host_id; static int counter = 1; /* Make up a new UID */ host_id.ul = gethostid(); current_time = time(NULL); (void) sprintf(uid, "1.%d.%d.%d.%d.%d.%d.%d", (int) 'I',(int) 'P', (int) host_id.ch[0], (int) host_id.ch[1], (int) host_id.ch[2], (int) host_id.ch[3], (int) getpid()); offset = strlen(uid); maxlen = sizeof(uid) - 1 - offset; (void) strftime(&uid[offset], (size_t) maxlen, ".%Y%m%d%H%M%S", localtime(¤t_time)); (void) sprintf(&uid[strlen(uid)], ".%08d", counter++); return uid; } /* Space for storing the implementation class uid. Should only be used by acr_set_implementation_uid and acr_get_implementation_uid */ static char Implementation_class_uid[65] = ""; /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_implementation_uid @INPUT : uid to set for implementation class @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set a uid for this software implementation. This string is returned by acr_get_implementation_uid which is used by client routines setting up associations. A static space is used to store the uid string. @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_implementation_uid(char *uid) { (void) strncpy(Implementation_class_uid, uid, sizeof(Implementation_class_uid)); Implementation_class_uid[sizeof(Implementation_class_uid)-1] = '\0'; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_implementation_uid @INPUT : (none) @OUTPUT : (none) @RETURNS : pointer to internal buffer containing uid @DESCRIPTION: Routine to generate a uid for this software implementation. The string is returned in an internal buffer space and will be overwritten by subsequent calls. @METHOD : @GLOBALS : @CALLS : @CREATED : March 23, 1998 (Peter Neelin) @MODIFIED : March 7, 2001 (P.N.) - changed name from acr_implementation_uid ---------------------------------------------------------------------------- */ char *acr_get_implementation_uid(void) { /* Set the uid if it is not already set */ if (Implementation_class_uid[0] == '\0') { (void) sprintf(Implementation_class_uid, "1.%d.%d.%d.%d.%d", (int) 'I', (int) 'P', (int) 'M', (int) 'N', (int) 'I'); } return Implementation_class_uid; } /*****************************************************************************/ /*************************** INPUT ROUTINES **********************************/ /*****************************************************************************/ /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_input_dicom_message @INPUT : dicom_afp - acr file pointer for dicom input @OUTPUT : message - NULL if no message is read @RETURNS : status of input @DESCRIPTION: Reads in a dicom message and returns it in a message structure. @METHOD : Although dicom connection, etc. syntaxes differ from the message syntax, this routine and the ones following put it all in the same form. @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_input_dicom_message(Acr_File *dicom_afp, Acr_Message *message) { int pdu_type; Acr_Long pdu_length; long old_watchpoint; Acr_File *afp; Acr_Group group; Acr_Status status; /* Set message to NULL in case of error */ *message = NULL; /* Get real afp */ afp = get_dicom_real_afp(dicom_afp); if (afp == NULL) { (void) fprintf(stderr, "Bad dicom file pointer\n"); exit(EXIT_FAILURE); } /* Set a watchpoint before we do anything so that we don't get stuck reading too much */ old_watchpoint = acr_get_io_watchpoint(afp); acr_set_io_watchpoint(afp, PDU_HEADER_LEN); /* Read in PDU type and length */ status = read_pdu_header(afp, &pdu_type, &pdu_length); if (old_watchpoint != ACR_NO_WATCHPOINT) old_watchpoint -= PDU_HEADER_LEN - acr_get_io_watchpoint(afp); if (status != ACR_OK) { acr_set_io_watchpoint(afp, old_watchpoint); return status; } /* Create the message and add the PDU type */ group = acr_create_group(DCM_PDU_GRPID); SAVE_SHORT(group, DCM_PDU_Type, pdu_type); /* Set a watchpoint for reading */ acr_set_io_watchpoint(afp, (long)pdu_length); /* Call the appropriate routine */ switch (pdu_type) { case ACR_PDU_ASSOC_RQ: status = read_assoc_rq(afp, group); break; case ACR_PDU_ASSOC_AC: status = read_assoc_ac(afp, group); break; case ACR_PDU_ASSOC_RJ: status = read_assoc_rj(afp, group); break; case ACR_PDU_DATA_TF: status = read_data_tf(dicom_afp, group); break; case ACR_PDU_REL_RQ: status = read_rel_rq(afp, group); break; case ACR_PDU_REL_RP: status = read_rel_rp(afp, group); break; case ACR_PDU_ABORT_RQ: status = read_abort_rq(afp, group); break; default: status = acr_skip_input_data(afp, pdu_length); break; } /* Check that we read exactly up to the watchpoint */ if (acr_get_io_watchpoint(afp) != 0) { switch (status) { case ACR_OK: status = ACR_PROTOCOL_ERROR; break; case ACR_END_OF_INPUT: status = ACR_ABNORMAL_END_OF_INPUT; break; default: status = ACR_OTHER_ERROR; break; } } if (old_watchpoint != ACR_NO_WATCHPOINT) old_watchpoint -= pdu_length - acr_get_io_watchpoint(afp); acr_set_io_watchpoint(afp, old_watchpoint); /* Create the message */ *message = acr_create_message(); acr_message_add_group_list(*message, group); return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_pdu_header @INPUT : afp - acr file pointer @OUTPUT : pdu_type pdu_length @RETURNS : status of input @DESCRIPTION: Reads in the header of a dicom PDU and returns the appropriate values. @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_pdu_header(Acr_File *afp, int *pdu_type, Acr_Long *pdu_length) { unsigned char buffer[PDU_HEADER_LEN]; Acr_Status status; /* Read in PDU type and length */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; *pdu_type = (int) buffer[0]; GET_LONG(&buffer[2], pdu_length); return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_assoc_rq @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in an association request @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_assoc_rq(Acr_File *afp, Acr_Group group) { return read_assoc_rq_ac(afp, group, TRUE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_assoc_rq_ac @INPUT : afp - acr file pointer is_request - TRUE if we should handle request type PDU, FALSE if we should handle accept type PDU @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in an association request or accept PDU @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_assoc_rq_ac(Acr_File *afp, Acr_Group group, int is_request) { unsigned char buffer[ASSOC_RQ_LEN - PDU_HEADER_LEN]; Acr_Element item, presentation_context_list, nextitem, followingitem; int have_pres_context_request; Acr_Status status; /* Read in header */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; /* Extract appropriate info and add it to the group */ EXTRACT_SHORT(group, DCM_PDU_Protocol_Version, (Acr_Short*)&buffer[6-PDU_HEADER_LEN]); if (is_request) { EXTRACT_UID(group, DCM_PDU_Called_Ap_title, (char *) &buffer[10-PDU_HEADER_LEN], 16); EXTRACT_UID(group, DCM_PDU_Calling_Ap_title, (char *) &buffer[26-PDU_HEADER_LEN], 16); } /* Loop, reading items, until the watchpoint is reached */ presentation_context_list = NULL; while (acr_get_io_watchpoint(afp) > 0) { /* Read in the item (or item list) */ status = read_pdu_item(afp, &item); if (status != ACR_OK) { acr_delete_element_list(item); acr_delete_element_list(presentation_context_list); return status; } /* Loop over items, checking that we got an acceptable one and add it to the appropriate thing */ for (nextitem = item; nextitem != NULL; nextitem = followingitem) { /* We have to get the next item before adding the current one to the list, otherwise we lose the link, since adding sets the next pointer to NULL */ followingitem = acr_get_element_next(nextitem); /* Put presentation context items in a list */ if (acr_match_element_id(DCM_PDU_Presentation_context, nextitem) || acr_match_element_id(DCM_PDU_Presentation_context_reply, nextitem)) { presentation_context_list = acr_element_list_add(presentation_context_list, nextitem); have_pres_context_request = acr_match_element_id(DCM_PDU_Presentation_context, nextitem); } else { acr_group_add_element(group, nextitem); } } } /* Add the presentation context list to the group */ if (have_pres_context_request) { acr_group_add_element(group, acr_create_element_sequence(DCM_PDU_Presentation_context_list, presentation_context_list)); } else { acr_group_add_element(group, acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list, presentation_context_list)); } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_data_tf @INPUT : dicom_afp - acr file pointer for DICOM message level @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in a data PDU @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_data_tf(Acr_File *dicom_afp, Acr_Group group) { Acr_Status status; Acr_Message message; Acr_Group cur, new; Acr_File *real_afp; /* Reset the virtual input stream */ dicom_reset(dicom_afp); /* Set the watchpoint to some very large number (other than ACR_NO_WATCHPOINT) so that acr_input_message will be happy knowing that there is a watchpoint, even though we don't know what it really is until we get close to it. */ acr_set_io_watchpoint(dicom_afp, LONG_MAX-1); /* Get the real afp */ real_afp = get_dicom_real_afp(dicom_afp); if (real_afp == NULL) { (void) fprintf(stderr, "Bad dicom file pointer\n"); exit(EXIT_FAILURE); } /* Set a watchpoint to force the read routine to look for a PDV header */ set_dicom_pdv_watchpoint(dicom_afp, (long) 0); /* Read in the message */ status = acr_input_message(dicom_afp, &message); /* Add the presentation context id to the pdu group */ SAVE_SHORT(group, DCM_PDU_Presentation_context_id, (Acr_Short) acr_get_dicom_pres_context_id(dicom_afp)); /* Get groups and tack them onto the group list that we already have */ if (message != NULL) { new = acr_get_message_group_list(message); cur = group; while (acr_get_group_next(cur) != NULL) { cur = acr_get_group_next(cur); } acr_set_group_next(cur, new); acr_message_reset(message); acr_delete_message(message); } /* Check that we read exactly up to the watchpoint */ if (acr_get_io_watchpoint(real_afp) != 0) { switch (status) { case ACR_OK: status = ACR_PROTOCOL_ERROR; break; case ACR_END_OF_INPUT: status = ACR_ABNORMAL_END_OF_INPUT; break; default: status = ACR_OTHER_ERROR; break; } } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_rel_rq @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in a release request @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ static Acr_Status read_rel_rq(Acr_File *afp, Acr_Group group) { return acr_skip_input_data(afp, (long) ACR_SIZEOF_LONG); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_abort_rq @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in an abort request @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_abort_rq(Acr_File *afp, Acr_Group group) { unsigned char buffer[ABORT_RQ_LEN - PDU_HEADER_LEN]; Acr_Status status; /* Read in request header */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; /* Get values */ SAVE_SHORT(group, DCM_PDU_Source, buffer[8 - PDU_HEADER_LEN]); SAVE_SHORT(group, DCM_PDU_Reason, buffer[9 - PDU_HEADER_LEN]); return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_assoc_ac @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in an association accept PDU @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_assoc_ac(Acr_File *afp, Acr_Group group) { return read_assoc_rq_ac(afp, group, FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_assoc_rj @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in an association reject PDU @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_assoc_rj(Acr_File *afp, Acr_Group group) { unsigned char buffer[ASSOC_RJ_LEN - PDU_HEADER_LEN]; Acr_Status status; /* Read in request header */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; /* Get values */ SAVE_SHORT(group, DCM_PDU_Result, buffer[7 - PDU_HEADER_LEN]); SAVE_SHORT(group, DCM_PDU_Source, buffer[8 - PDU_HEADER_LEN]); SAVE_SHORT(group, DCM_PDU_Reason, buffer[9 - PDU_HEADER_LEN]); return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_rel_rp @INPUT : afp - acr file pointer @OUTPUT : group - group to which pdu information should be added @RETURNS : status of input @DESCRIPTION: Reads in a release reply @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ static Acr_Status read_rel_rp(Acr_File *afp, Acr_Group group) { return acr_skip_input_data(afp, (long) ACR_SIZEOF_LONG); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_pdu_item @INPUT : afp - acr file pointer @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in any type of PDU item and returns it as an element. The element may either by a real DICOM-type element or may be a sequence item. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_pdu_item(Acr_File *afp, Acr_Element *item) { unsigned char buffer[PDU_ITEM_HEADER_LEN]; Acr_Short item_length; int item_type; long old_watchpoint; Acr_Status status; /* Read in PDU item type and length */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; item_type = (int) buffer[0]; GET_SHORT(&buffer[2], &item_length); /* Set a watchpoint for reading after saving the previous watchpoint */ old_watchpoint = acr_get_io_watchpoint(afp); acr_set_io_watchpoint(afp, (long) item_length); /* Call the appropriate routine */ switch (item_type) { case PDU_ITEM_APPLICATION_CONTEXT: status = read_uid_item(afp, DCM_PDU_Application_context, item); break; case PDU_ITEM_PRESENTATION_CONTEXT: status = read_pres_context_item(afp, item); break; case PDU_ITEM_PRES_CONTEXT_REPLY: status = read_pres_context_reply_item(afp, item); break; case PDU_ITEM_ABSTRACT_SYNTAX: status = read_uid_item(afp, DCM_PDU_Abstract_syntax, item); break; case PDU_ITEM_TRANSFER_SYNTAX: status = read_uid_item(afp, DCM_PDU_Transfer_syntax, item); break; case PDU_ITEM_USER_INFORMATION: status = read_user_info_item(afp, item); break; case PDU_ITEM_MAXIMUM_LENGTH: status = read_long_item(afp, DCM_PDU_Maximum_length, item); break; case PDU_ITEM_IMPLEMENTATION_CLASS_UID: status = read_uid_item(afp, DCM_PDU_Implementation_class_uid, item); break; case PDU_ITEM_IMPLEMENTATION_VERSION_NAME: status = read_uid_item(afp, DCM_PDU_Implementation_version_name, item); break; default: status = read_unknown_item(afp, item_type, item); break; } /* Make sure that we read the right amount of data */ if ((status == ACR_OK) && (acr_get_io_watchpoint(afp) != 0)) { status = ACR_PROTOCOL_ERROR; } /* Set the old watchpoint */ if (old_watchpoint != ACR_NO_WATCHPOINT) old_watchpoint -= (long) item_length - acr_get_io_watchpoint(afp); acr_set_io_watchpoint(afp, old_watchpoint); return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_uid_item @INPUT : afp - acr file pointer elid - id of element to be created @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in a UID item and creates the corresponding element. Data is read up to the next watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_uid_item(Acr_File *afp, Acr_Element_Id elid, Acr_Element *item) { unsigned char buffer[MAX_PDU_STRING_LENGTH]; long length; Acr_Status status; /* Set default item */ *item = NULL; /* Figure out how much data to read */ length = acr_get_io_watchpoint(afp); if (length > MAX_PDU_STRING_LENGTH) return ACR_PDU_UID_TOO_LONG; /* Read in the buffer */ status = acr_read_buffer(afp, buffer, length, NULL); if (status != ACR_OK) return status; /* Pull out the uid and create the item */ *item = pdu_create_element_uid(elid, (char *) buffer, (int) length); return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_long_item @INPUT : afp - acr file pointer elid - id of element to be created @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in an item of type long and creates the corresponding element. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_long_item(Acr_File *afp, Acr_Element_Id elid, Acr_Element *item) { unsigned char buffer[ACR_SIZEOF_LONG]; Acr_Long value; Acr_Status status; /* Set default item */ *item = NULL; /* Check that the right amount of data is to be read */ if (acr_get_io_watchpoint(afp) != ACR_SIZEOF_LONG) return ACR_PROTOCOL_ERROR; /* Read in the buffer and get the value */ status = acr_read_buffer(afp, buffer, (long) ACR_SIZEOF_LONG, NULL); if (status != ACR_OK) return status; GET_LONG((void *) buffer, &value); /* Create the item */ *item = acr_create_element_long(elid, value); return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_unknown_item @INPUT : afp - acr file pointer item_type - type code for item to be read in (should be a value between 0 and 255) @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in an item of unknown type and creates an element with element id = item_type + ACR_UNKNOWN_PDU_ITEM_OFFSET @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_unknown_item(Acr_File *afp, int item_type, Acr_Element *item) { Acr_Status status; long length; unsigned char *data_pointer; /* Set default item */ *item = NULL; /* Figure out how much data to read */ length = acr_get_io_watchpoint(afp); /* Get space for the data. Add a NUL character to the end in case it is a string */ data_pointer = MALLOC((size_t) length+1); data_pointer[length] = '\0'; /* Read in the buffer and get the value */ status = acr_read_buffer(afp, data_pointer, length, NULL); if (status != ACR_OK) { FREE(data_pointer); return status; } /* Figure out the element id */ item_type += ACR_UNKNOWN_PDU_ITEM_OFFSET; /* Create the item */ *item = acr_create_element(DCM_PDU_GRPID, item_type, ACR_VR_UNKNOWN, length, (char *) data_pointer); return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_pres_context_item @INPUT : afp - acr file pointer @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in a presentation context item for an associate request and creates the corresponding element. Data is read up to the next watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_pres_context_item(Acr_File *afp, Acr_Element *item) { unsigned char buffer[4]; Acr_Element itemlist = NULL, newitem; Acr_Status status; int nitems; /* Set default item */ *item = NULL; /* Read in buffer */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; /* Get presentation context id and save it in the item list */ newitem = acr_create_element_short(DCM_PDU_Presentation_context_id, (Acr_Short) buffer[0]); itemlist = acr_element_list_add(itemlist, newitem); /* Read in items until we reach watchpoint */ nitems = 0; while (acr_get_io_watchpoint(afp) > 0) { /* Read in the item and add it to the list */ status = read_pdu_item(afp, &newitem); if (newitem != NULL) { itemlist = acr_element_list_add(itemlist, newitem); } /* Check for a read error */ if (status != ACR_OK) { acr_delete_element_list(itemlist); return status; } /* Make sure that we got the right thing */ if (((nitems == 0) && !acr_match_element_id(DCM_PDU_Abstract_syntax, newitem)) || ((nitems > 0) && !acr_match_element_id(DCM_PDU_Transfer_syntax, newitem))) { acr_delete_element_list(itemlist); return ACR_PROTOCOL_ERROR; } nitems++; } /* Create the item */ *item = acr_create_element_sequence(DCM_PDU_Presentation_context, itemlist); return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_pres_context_reply_item @INPUT : afp - acr file pointer @OUTPUT : item - element representing value read in @RETURNS : status of input @DESCRIPTION: Reads in a presentation context item for an associate accept and creates the corresponding element. Data is read up to the next watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_pres_context_reply_item(Acr_File *afp, Acr_Element *item) { unsigned char buffer[4]; Acr_Element itemlist = NULL, newitem; Acr_Status status; /* Set default item */ *item = NULL; /* Read in buffer */ status = acr_read_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; /* Get presentation context id and result and save them in the item list */ newitem = acr_create_element_short(DCM_PDU_Presentation_context_id, (Acr_Short) buffer[0]); itemlist = acr_element_list_add(itemlist, newitem); newitem = acr_create_element_short(DCM_PDU_Result, (Acr_Short) buffer[2]); itemlist = acr_element_list_add(itemlist, newitem); /* Read in items until we reach watchpoint */ while (acr_get_io_watchpoint(afp) > 0) { /* Read in the item and add it to the list */ status = read_pdu_item(afp, &newitem); if (newitem != NULL) { itemlist = acr_element_list_add(itemlist, newitem); } /* Check for a read error */ if (status != ACR_OK) { acr_delete_element_list(itemlist); return status; } /* Make sure that we got the right thing */ if (!acr_match_element_id(DCM_PDU_Transfer_syntax, newitem)) { acr_delete_element_list(itemlist); return ACR_PROTOCOL_ERROR; } } /* Create the item */ *item = acr_create_element_sequence(DCM_PDU_Presentation_context_reply, itemlist); return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_user_info_item @INPUT : afp - acr file pointer @OUTPUT : item - element list representing values read in @RETURNS : status of input @DESCRIPTION: Reads in a user info item and creates the corresponding element list. Data is read up to the next watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status read_user_info_item(Acr_File *afp, Acr_Element *item) { Acr_Status status; Acr_Element newitem; /* Set default item */ *item = NULL; /* Read in items until we reach watchpoint */ while (acr_get_io_watchpoint(afp) > 0) { /* Read in the item */ status = read_pdu_item(afp, &newitem); /* Check for a read error */ if (status != ACR_OK) { acr_delete_element_list(newitem); return status; } /* Add the item to the list */ *item = acr_element_list_add(*item, newitem); } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_uid_string @INPUT : buffer - pointer to start of string length - length of string @OUTPUT : (none) @RETURNS : Pointer to temporary string space containing a copy of the string without leading and trailing blanks. @DESCRIPTION: Removes leading and trailing blanks from a string and copies it into temporary space. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static char *get_uid_string(char *buffer, int length) { static char string[MAX_PDU_STRING_LENGTH]; int start, end, ichar; /* Check for maximum string length */ if (length >= MAX_PDU_STRING_LENGTH) length = MAX_PDU_STRING_LENGTH - 1; /* Skip leading spaces */ for (start=0; (start < length) && isspace((int) buffer[start]); start++) {} /* Look for the last non-space */ for (end=length-1; (end >= 0) && isspace((int) buffer[end]); end--) {} /* Copy the string */ for (ichar=start; ichar <= end; ichar++) { string[ichar-start] = buffer[ichar]; } /* Finish off the string. Pad the string with a blank if needed to give an even length */ if (((end-start+1) % 2) != 0) { /* Odd length */ string[++end] = ' '; } string[end+1] = '\0'; return string; } /* ----------------------------- MNI Header ----------------------------------- @NAME : pdu_create_element_uid @INPUT : elid - Element id structure value - pointer to start of string length - length of string @OUTPUT : (none) @RETURNS : Pointer to newly created element @DESCRIPTION: Creates an element containing a string of fixed length. Leading and trailing spaces are removed. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Element pdu_create_element_uid(Acr_Element_Id elid, char *value, int length) { Acr_Element item; item = acr_create_element_string(elid, get_uid_string(value, length)); acr_set_element_vr(item, ACR_VR_UI); return item; } /* ----------------------------- MNI Header ----------------------------------- @NAME : pdu_create_element_short @INPUT : elid - Element id structure value - pointer to location containing short @OUTPUT : (none) @RETURNS : Pointer to newly created element @DESCRIPTION: Creates an element containing a short which is converted from the input format. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Element pdu_create_element_short(Acr_Element_Id elid, void *input_value) { Acr_Short svalue; GET_SHORT(input_value, &svalue); return acr_create_element_short(elid, svalue); } /*****************************************************************************/ /*************************** OUTPUT ROUTINES *********************************/ /*****************************************************************************/ /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_output_dicom_message @INPUT : dicom_afp - acr file pointer for dicom output message - the message to write out @OUTPUT : (none) @RETURNS : status of output @DESCRIPTION: Writes out a dicom message. @METHOD : Although dicom connection, etc. syntaxes differ from the message syntax, this routine and the ones following take a message in the same form. @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_Status acr_output_dicom_message(Acr_File *dicom_afp, Acr_Message message) { int pdu_type; Acr_Group group_list; Acr_File *afp; Acr_Status status; /* Get real afp */ afp = get_dicom_real_afp(dicom_afp); if (afp == NULL) { (void) fprintf(stderr, "Bad dicom file pointer\n"); exit(EXIT_FAILURE); } /* Get group list */ group_list = acr_get_message_group_list(message); /* Switch on pdu type. If the type is not found, then assume that we are writing data. */ pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); switch (pdu_type) { case ACR_PDU_ASSOC_RQ: status = write_assoc_rq(afp, group_list); break; case ACR_PDU_ASSOC_AC: status = write_assoc_ac(afp, group_list); break; case ACR_PDU_ASSOC_RJ: status = write_assoc_rj(afp, group_list); break; case ACR_PDU_DATA_TF: status = write_data_tf(dicom_afp, message); break; case ACR_PDU_REL_RQ: status = write_rel_rq(afp, group_list); break; case ACR_PDU_REL_RP: status = write_rel_rp(afp, group_list); break; case ACR_PDU_ABORT_RQ: status = write_abort_rq(afp, group_list); break; default: (void) fprintf(stderr, "Unrecognized pdu type for output (%d)\n", pdu_type); status = ACR_PROTOCOL_ERROR; } /* Flush the output buffer */ if ((acr_file_flush(afp) == EOF) && (status == ACR_OK)) { status = ACR_ABNORMAL_END_OF_OUTPUT; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_assoc_rq @INPUT : afp - acr file pointer group - info to write @OUTPUT : (none) @RETURNS : status of output @DESCRIPTION: Writes out an association request @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_assoc_rq(Acr_File *afp, Acr_Group group) { return write_assoc_rq_ac(afp, group, TRUE, NULL); } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_assoc_ac @INPUT : afp - acr file pointer group - info to write @OUTPUT : (none) @RETURNS : status of output @DESCRIPTION: Writes out an association accept @METHOD : @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_assoc_ac(Acr_File *afp, Acr_Group group) { return write_assoc_rq_ac(afp, group, FALSE, NULL); } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_assoc_rq_ac @INPUT : afp - acr file pointer group - info to write is_request - TRUE if we should handle request type PDU, FALSE if we should handle accept type PDU @OUTPUT : length - if non-NULL, then nothing is written and the value is incremented by the amount of data written out. @RETURNS : status of output @DESCRIPTION: Writes out an association request or accept PDU @METHOD : Can be used to write out a PDU or calculate its length. If length is non-NULL, then length calculation is done and nothing is written out. Otherwise the PDU is written out. This routine calls itself to work out the PDU length. @GLOBALS : @CALLS : @CREATED : February 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_assoc_rq_ac(Acr_File *afp, Acr_Group group, int is_request, long *length) { unsigned char buffer[ASSOC_RQ_LEN]; long pdu_length; int do_write; size_t offset; Acr_Short svalue; Acr_Long lvalue; Acr_Element element; Acr_Status status; Acr_Element_Id elid; /* Should we write or calculate length */ do_write = (length == NULL); /* Write out the first part of the PDU */ if (do_write) { /* Zero the buffer */ for (offset=0; offset 0) && isspace(uid[uid_length-1])) {uid_length--;} /* Write out the uid */ if (do_write) { status = acr_write_buffer(afp, (unsigned char *)uid, uid_length, NULL); if (status != ACR_OK) return status; } else { *length += uid_length; } #if 0 /* Check that we wrote an even number of bytes */ if ((uid_length % 2) != 0) { if (do_write) { (void) acr_putc((int) ' ', afp); } else { *length += 1; } } #endif return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_pres_context_item @INPUT : afp - acr file pointer item - information to write out @OUTPUT : length - if non-NULL, then routine computes length of output @RETURNS : status of output @DESCRIPTION: Writes out a presentation context item (or reply item). @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_pres_context_item(Acr_File *afp, Acr_Element item, int is_request, long *length) { unsigned char buffer[PDU_ITEM_HEADER_LEN+4]; long item_length; Acr_Status status; int do_write; Acr_Element itemlist, subitem; Acr_Short svalue; /* Check element */ if ((item == NULL) || (!acr_element_is_sequence(item))) { return ACR_PROTOCOL_ERROR; } itemlist = (Acr_Element) acr_get_element_data(item); /* Check whether we are writing or counting */ do_write = (length == NULL); /* Write item header */ if (do_write) { buffer[0] = (is_request ? PDU_ITEM_PRESENTATION_CONTEXT : PDU_ITEM_PRES_CONTEXT_REPLY); buffer[1] = 0; item_length = 0; (void) write_pres_context_item(afp, item, is_request, &item_length); item_length -= PDU_ITEM_HEADER_LEN; svalue = (Acr_Short) item_length; PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]); subitem = acr_find_element_id(itemlist, DCM_PDU_Presentation_context_id); if (subitem == NULL) return ACR_PROTOCOL_ERROR; buffer[4] = (unsigned char) acr_get_element_short(subitem); buffer[5] = 0; if (!is_request) { subitem = acr_find_element_id(itemlist, DCM_PDU_Result); if (subitem == NULL) return ACR_PROTOCOL_ERROR; buffer[6] = (unsigned char) acr_get_element_short(subitem); } else buffer[6] = 0; buffer[7] = 0; status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; } else { *length += sizeof(buffer); } /* Write out the transfer syntaxes */ if (is_request) { subitem = acr_find_element_id(itemlist, DCM_PDU_Abstract_syntax); if (subitem == NULL) return ACR_PROTOCOL_ERROR; status = write_uid_item(afp, subitem, PDU_ITEM_ABSTRACT_SYNTAX, length); if (status != ACR_OK) return status; subitem = itemlist; while (subitem != NULL) { if (acr_match_element_id(DCM_PDU_Transfer_syntax, subitem)) { status = write_uid_item(afp, subitem, PDU_ITEM_TRANSFER_SYNTAX, length); if (status != ACR_OK) return status; } subitem = acr_get_element_next(subitem); } } else { subitem = acr_find_element_id(itemlist, DCM_PDU_Transfer_syntax); if (subitem == NULL) return ACR_PROTOCOL_ERROR; status = write_uid_item(afp, subitem, PDU_ITEM_TRANSFER_SYNTAX, length); if (status != ACR_OK) return status; } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_user_info_item @INPUT : afp - acr file pointer group - information to write out @OUTPUT : length - if non-NULL, then routine computes length of output @RETURNS : status of output @DESCRIPTION: Writes out a user info item @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_user_info_item(Acr_File *afp, Acr_Group group, long *length) { unsigned char buffer[2*PDU_ITEM_HEADER_LEN+ACR_SIZEOF_LONG]; long item_length; Acr_Long maximum_length; Acr_Status status; int do_write; Acr_Short svalue; Acr_Element element; int item_type; /* Check whether we are writing or counting */ do_write = (length == NULL); /* Write item header */ if (do_write) { buffer[0] = PDU_ITEM_USER_INFORMATION; buffer[1] = 0; item_length = 0; (void) write_user_info_item(afp, group, &item_length); item_length -= PDU_ITEM_HEADER_LEN; svalue = (Acr_Short) item_length; PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]); buffer[4] = PDU_ITEM_MAXIMUM_LENGTH; buffer[5] = 0; svalue = ACR_SIZEOF_LONG; PUT_SHORT(&svalue, (Acr_Short*)&buffer[6]); maximum_length = acr_find_long(group, DCM_PDU_Maximum_length, 0); PUT_LONG(&maximum_length, (Acr_Long*)&buffer[8]); status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; } else { *length += sizeof(buffer); } /* Write out implementation information */ element = acr_find_group_element(group, DCM_PDU_Implementation_class_uid); if (element != NULL) { status = write_unknown_item(afp, PDU_ITEM_IMPLEMENTATION_CLASS_UID, acr_get_element_length(element), acr_get_element_data(element), length); if (status != ACR_OK) return status; } element = acr_find_group_element(group, DCM_PDU_Implementation_version_name); if (element != NULL) { status = write_unknown_item(afp, PDU_ITEM_IMPLEMENTATION_VERSION_NAME, acr_get_element_length(element), acr_get_element_data(element), length); if (status != ACR_OK) return status; } /* Write any unknown items with an item type > PDU_ITEM_USER_INFORMATION */ if (acr_get_group_group(group) == DCM_PDU_GRPID) { /* Loop over all elements */ for (element = acr_get_group_element_list(group); element != NULL; element = acr_get_element_next(element)) { /* Check that the element is unknown and that it is an user info item */ item_type = acr_get_element_element(element); if (item_type < ACR_UNKNOWN_PDU_ITEM_OFFSET) continue; item_type -= ACR_UNKNOWN_PDU_ITEM_OFFSET; if (item_type < PDU_ITEM_USER_INFORMATION) continue; /* Write out the value */ status = write_unknown_item(afp, item_type, acr_get_element_length(element), acr_get_element_data(element), length); if (status != ACR_OK) return status; } } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_unknown_item @INPUT : afp - acr file pointer item_type - code identifying item to be written out data_length - length of data to be written data_pointer - pointer to data @OUTPUT : length - if non-NULL, then nothing is written and the value is incremented by the amount of data written out. @RETURNS : status of output @DESCRIPTION: Writes out an item of unknown type. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status write_unknown_item(Acr_File *afp, int item_type, long data_length, char *data_pointer, long *length) { unsigned char buffer[PDU_ITEM_HEADER_LEN]; long item_length; int do_write; Acr_Status status; Acr_Short svalue; /* Check whether we are writing or counting */ do_write = (length == NULL); /* Write item header */ if (do_write) { buffer[0] = (unsigned char) item_type; buffer[1] = 0; item_length = 0; (void) write_unknown_item(afp, item_type, data_length, data_pointer, &item_length); item_length -= PDU_ITEM_HEADER_LEN; svalue = (Acr_Short) item_length; PUT_SHORT(&svalue, (Acr_Short*)&buffer[2]); status = acr_write_buffer(afp, buffer, sizeof(buffer), NULL); if (status != ACR_OK) return status; } else { *length += sizeof(buffer); } /* Write out the data */ if (do_write) { status = acr_write_buffer(afp, (unsigned char *) data_pointer, data_length, NULL); if (status != ACR_OK) return status; } else { *length += data_length; } return ACR_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : pdu_copy_uid @INPUT : string length - maximum length of uid @OUTPUT : buffer @RETURNS : (nothing) @DESCRIPTION: Copies a uid from string to buffer, padding with trailing blanks up to length. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void pdu_copy_uid(Acr_String string, char *buffer, int length) { int i; for (i=0; (i < length) && (string[i] != '\0'); i++) { buffer[i] = string[i]; } for (; (i < length); i++) { buffer[i] = ' '; } } /*****************************************************************************/ /************************** DICOM I/O STREAM *********************************/ /*****************************************************************************/ /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_initialize_dicom_input @INPUT : io_data - pointer to data for read and write routines maxlength - maximum length for a single read or write (zero or negative means use internal maximum). io_routine - routine to read or write data @OUTPUT : (none) @RETURNS : pointer to Acr_File structure created @DESCRIPTION: Sets up the routines for reading dicom messages from an input stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_File *acr_initialize_dicom_input(void *io_data, int maxlength, Acr_Io_Routine io_routine) { return initialize_dicom_stream(io_data, maxlength, io_routine, DICOM_INPUT); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dicom_set_ismore_function @INPUT : afp ismore_function @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Sets the function to be used for testing if there is more data waiting on the input stream. stream. @METHOD : @GLOBALS : @CALLS : @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dicom_set_ismore_function(Acr_File *afp, Acr_Ismore_Function ismore_function) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); /* Set the dicom ismore function on the virtual stream and the user-supplied one on the real stream */ if (stream_data == NULL) { acr_file_set_ismore_function(afp, ismore_function); } else { acr_file_set_ismore_function(stream_data->virtual_afp, dicom_ismore); acr_file_set_ismore_function(stream_data->real_afp, ismore_function); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_initialize_dicom_output @INPUT : io_data - pointer to data for read and write routines maxlength - maximum length for a single read or write (zero or negative means use internal maximum). io_routine - routine to read or write data @OUTPUT : (none) @RETURNS : pointer to Acr_File structure created @DESCRIPTION: Sets up the routines for writing dicom messages to an output stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_File *acr_initialize_dicom_output(void *io_data, int maxlength, Acr_Io_Routine io_routine) { return initialize_dicom_stream(io_data, maxlength, io_routine, DICOM_OUTPUT); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_close_dicom_file @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Closes a dicom output stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_close_dicom_file(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); /* Flush the virtual stream */ (void) acr_file_flush(afp); /* Close the real stream */ if (stream_data != NULL) { acr_file_free(stream_data->real_afp); if (stream_data->client_data != NULL) { FREE(stream_data->client_data); } } /* Free the virtual stream. This will free the stream data structure. */ acr_file_free(afp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dicom_get_io_data @INPUT : afp @OUTPUT : (none) @RETURNS : pointer to io data @DESCRIPTION: Gets back pointer that was passed in to acr_initialize_dicom_{input,output}. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *acr_dicom_get_io_data(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); /* Get the io pointer for the real stream */ return acr_file_get_io_data(stream_data->real_afp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dicom_enable_trace / acr_dicom_disable_trace @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Enables / disables trace on a dicom stream (on the real stream) @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dicom_enable_trace(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); acr_file_enable_trace((stream_data == NULL) ? afp : stream_data->real_afp); } void acr_dicom_disable_trace(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); acr_file_disable_trace((stream_data == NULL) ? afp : stream_data->real_afp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dicom_set_eof @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Tells the input or output stream that it has reached the end of file so that no more data will be read or written. This can be useful for preventing further reading or writing if an alarm goes off, for example. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dicom_set_eof(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); /* Set eof on both afp's */ acr_file_set_eof(afp); acr_file_set_eof(stream_data->real_afp); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_dicom_stream @INPUT : io_data - pointer to data for read and write routines maxlength - maximum length for a single read or write (zero or negative means use internal maximum). io_routine - routine to read or write data stream_type - DICOM_INPUT or DICOM_OUTPUT @OUTPUT : (none) @RETURNS : pointer to Acr_File structure created @DESCRIPTION: Sets up the routines for dicom i/o @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_File *initialize_dicom_stream(void *io_data, int maxlength, Acr_Io_Routine io_routine, Dicom_IO_stream_type stream_type) { Acr_File *real_afp, *virtual_afp; Acr_Dicom_IO *stream_data; Acr_Io_Routine virtual_io_routine; /* Create the real input stream */ real_afp = acr_file_initialize(io_data, maxlength, io_routine); /* Create the virtual afp data */ stream_data = MALLOC(sizeof(*stream_data)); stream_data->stream_type = stream_type; stream_data->real_afp = real_afp; stream_data->presentation_context_id = 0; stream_data->pdv_watchpoint = ACR_NO_WATCHPOINT; stream_data->maximum_length = LONG_MAX; stream_data->writing_command = FALSE; stream_data->data_length = 0; stream_data->client_data = NULL; /* Create the virtual input stream */ if (stream_type == DICOM_INPUT) { virtual_io_routine = dicom_input_routine; } else { virtual_io_routine = dicom_output_routine; } virtual_afp = acr_file_initialize((void *) stream_data, 0, virtual_io_routine); stream_data->virtual_afp = virtual_afp; return virtual_afp; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dicom_io_pointer @INPUT : afp @OUTPUT : (none) @RETURNS : pointer to Acr_Dicom_IO structure or NULL if an error occurs @DESCRIPTION: Gets the pointer to the Acr_Dicom_IO structure contained within the afp. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Dicom_IO *get_dicom_io_pointer(Acr_File *afp) { Acr_Dicom_IO *stream_data; stream_data = (Acr_Dicom_IO *) acr_file_get_io_data(afp); if ((stream_data == NULL) || ((stream_data->stream_type != DICOM_INPUT) && (stream_data->stream_type != DICOM_OUTPUT))) { return NULL; } return stream_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dicom_real_afp @INPUT : afp @OUTPUT : (none) @RETURNS : pointer to Acr_File for real input or output @DESCRIPTION: Gets the pointer to the Acr_File structure that is really connected to a stdio input or output stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_File *get_dicom_real_afp(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return NULL; /* Return the afp */ return stream_data->real_afp; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dicom_pdv_watchpoint @INPUT : afp @OUTPUT : (none) @RETURNS : Distance from current position to pdv watchpoint on real stream @DESCRIPTION: Returns the distance to the PDV watchpoint on the real i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static long get_dicom_pdv_watchpoint(Acr_File *afp) { Acr_Dicom_IO *stream_data; long current_watchpoint; stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return ACR_NO_WATCHPOINT; current_watchpoint = acr_get_io_watchpoint(stream_data->real_afp); if ((stream_data->pdv_watchpoint == ACR_NO_WATCHPOINT) || (current_watchpoint == ACR_NO_WATCHPOINT)) { return ACR_NO_WATCHPOINT; } else { return current_watchpoint - stream_data->pdv_watchpoint; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_dicom_pdv_watchpoint @INPUT : afp pdv_watchpoint - distance to pdu watchpoint @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Sets the watchpoint for the PDV relative to the current position. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void set_dicom_pdv_watchpoint(Acr_File *afp, long pdv_watchpoint) { Acr_Dicom_IO *stream_data; long current_watchpoint; stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; current_watchpoint = acr_get_io_watchpoint(stream_data->real_afp); if (pdv_watchpoint == ACR_NO_WATCHPOINT) { stream_data->pdv_watchpoint = ACR_NO_WATCHPOINT; } else if (current_watchpoint == ACR_NO_WATCHPOINT) { acr_set_io_watchpoint(stream_data->real_afp, pdv_watchpoint); stream_data->pdv_watchpoint = 0; } else { stream_data->pdv_watchpoint = current_watchpoint - pdv_watchpoint; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_dicom_maximum_length @INPUT : afp maximum_length - if <= 0, then a very large value is used @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Sets the maximum PDU length for a dicom stream. If the length is unreasonably small (<= PDV header length) then a more reasonable value is set. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_dicom_maximum_length(Acr_File *afp, long maximum_length) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; /* Set the maximum length */ if (maximum_length <= 0) maximum_length = LONG_MAX; else if (maximum_length <= DATA_TF_LEN) maximum_length = DATA_TF_LEN * 2; stream_data->maximum_length = maximum_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_dicom_pres_context_id @INPUT : afp presentation_context_id - presentation context id to use @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Sets the presentation context id for an i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_dicom_pres_context_id(Acr_File *afp, int presentation_context_id) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; /* Set the id */ stream_data->presentation_context_id = presentation_context_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_dicom_pres_context_id @INPUT : afp @OUTPUT : (none) @RETURNS : presentation context id @DESCRIPTION: Gets the presentation context id for an i/o stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_dicom_pres_context_id(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return 0; /* Return the id */ return stream_data->presentation_context_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_dicom_client_data @INPUT : afp client_data - pointer to client data to store @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Stores a pointer to client data @METHOD : @GLOBALS : @CALLS : @CREATED : November 11, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_dicom_client_data(Acr_File *afp, void *client_data) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; /* Check for an old value */ if (stream_data->client_data != NULL) { FREE(stream_data->client_data); } /* Set the pointer */ stream_data->client_data = client_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_dicom_client_data @INPUT : afp @OUTPUT : (none) @RETURNS : pointer to client data @DESCRIPTION: Gets the pointer to the client data @METHOD : @GLOBALS : @CALLS : @CREATED : November 11, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *acr_get_dicom_client_data(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return 0; /* Return the pointer */ return stream_data->client_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_reset @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Resets the dicom (virtual) stream @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void dicom_reset(Acr_File *afp) { Acr_Dicom_IO *stream_data; /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; /* Do the reset and mark this as the first time */ acr_file_reset(afp); set_dicom_pdv_watchpoint(afp, ACR_NO_WATCHPOINT); } /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_setup_output @INPUT : afp command_length - length of command portion of message data_length - length of data portion of message @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Sets up the dicom virtual stream to handle data output @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void dicom_setup_output(Acr_File *afp, long command_length, long data_length) { Acr_Dicom_IO *stream_data; /* Reset the stream */ dicom_reset(afp); /* Get the structure pointer */ stream_data = get_dicom_io_pointer(afp); if (stream_data == NULL) return; /* Set real afp watchpoint to zero to force a PDU header right away */ acr_set_io_watchpoint(stream_data->real_afp, 0); set_dicom_pdv_watchpoint(afp, 0); /* Figure out whether we are starting with a command or data, save the data length and set the watchpoint so that we can figure out when to change from command to data */ if (command_length > 0) { stream_data->writing_command = TRUE; stream_data->data_length = data_length; acr_set_io_watchpoint(afp, command_length); } else { stream_data->writing_command = FALSE; stream_data->data_length = data_length; acr_set_io_watchpoint(afp, data_length); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_input_routine @INPUT : io_data nbytes - number of bytes to read @OUTPUT : buffer - buffer into which we will read @RETURNS : Number of bytes read. @DESCRIPTION: Dicom input routine for reading from a real input stream into a virtual input stream. @METHOD : We use three watchpoints. One on the real stream to mark the end of the PDU. One in stream data structure to mark the end of the PDV. One is set on the virtual (dicom) stream when we find the last fragment of a message to mark the end of the message data. @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int dicom_input_routine(void *io_data, void *buffer, int nbytes) { unsigned char header_buffer[DATA_TF_LEN]; Acr_Dicom_IO *stream_data; long nread; int pdu_type; Acr_Long pdu_length, pdv_length; long pdv_watchpoint; Acr_Status status; int presentation_context_id, control_header; Acr_File *real_afp, *dicom_afp; /* Get file pointer */ if (io_data == NULL) return 0; stream_data = (Acr_Dicom_IO *) io_data; real_afp = stream_data->real_afp; dicom_afp = stream_data->virtual_afp; /* Get distance to next watchpoint on real stream */ pdv_watchpoint = get_dicom_pdv_watchpoint(dicom_afp); /* Check whether we need to read in some more headers */ if (pdv_watchpoint <= 0) { /* Do we need to read in a PDU header? */ if (acr_get_io_watchpoint(real_afp) <= 0) { acr_set_io_watchpoint(real_afp, PDU_HEADER_LEN + sizeof(header_buffer)); status = read_pdu_header(real_afp, &pdu_type, &pdu_length); if ((status != ACR_OK) || (pdu_type != ACR_PDU_DATA_TF)) { return -1; } acr_set_io_watchpoint(real_afp, pdu_length); } /* Read in the PDV header */ status = acr_read_buffer(real_afp, header_buffer, sizeof(header_buffer), NULL); if (status != ACR_OK) return -1; GET_LONG((void *) header_buffer, &pdv_length); pdv_length -= 2; presentation_context_id = (int) header_buffer[ACR_SIZEOF_LONG]; control_header = (int) header_buffer[ACR_SIZEOF_LONG+1]; /* Check for a bad PDV length compared to PDU length */ if (pdv_length > acr_get_io_watchpoint(real_afp)) { return -1; } /* Set the watchpoint for this PDV */ set_dicom_pdv_watchpoint(dicom_afp, (long)pdv_length); pdv_watchpoint = (long)pdv_length; /* Save the presentation context id */ acr_set_dicom_pres_context_id(dicom_afp, presentation_context_id); /* Check whether this is the last fragment. If it is, set the watchpoint. */ if ((control_header & PDV_LAST_FRAGMENT_MASK) && ((long)pdv_length == acr_get_io_watchpoint(real_afp))) { acr_set_io_watchpoint(dicom_afp, (long)pdv_length); } } /* Make sure that we don't read too far */ if (nbytes > pdv_watchpoint) { nbytes = pdv_watchpoint; } /* Read the data into the buffer */ status = acr_read_buffer(real_afp, buffer, (long) nbytes, &nread); return (int) nread; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_output_routine @INPUT : io_data nbytes - number of bytes to write @OUTPUT : buffer - buffer into which we will write @RETURNS : Number of bytes written. @DESCRIPTION: Dicom output routine for writing a virtual output stream into a real output stream. @METHOD : We use three watchpoints. One one the real stream to mark the end of the next PDV. One stored in the stream data structure to indicate the end of the PDU (this is relative to the end of the PDV watchpoint). One on the virtual stream to mark the end of the command or of the data. @METHOD : We use three watchpoints. One on the real stream to mark the end of the PDU. One in stream data structure to mark the end of the PDV. One is set on the virtual (dicom) stream to mark the end of the command or data. @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int dicom_output_routine(void *io_data, void *buffer, int nbytes) { unsigned char pdu_buffer[PDU_HEADER_LEN]; unsigned char header_buffer[DATA_TF_LEN]; Acr_Dicom_IO *stream_data; Acr_File *real_afp, *dicom_afp; Acr_Long pdu_length, pdv_length; long message_watchpoint, pdv_watchpoint, data_length, pdu_watchpoint; long nwritten; int total_written, bytes_to_write; Acr_Status status; int message_control_header; /* Get file pointer */ if (io_data == NULL) return 0; stream_data = (Acr_Dicom_IO *) io_data; real_afp = stream_data->real_afp; dicom_afp = stream_data->virtual_afp; /* Get distances to watchpoints */ pdv_watchpoint = get_dicom_pdv_watchpoint(dicom_afp); message_watchpoint = acr_get_io_watchpoint(dicom_afp); /* Make sure that we have not gone too far */ if (message_watchpoint < 0) { return -1; } /* Loop until we have written out the whole buffer */ total_written = 0; while (nbytes > 0) { /* Check whether we need to write out more headers */ if (pdv_watchpoint <= 0) { /* If the PDU watchpoint is <= 0, then we need to write out a PDU header. */ if (acr_get_io_watchpoint(real_afp) <= 0) { /* Figure out pdu length */ pdu_length = 0; /* Get data length */ data_length = (stream_data->writing_command ? stream_data->data_length : message_watchpoint + nbytes); /* Add in command length if needed */ if (stream_data->writing_command) { pdu_length += sizeof(header_buffer) + message_watchpoint + nbytes; } /* Add in data length if needed, making sure that we can get some data into this pdu */ if ((data_length > 0) && (pdu_length+sizeof(header_buffer) < (Acr_Long)stream_data->maximum_length)) { pdu_length += sizeof(header_buffer) + data_length; } /* Check that we haven't exceeded the maximum length */ if (pdu_length > stream_data->maximum_length) { pdu_length = stream_data->maximum_length; } /* Set the PDU watchpoint */ acr_set_io_watchpoint(real_afp, pdu_length + sizeof(pdu_buffer)); /* Write out the buffer */ pdu_buffer[0] = ACR_PDU_DATA_TF; pdu_buffer[1] = 0; PUT_LONG(&pdu_length, (Acr_Long*)&pdu_buffer[2]); status = acr_write_buffer(real_afp, pdu_buffer, sizeof(pdu_buffer), NULL); if (status != ACR_OK) return total_written; } /* If PDU watchpoint <= 0 */ /* Get info for PDV header */ message_control_header = 0; if (stream_data->writing_command) { message_control_header |= PDV_COMMAND_PDV_MASK; } /* Get length of PDV, making sure that it will fit in the PDU */ pdv_length = message_watchpoint + nbytes; pdu_watchpoint = acr_get_io_watchpoint(real_afp); if ((Acr_Long)(pdv_length + sizeof(header_buffer)) > pdu_watchpoint) { pdv_length = pdu_watchpoint - sizeof(header_buffer); } else { message_control_header |= PDV_LAST_FRAGMENT_MASK; } pdv_length += 2; /* Write out PDV header */ PUT_LONG(&pdv_length, (Acr_Long*)&header_buffer[0]); header_buffer[4] = (unsigned char) stream_data->presentation_context_id; header_buffer[5] = (unsigned char) message_control_header; status = acr_write_buffer(real_afp, header_buffer, sizeof(header_buffer), NULL); if (status != ACR_OK) return total_written; /* Set the watchpoint for this PDV */ pdv_length -= 2; if ((pdv_length <= 0) || (pdv_length > acr_get_io_watchpoint(real_afp))) return total_written; set_dicom_pdv_watchpoint(dicom_afp, pdv_length); pdv_watchpoint = pdv_length; } /* If pdv_watchpoint <= 0 */ /* Write out data */ bytes_to_write = nbytes; if (nbytes >= pdv_watchpoint) { bytes_to_write = pdv_watchpoint; } status = acr_write_buffer(real_afp, buffer, bytes_to_write, &nwritten); total_written += nwritten; if (nwritten < bytes_to_write) return total_written; nbytes -= bytes_to_write; buffer = (void *) ((char *) buffer + bytes_to_write); pdv_watchpoint -= bytes_to_write; } /* While nbytes > 0 */ /* Check for end of command part */ if (stream_data->writing_command && (message_watchpoint == 0)) { acr_set_io_watchpoint(dicom_afp, stream_data->data_length); stream_data->writing_command = FALSE; } return total_written; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_ismore @INPUT : io_data @OUTPUT : (nothing) @RETURNS : 1 if more data is waiting, 0 if not, and -1 if EOF or error. @DESCRIPTION: Routine for testing for waiting data on a dicom input stream. @METHOD : @GLOBALS : @CALLS : @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int dicom_ismore(void *io_data) { Acr_Dicom_IO *stream_data; /* Get file pointer */ if (io_data == NULL) return -1; stream_data = (Acr_Dicom_IO *) io_data; /* Call ismore function for real stream */ return acr_file_ismore(stream_data->real_afp); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/read_acr_nema.c0000644000175000000620000001252512574624760022425 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : read_acr_nema.c @DESCRIPTION: Program to read an image from an acr-nema file and write it on stdout. @METHOD : @GLOBALS : @CREATED : March 14, 1994 (Peter Neelin) @MODIFIED : * $Log: read_acr_nema.c,v $ * Revision 6.4 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.3 2004/10/29 13:08:42 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.2 2001/11/08 14:17:06 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.1 1999/10/29 17:51:54 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:19 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:35:55 neelin * Pre-release * * Revision 1.2 94/04/07 10:05:08 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.1 94/03/14 16:02:49 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 #include #include #include /* Define some constants */ #define ACR_IMAGE_EID 0x10 #define ACR_IMAGE_GID 0x7fe0 /* Define element id's */ DEFINE_ELEMENT(static, ACR_rows , 0x0028, 0x0010, US); DEFINE_ELEMENT(static, ACR_columns , 0x0028, 0x0011, US); DEFINE_ELEMENT(static, ACR_bits_allocated, 0x0028, 0x0100, US); DEFINE_ELEMENT(static, ACR_image_location, 0x0028, 0x0200, US); int main(int argc, char *argv[]) { char *pname, *filename; FILE *fp; Acr_File *afp; Acr_Group group_list; Acr_Element element; Acr_Element_Id element_id; unsigned short *image; int nrows, ncols, bits_alloc; /* Check arguments */ pname = argv[0]; if ((argc > 2) || (argc < 1)) { (void) fprintf(stderr, "Usage: %s []\n", pname); exit(EXIT_FAILURE); } /* Get file name */ if (argc == 2) filename = argv[1]; else filename = NULL; /* Open input file */ if (filename != NULL) { fp = fopen(filename, "r"); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening file %s\n", pname, filename); exit(EXIT_FAILURE); } } else { fp = stdin; } /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); (void) acr_test_dicom_file(afp); /* Read in group list up to group */ (void) acr_input_group_list(afp, &group_list, 0); /* Free the afp */ acr_file_free(afp); /* Get image size and location */ nrows = acr_find_short(group_list, ACR_rows, 0); ncols = acr_find_short(group_list, ACR_columns, 0); bits_alloc = acr_find_short(group_list, ACR_bits_allocated, 0); if ((nrows <= 0) || (ncols <= 0)) { (void) fprintf(stderr, "%s: Invalid imaged size %d x %d\n", pname, nrows, ncols); exit(EXIT_FAILURE); } if (bits_alloc != 16) { (void) fprintf(stderr, "%s: Pixels not stored as short ints\n", pname); exit(EXIT_FAILURE); } element_id = MALLOC(sizeof(*element_id)); element_id->element_id = ACR_IMAGE_EID; element_id->group_id = acr_find_short(group_list, ACR_image_location, ACR_IMAGE_GID); /* Look for image and print it out */ image = (unsigned short *) MALLOC(nrows*ncols*sizeof(*image)); element = acr_find_group_element(group_list, element_id); if (element != NULL) { if (acr_get_element_short_array(element, (long) nrows*ncols, image) != nrows*ncols) { (void) fprintf(stderr, "Incorrect image size\n"); } (void) fwrite(image, sizeof(*image), (size_t) nrows*ncols, stdout); } FREE(element_id); FREE(image); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_test.c0000644000175000000620000000436412574624760021473 0ustar stevestaff#include #include #include #define GLOBAL_ELEMENT_DEFINITION #include GLOBAL_ELEMENT(ACR_Recognition_code, 0x0, 0x10, LO); GLOBAL_ELEMENT(Shadow_Recognition_code, 0x1, 0x10, LO); GLOBAL_ELEMENT(Shadow_Command_code, 0x1, 0x18, US); int main(int argc, char *argv[]) /* ARGSUSED */ { Acr_File *afpin, *afpout; Acr_Message message; Acr_Status status; Acr_Element element; char *error_type, *error_string; afpin=acr_file_initialize(stdin, 0, acr_stdio_read); (void) acr_test_byte_order(afpin); acr_set_io_watchpoint(afpin, 102382L); #if 0 afpout=acr_file_initialize(stdout, 0, acr_stdio_write); #else afpout=acr_initialize_dicom_output(stdout, 0, acr_stdio_write); #endif error_type = "reading"; while ((status=acr_input_message(afpin, &message)) == ACR_OK) { #if 0 if ((status=acr_output_message(afpout, message)) != ACR_OK) { #else acr_set_dicom_pres_context_id(afpout, 19); if ((status=acr_output_dicom_message(afpout, message)) != ACR_OK) { #endif error_type = "writing"; break; } element = acr_find_group_element(acr_get_message_group_list(message), ACR_Recognition_code); if (element != NULL) (void) fprintf(stderr, "ACR-NEMA recognition code: '%s'\n", acr_get_element_string(element)); element = acr_find_group_element(acr_get_message_group_list(message), Shadow_Recognition_code); if (element != NULL) (void) fprintf(stderr, "Shadow recognition code: '%s'\n", acr_get_element_string(element)); element = acr_find_group_element(acr_get_message_group_list(message), Shadow_Command_code); if (element != NULL) (void) fprintf(stderr, "Shadow command code: 0x%x\n", (int) acr_get_element_short(element)); } error_string = acr_status_string(status); (void) fprintf(stderr, "Terminated while %s: %s\n", error_type, error_string); /* Free the afp */ acr_file_free(afpin); #if 0 acr_file_free(afpout); #else acr_close_dicom_file(afpout); #endif exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/value_repr.c0000644000175000000620000005121712574624760022032 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : value_repr.c @DESCRIPTION: Routines for doing acr_nema VR and value conversion operations. @METHOD : @GLOBALS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : * $Log: value_repr.c,v $ * Revision 6.5 2008-08-12 05:00:22 rotor * * large number of changes from Claude (64 bit and updates) * * Revision 6.4 2005/03/04 00:08:37 bert * Lose static and * * Revision 6.3 2004/10/29 13:08:42 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.2 2000/08/16 15:53:46 neelin * Added VR type UN (unknown) which has a length field similar to OB. * * Revision 6.1 1999/10/29 17:51:54 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/10 17:13:53 neelin * Fixed lookup of VR names so that both letters are used. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 1.2 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 1.1 1997/02/11 16:23:43 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include /* Types for VR table entries and conversion functions */ typedef struct Acr_VR_Entry Acr_VR_Entry; typedef double (*Acr_VR_Numeric_Function) (Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); typedef char * (*Acr_VR_String_Function) (Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); struct Acr_VR_Entry { Acr_VR_Type vr_code; char *vr_name; Acr_VR_Numeric_Function convert_to_numeric; Acr_VR_String_Function convert_to_string; }; /* Private functions */ static void check_table_integrity(); static Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code); static Acr_VR_Type find_vr_name(char *vr_name); static double return_zero(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double string_to_numeric(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double get_short(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double get_long(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double get_float(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double get_double(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static double guess_numeric_type(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static void extend_internal_buffer(int length); static char *return_empty_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static char *return_the_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); static char *numeric_to_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length); /* Table of VRs and conversion routines */ static Acr_VR_Entry VR_table[] = { {ACR_VR_UNKNOWN, "\0\0", guess_numeric_type, return_the_string }, {ACR_VR_AE, "AE", return_zero, return_the_string }, {ACR_VR_AS, "AS", string_to_numeric, return_the_string }, {ACR_VR_AT, "AT", get_long, numeric_to_string }, {ACR_VR_CS, "CS", string_to_numeric, return_the_string }, {ACR_VR_DA, "DA", string_to_numeric, return_the_string }, {ACR_VR_DS, "DS", string_to_numeric, return_the_string }, {ACR_VR_DT, "DT", string_to_numeric, return_the_string }, {ACR_VR_FL, "FL", get_float, numeric_to_string }, {ACR_VR_FD, "FD", get_double, numeric_to_string }, {ACR_VR_IS, "IS", string_to_numeric, return_the_string }, {ACR_VR_LO, "LO", string_to_numeric, return_the_string }, {ACR_VR_LT, "LT", string_to_numeric, return_the_string }, {ACR_VR_OB, "OB", return_zero, return_empty_string }, {ACR_VR_OW, "OW", return_zero, return_empty_string }, {ACR_VR_PN, "PN", return_zero, return_the_string }, {ACR_VR_SH, "SH", string_to_numeric, return_the_string }, {ACR_VR_SL, "SL", get_long, numeric_to_string }, {ACR_VR_SQ, "SQ", return_zero, return_empty_string }, {ACR_VR_SS, "SS", get_short, numeric_to_string }, {ACR_VR_ST, "ST", string_to_numeric, return_the_string }, {ACR_VR_TM, "TM", string_to_numeric, return_the_string }, {ACR_VR_UI, "UI", return_zero, return_the_string }, {ACR_VR_UL, "UL", get_long, numeric_to_string }, {ACR_VR_US, "US", get_short, numeric_to_string }, {ACR_VR_UN, "UN", return_zero, return_empty_string }, {ACR_VR_UT, "UT", return_zero, return_empty_string }, {ACR_VR_NUM_TYPES, NULL, NULL, NULL} }; /* Table for unknown VRs */ static Acr_VR_Entry *unknown_VR_table = NULL; static int unknown_VR_table_length = 0; static int table_integrity_checked = FALSE; /* Macros */ #define CHECK_TABLE_INTEGRITY \ {if (!table_integrity_checked) {check_table_integrity();}} /* ----------------------------- MNI Header ----------------------------------- @NAME : check_table_integrity @INPUT : (none) @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Checks the table for consistency and exits with failure if a problem is found. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void check_table_integrity() { unsigned int ientry; /* Don't do anything if check has already been performed */ if (table_integrity_checked) return; /* Check size of table */ if (sizeof(VR_table)/sizeof(VR_table[0]) != ACR_VR_NUM_TYPES+1) { (void) fprintf(stderr, "Internal error: VR table size is wrong! %lu %lu %d\n", sizeof(VR_table), sizeof(VR_table[0]), ACR_VR_NUM_TYPES); exit(EXIT_FAILURE); } /* Check each entry for the correct VR code */ for (ientry=0; ientry < ACR_VR_NUM_TYPES+1; ientry++) { if (VR_table[ientry].vr_code != ientry) { (void) fprintf(stderr, "Internal error: VR code mismatch in table\n"); exit(EXIT_FAILURE); } } /* Set flag */ table_integrity_checked = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vr_entry @INPUT : vr_code - the internal VR code to look up @OUTPUT : (none) @RETURNS : Pointer to VR table entry @DESCRIPTION: Looks up a VR table entry in the appropriate table based on the internal VR code. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_VR_Entry *get_vr_entry(Acr_VR_Type vr_code) { CHECK_TABLE_INTEGRITY; /* Check to see if we have a bogus vr_code */ if (vr_code >= (Acr_VR_Type)(ACR_VR_NUM_TYPES+unknown_VR_table_length)) { return NULL; } /* Check to see if vr_code is in standard table or not */ if (vr_code < ACR_VR_NUM_TYPES) { return &VR_table[vr_code]; } else { return &unknown_VR_table[vr_code-ACR_VR_NUM_TYPES]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : find_vr_name @INPUT : vr_name - the 2-letter VR name to look up @OUTPUT : (none) @RETURNS : Internal VR code @DESCRIPTION: Looks up a VR table entry in the appropriate table based on the 2-letter VR name. If nothing is found, -1 is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #define UNKNOWN_VR ((Acr_VR_Type) -1) static Acr_VR_Type find_vr_name(char *vr_name) { int ientry; CHECK_TABLE_INTEGRITY; /* Loop through standard VR table */ for (ientry=0; ientry < ACR_VR_NUM_TYPES; ientry++) { if ((VR_table[ientry].vr_name[0] == vr_name[0]) && (VR_table[ientry].vr_name[1] == vr_name[1])) { return (Acr_VR_Type) ientry; } } /* Loop through unknown VR table */ for (ientry=0; ientry < unknown_VR_table_length; ientry++) { if ((unknown_VR_table[ientry].vr_name[0] == vr_name[0]) && (unknown_VR_table[ientry].vr_name[1] == vr_name[1])) { return (Acr_VR_Type) ientry+ACR_VR_NUM_TYPES; } } return UNKNOWN_VR; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_vr_name @INPUT : vr_code - the internal VR code @OUTPUT : (none) @RETURNS : VR name @DESCRIPTION: Gets the name corresponding to a VR code, returning a pointer to the table string (don't free it!). NULL is returned if the code is not found. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_get_vr_name(Acr_VR_Type vr_code) { Acr_VR_Entry *entry; if ((entry = get_vr_entry(vr_code)) == NULL) { return NULL; } return entry->vr_name; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_test_vr_name @INPUT : vr_name - the 2-letter VR name to look up @OUTPUT : (none) @RETURNS : TRUE if vr_name is found @DESCRIPTION: Checks to see if VR name is standard. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_test_vr_name(char *vr_name) { Acr_VR_Type vr_code; vr_code = find_vr_name(vr_name); return (vr_code != UNKNOWN_VR && vr_code < ACR_VR_NUM_TYPES); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_lookup_vr_name @INPUT : vr_name - the 2-letter VR name to look up @OUTPUT : (none) @RETURNS : Internal VR code @DESCRIPTION: Looks up a VR name in the table. If it does not exist, the name is added to the unknown table. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Acr_VR_Type acr_lookup_vr_name(char *vr_name) { Acr_VR_Type vr_code; int ientry; char *string; /* Look up the name and return the matching code if found */ vr_code = find_vr_name(vr_name); if (vr_code != UNKNOWN_VR) { return vr_code; } /* If name is not found, add it to the unknown table */ if (unknown_VR_table_length <= 0) { unknown_VR_table_length = 1; unknown_VR_table = MALLOC((size_t) unknown_VR_table_length * sizeof(*unknown_VR_table)); } else { unknown_VR_table_length++; unknown_VR_table = REALLOC(unknown_VR_table, (size_t) unknown_VR_table_length * sizeof(*unknown_VR_table)); } /* Fill in the entry */ ientry = unknown_VR_table_length-1; unknown_VR_table[ientry].vr_code = ientry + ACR_VR_NUM_TYPES; string = MALLOC(3); string[0] = vr_name[0]; string[1] = vr_name[1]; string[2] = '\0'; unknown_VR_table[ientry].vr_name = string; unknown_VR_table[ientry].convert_to_numeric = return_zero; unknown_VR_table[ientry].convert_to_string = return_empty_string; /* Return the code */ return unknown_VR_table[ientry].vr_code; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_numeric_vr @INPUT : vr_code - the internal VR code byte_order - ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN data - the data to convert data_length - the length of the data @OUTPUT : (none) @RETURNS : The converted value. @DESCRIPTION: Convert a value to a number according to its VR. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ double acr_get_numeric_vr(Acr_VR_Type vr_code, Acr_byte_order byte_order, char *data, long data_length) { Acr_VR_Entry *entry; if ((entry = get_vr_entry(vr_code)) == NULL) { return return_zero(entry, byte_order, data, data_length); } return entry->convert_to_numeric(entry, byte_order, data, data_length); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_string_vr @INPUT : vr_code - the internal VR code byte_order - ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN data - the data to convert data_length - the length of the data @OUTPUT : (none) @RETURNS : The converted value. @DESCRIPTION: Convert a value to a string according to its VR. The pointer returned is to an internal buffer. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_get_string_vr(Acr_VR_Type vr_code, Acr_byte_order byte_order, char *data, long data_length) { Acr_VR_Entry *entry; if ((entry = get_vr_entry(vr_code)) == NULL) { return return_empty_string(entry, byte_order, data, data_length); } return entry->convert_to_string(entry, byte_order, data, data_length); } /* ----------------------------- MNI Header ----------------------------------- @NAME : return_zero string_to_numeric get_short get_long get_float get_double @INPUT : vr_entry - pointer to VR table entry byte_order - ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN data - a pointer to the actual data data_length - number of bytes in the data @OUTPUT : (none) @RETURNS : Numeric equivalent of data @DESCRIPTION: Routines to convert values from a DICOM form to a number @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ static double return_zero(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { return 0.0; } /* ARGSUSED */ static double string_to_numeric(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { return atof((char *) data); } /* ARGSUSED */ static double get_short(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { Acr_Short value; if (data_length == ACR_SIZEOF_SHORT) { acr_get_short(byte_order, 1, data, &value); return (double) value; } else { return 0.0; } } /* ARGSUSED */ static double get_long(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { Acr_Long value; if (data_length == ACR_SIZEOF_LONG) { acr_get_long(byte_order, 1, data, &value); return (double) value; } else { return 0.0; } } /* ARGSUSED */ static double get_float(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { Acr_Float value; if (data_length == ACR_SIZEOF_FLOAT) { acr_get_float(byte_order, 1, data, &value); return (double) value; } else { return 0.0; } } /* ARGSUSED */ static double get_double(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { Acr_Double value; if (data_length == ACR_SIZEOF_DOUBLE) { acr_get_double(byte_order, 1, data, &value); return (double) value; } else { return 0.0; } } /* ARGSUSED */ static double guess_numeric_type(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { switch (data_length) { case ACR_SIZEOF_SHORT: return get_short(vr_entry, byte_order, data, data_length); break; case ACR_SIZEOF_LONG: return get_long(vr_entry, byte_order, data, data_length); break; default: return string_to_numeric(vr_entry, byte_order, data, data_length); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : return_empty_string return_the_string numeric_to_string @INPUT : vr_entry - pointer to VR table entry byte_order - ACR_BIG_ENDIAN or ACR_LITTLE_ENDIAN data - a pointer to the actual data data_length - number of bytes in the data @OUTPUT : (none) @RETURNS : String equivalent of data @DESCRIPTION: Routines to convert values from a DICOM form to a string. If temporary space is needed, an internal buffer is used. @METHOD : @GLOBALS : @CALLS : @CREATED : January 31, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #define LINE_LENGTH 256 static char *internal_string_buffer = NULL; static int length_of_internal_string = 0; static void extend_internal_buffer(int length) { if (length+1 > length_of_internal_string) { length_of_internal_string = length+1; if (internal_string_buffer == NULL) internal_string_buffer = MALLOC((size_t) length_of_internal_string); else internal_string_buffer = REALLOC(internal_string_buffer, (size_t) length_of_internal_string); } } /* ARGSUSED */ static char *return_empty_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { extend_internal_buffer(LINE_LENGTH); internal_string_buffer[0] = '\0'; return internal_string_buffer; } /* ARGSUSED */ static char *return_the_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { return (char *) data; } /* ARGSUSED */ static char *numeric_to_string(Acr_VR_Entry *vr_entry, Acr_byte_order byte_order, char *data, long data_length) { extend_internal_buffer(LINE_LENGTH); (void) sprintf(internal_string_buffer, "%.6g", vr_entry->convert_to_numeric(vr_entry, byte_order, data, data_length)); return internal_string_buffer; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/dicom_test.c0000644000175000000620000000221212574624760022007 0ustar stevestaff#include #include #include #include #if 0 #define WRITING #endif int main(int argc, char *argv[]) /* ARGSUSED */ { Acr_File *afpin; Acr_Message message; Acr_Status status; char *error_type, *error_string; afpin=acr_initialize_dicom_input(stdin, 0, acr_stdio_read); #ifdef WRITING afpout=acr_initialize_dicom_output(stdout, 0, acr_stdio_write); #endif error_type = "reading"; /* Loop over messages */ while ((status=acr_input_dicom_message(afpin, &message)) == ACR_OK) { /* Dump the values */ acr_dump_group_list(stderr, acr_get_message_group_list(message)); #ifdef WRITING /* Write out message */ if ((status=acr_output_dicom_message(afpout, message)) != ACR_OK) { error_type = "writing"; break; } #endif acr_delete_message(message); } error_string = acr_status_string(status); (void) fprintf(stderr, "Terminated while %s: %s\n", error_type, error_string); /* Free the afp */ acr_close_dicom_file(afpin); #ifdef WRITING acr_close_dicom_file(afpout); #endif exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/Makefile.am0000644000175000000620000000220012574624760021542 0ustar stevestaffAUTOMAKE_OPTIONS = check-news # This is the only header installed in the "includedir" # include_HEADERS = acr_nema.h # All other headers are installed in this subdirectory. # include_acr_nemadir = $(includedir)/acr_nema include_acr_nema_HEADERS = \ acr_nema/acr_io.h \ acr_nema/dicom_client_routines.h \ acr_nema/dicom_network.h \ acr_nema/element.h \ acr_nema/file_io.h \ acr_nema/group.h \ acr_nema/message.h \ acr_nema/value_repr.h # Libraries which must be built and installed. # lib_LTLIBRARIES = libacr_nema.la bin_PROGRAMS = \ acr_test \ dump_acr_nema \ extract_acr_nema \ read_acr_nema \ dicom_test \ copy_acr_nema noinst_PROGRAMS = \ sample_dicom_client LDADD = libacr_nema.la acr_test_SOURCES = acr_test.c dump_acr_nema_SOURCES = dump_acr_nema.c extract_acr_nema_SOURCES = extract_acr_nema.c read_acr_nema_SOURCES = read_acr_nema.c dicom_test_SOURCES = dicom_test.c copy_acr_nema_SOURCES = copy_acr_nema.c libacr_nema_la_LDFLAGS = -version-info 1:0:1 libacr_nema_la_SOURCES = \ acr_io.c \ dicom_client_routines.c \ dicom_network.c \ element.c \ file_io.c \ globals.c \ group.c \ message.c \ value_repr.c minc-tools-2.3.00+dfsg/conversion/Acr_nema/dump_acr_nema.c0000644000175000000620000002300212574624760022447 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dump_acr_nema.c @DESCRIPTION: Program to dump the contents of an acr-nema file. @METHOD : @GLOBALS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : * $Log: dump_acr_nema.c,v $ * Revision 6.8 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.7 2005/03/11 22:19:59 bert * add '-t' option to parse files which contain lists of field names with corresponding group and element id's. * * Revision 6.6 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.5 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.4 2000/05/01 17:54:45 neelin * Fixed handling of test for byte order. * * Revision 6.3 2000/05/01 13:59:55 neelin * Added -e option to allow reading data streams with explicit VR. * * Revision 6.2 2000/04/28 15:02:01 neelin * Added more general argument processing (but not with ParseArgv). * Added support for ignoring non-fatal protocol errors. * Added support for user-specified byte-order. * * Revision 6.1 1999/10/29 17:51:51 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/10 17:14:38 neelin * Added more status codes and function to return status string. * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.1 1995/02/06 14:12:55 neelin * Added argument to specify maximum group id to dump. * * Revision 2.0 94/09/28 10:36:09 neelin * Release of minc version 0.2 * * Revision 1.6 94/09/28 10:35:53 neelin * Pre-release * * Revision 1.5 94/05/18 08:48:05 neelin * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT. * * Revision 1.4 94/04/07 10:04:58 neelin * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs * to that or ACR_OTHER_ERROR. * Added #ifdef lint to DEFINE_ELEMENT. * * Revision 1.3 93/11/25 10:35:33 neelin * Added byte-order test and file free. * * Revision 1.2 93/11/24 12:05:00 neelin * Write output to stdout instead of stderr. * * Revision 1.1 93/11/24 11:25:01 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 #include #include #include #include #define N_NAME 31 struct table_element { struct table_element *next; unsigned short el_id; char name[N_NAME+1]; }; struct table_group { struct table_group *next; struct table_element *list; unsigned short grp_id; }; struct table_group *_head; char *get_name(unsigned short grp_id, unsigned short el_id) { struct table_group *tg_ptr; struct table_element *te_ptr; for (tg_ptr = _head; tg_ptr; tg_ptr = tg_ptr->next) { if (tg_ptr->grp_id == grp_id) { for (te_ptr = tg_ptr->list; te_ptr; te_ptr = te_ptr->next) { if (te_ptr->el_id == el_id) { return (te_ptr->name); } } } } return (NULL); } void parse_table(char *filename) { FILE *fp; char line[1024]; char name[1024]; unsigned int el_id; /* Must be int for sscanf */ unsigned int grp_id; /* Must be int for sscanf */ char vr[1024]; struct table_element *te_ptr; struct table_group *tg_ptr; fp = fopen(filename, "r"); if (fp == NULL) { return; } while (fgets(line, sizeof(line), fp)) { if (!isalnum(line[0])) { continue; /* Ignore */ } if (sscanf(line, "%s %x %x %s\n", name, &grp_id, &el_id, vr) != 4) { continue; } for (tg_ptr = _head; tg_ptr; tg_ptr = tg_ptr->next) { if (tg_ptr->grp_id == grp_id) { break; } } if (tg_ptr == NULL) { tg_ptr = malloc(sizeof(struct table_group)); tg_ptr->next = _head; _head = tg_ptr; tg_ptr->list = NULL; tg_ptr->grp_id = (unsigned short) grp_id; } te_ptr = malloc(sizeof(struct table_element)); te_ptr->el_id = (unsigned short) el_id; te_ptr->next = tg_ptr->list; tg_ptr->list = te_ptr; strncpy(te_ptr->name, name, N_NAME); } fclose(fp); } #define UNKNOWN_VR_ENCODING ((Acr_VR_encoding_type) -1) int main(int argc, char *argv[]) { char *pname; char *file = NULL; char *maxidstr = NULL; int ignore_errors = FALSE; Acr_byte_order byte_order = ACR_UNKNOWN_ENDIAN; Acr_VR_encoding_type vr_encoding = UNKNOWN_VR_ENCODING; FILE *fp; Acr_File *afp; Acr_Group group_list; Acr_Status status; char *status_string; int maxid; char *ptr; int iarg, argcounter; char *arg; char *usage = "Usage: %s [-h] [-i] [-b] [-l] [-e] [-t ] [ []]\n"; /* Check arguments */ pname = argv[0]; argcounter = 0; for (iarg=1; iarg < argc; iarg++) { arg = argv[iarg]; if ((arg[0] == '-') && (arg[1] != '\0')) { if (arg[2] != '\0') { (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } switch (arg[1]) { case 't': if (iarg < argc - 1) { parse_table(argv[++iarg]); _acr_name_proc = get_name; break; } /* Fall through */ case 'h': (void) fprintf(stderr, "Options:\n"); (void) fprintf(stderr, " -h:\tPrint this message\n"); (void) fprintf(stderr, " -t
:\tUse table to decode element names\n"); (void) fprintf(stderr, " -i:\tIgnore protocol errors\n"); (void) fprintf(stderr, " -b:\tAssume big-endian data\n"); (void) fprintf(stderr, " -l:\tAssume little-endian data\n"); (void) fprintf(stderr, " -e:\tAssume explicit VR encoding\n\n"); (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); break; case 'i': ignore_errors = TRUE; break; case 'l': byte_order = ACR_LITTLE_ENDIAN; break; case 'b': byte_order = ACR_BIG_ENDIAN; break; case 'e': vr_encoding = ACR_EXPLICIT_VR; break; default: (void) fprintf(stderr, "Unrecognized option %s\n", arg); exit(EXIT_FAILURE); } } else { switch (argcounter) { case 0: file = arg; break; case 1: maxidstr = arg; break; default: (void) fprintf(stderr, usage, pname); exit(EXIT_FAILURE); } argcounter++; } } /* Open input file */ if ((file != NULL) && (strcmp(file, "-") != 0)) { fp = fopen(file, "r"); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening file %s\n", pname, file); exit(EXIT_FAILURE); } } else { fp = stdin; } /* Look for max group id */ if (maxidstr != NULL) { maxid = strtol(maxidstr, &ptr, 0); if (ptr == maxidstr) { (void) fprintf(stderr, "%s: Error in max group id (%s)\n", pname, maxidstr); exit(EXIT_FAILURE); } } else { maxid = 0; } /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); acr_set_ignore_errors(afp, ignore_errors); (void) acr_test_dicom_file(afp); if (byte_order != ACR_UNKNOWN_ENDIAN) { acr_set_byte_order(afp, byte_order); } if (vr_encoding != UNKNOWN_VR_ENCODING) { acr_set_vr_encoding(afp, vr_encoding); } /* Read in group list */ status = acr_input_group_list(afp, &group_list, maxid); /* Free the afp */ acr_file_free(afp); /* Dump the values */ acr_dump_group_list(stdout, group_list); /* Print status information */ if ((status != ACR_END_OF_INPUT) && (status != ACR_OK)) { status_string = acr_status_string(status); (void) fprintf(stderr, "Finished with status '%s'\n", status_string); } exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/Acr_nema/file_io.c0000644000175000000620000007055512574624760021302 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : file_io.c @DESCRIPTION: Routines for doing io from acr_nema code. @METHOD : @GLOBALS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : * $Log: file_io.c,v $ * Revision 6.10 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.9 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.8 2005/05/19 20:57:20 bert * Fixes for Windows, ported from 1.X branch * * Revision 6.7.2.2 2005/05/16 22:39:30 bert * Insert conditionals to get it building under Windows * * Revision 6.7.2.1 2005/05/12 21:15:30 bert * Initial checkin * * Revision 6.7 2005/03/04 00:09:00 bert * Lose static and * * Revision 6.6 2005/02/16 19:22:32 bert * Autoconfiscation * * Revision 6.5 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.4 2001/11/08 14:17:05 neelin * Added acr_test_dicom_file to allow reading of DICOM part 10 format * files. This function also calls acr_test_byte_order to set up the stream * properly and can be used as a direct replacement for that function. * This set of changes does NOT include the ability to write part 10 files. * * Revision 6.3 2000/05/17 20:17:48 neelin * Added mechanism to allow testing of input streams for more data through * function acr_file_ismore. * This is used in dicom_client_routines to allow asynchronous transfer * of data, with testing for more input done before sending new messages. * Previous use of select for this was misguided, since select may report that * no data is waiting on the file descriptor while data is store in the file * pointer buffer (or Acr file pointer buffer). * * Revision 6.2 1999/10/29 17:51:53 neelin * Fixed Log keyword * * Revision 6.1 1998/11/11 16:26:49 neelin * Increased default buffer size to 64K. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.1 1995/02/08 21:16:06 neelin * Changes to make irix 5 lint happy. * * Revision 2.0 1994/09/28 10:36:13 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:35:42 neelin * Pre-release * * Revision 1.6 94/04/08 10:34:47 neelin * Added include of string.h * * Revision 1.5 94/04/08 10:32:00 neelin * Fixed io tracing. * * Revision 1.4 93/11/25 10:36:14 neelin * Added file free and ungetc * * Revision 1.3 93/11/23 16:02:12 neelin * Corrected header comment in acr_file_flush. * * Revision 1.2 93/11/23 13:21:42 neelin * Added fflush for stdio write routine. * * Revision 1.1 93/11/10 10:33:22 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include /* Define some constants */ #define ACR_MAX_BUFFER_LENGTH (64*1024) #define ACR_BUFFER_MARGIN 256 #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif /* Stream type */ #define ACR_UNKNOWN_STREAM 0 #define ACR_READ_STREAM 1 #define ACR_WRITE_STREAM 2 #if HAVE_MKSTEMP /* Stuff for input and output tracing */ static char *Input_trace_file = "acr_file_input_XXXXXX"; static char *Output_trace_file = "acr_file_output_XXXXXX"; #endif /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_enable_trace @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Turns on tracing for an i/o stream @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_enable_trace(Acr_File *afp) { afp->do_trace = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_disable_trace @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Turns off tracing for an i/o stream @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_disable_trace(Acr_File *afp) { afp->do_trace = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_initialize @INPUT : io_data - pointer to data for read and write routines maxlength - maximum length for a single read or write (zero or negative means use internal maximum). io_routine - routine to read or write data @OUTPUT : (none) @RETURNS : pointer to Acr_File structure created @DESCRIPTION: Sets up the routines for reading/writing from an input or output stream. A given Acr_File can only do input or output, not both. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ Acr_File *acr_file_initialize(void *io_data, int maxlength, Acr_Io_Routine io_routine) { Acr_File *afp; /* Check that an io routine is given */ if (io_routine == NULL) { return NULL; } /* Allocate the strcture */ afp = malloc(sizeof(*afp)); /* Initialize fields */ afp->io_data = io_data; afp->io_routine = io_routine; afp->ismore_function = NULL; if ((maxlength < ACR_MAX_BUFFER_LENGTH) && (maxlength > 0)) afp->maxlength = maxlength; else afp->maxlength = ACR_MAX_BUFFER_LENGTH; afp->stream_type = ACR_UNKNOWN_STREAM; afp->buffer_length = ACR_MAX_BUFFER_LENGTH+ACR_BUFFER_MARGIN; afp->reached_eof = FALSE; afp->watchpoint_set = FALSE; afp->bytes_to_watchpoint = 0; afp->client_data = NULL; /* Allocate the buffer */ afp->start = malloc((size_t) afp->buffer_length); /* Set ptr and end to start so that we can detect beginning of i/o */ afp->length = 0; afp->end = afp->start; afp->ptr = afp->end; /* Set the trace file pointer to null */ afp->do_trace = FALSE; afp->tracefp = NULL; return afp; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_free @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees the Acr_File structure, flushing the buffer if needed. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_free(Acr_File *afp) { if (afp != NULL) { if (afp->stream_type == ACR_WRITE_STREAM) { (void) acr_file_flush(afp); } if (afp->start != NULL) { free(afp->start); } if (afp->tracefp != NULL) { (void) fclose(afp->tracefp); } if (afp->client_data != NULL) { free(afp->client_data); } free(afp); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_reset @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Resets the input or output stream, discarding anything that was buffered. Any watchpoint is unset. The eof flag is unset. @METHOD : @GLOBALS : @CALLS : @CREATED : February 18, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_reset(Acr_File *afp) { afp->watchpoint_set = FALSE; afp->bytes_to_watchpoint = 0; afp->length = 0; afp->end = afp->start; afp->ptr = afp->end; afp->reached_eof = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_set_ismore_function @INPUT : afp ismore_function @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Sets the function to be used for testing if there is more data waiting on an input stream. This must be set if acr_file_ismore is to be used. The ismore function should return 1 if there is data waiting, 0 if there is none, and -1 if EOF is reached or an error occurs. @METHOD : @GLOBALS : @CALLS : @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_set_ismore_function(Acr_File *afp, Acr_Ismore_Function ismore_function) { afp->ismore_function = ismore_function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_set_eof @INPUT : afp @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Tells the input or output stream that it has reached the end of file so that no more data will be read or written. This can be useful for preventing further reading or writing if an alarm goes off, for example. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_set_eof(Acr_File *afp) { afp->reached_eof = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_set_client_data @INPUT : afp client_data - pointer to data for client use @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Associates some client data with an io stream. The pointer is assumed to point to data that should be freed when the stream is closed. Note that this mechanism is designed only to be used by the acr_io routines and is not a general-purpose mechanism. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_file_set_client_data(Acr_File *afp, void *client_data) { if (afp->client_data != NULL) { free(afp->client_data); } afp->client_data = client_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_get_client_data @INPUT : afp @OUTPUT : (none) @RETURNS : pointer to client data @DESCRIPTION: Gets the pointer to client data for the given input stream. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *acr_file_get_client_data(Acr_File *afp) { return afp->client_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_read_more @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : The next character. @DESCRIPTION: Gets more input. If the watchpoint is reached, then EOF is returned. This routine will not read past the watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ int acr_file_read_more(Acr_File *afp) { int nread, ichar; char trace_file[128]; unsigned char *margin_ptr; long byte_shift, bytes_to_read, watchpoint_distance; /* Check the pointer */ if (afp == NULL) { return EOF; } /* Check whether we really need more */ if (afp->ptr < afp->end) { return (int) *(afp->ptr++); } /* Check for EOF */ if (afp->reached_eof) return EOF; /* Check the stream type */ switch (afp->stream_type) { case ACR_UNKNOWN_STREAM: afp->stream_type = ACR_READ_STREAM; break; case ACR_READ_STREAM: break; case ACR_WRITE_STREAM: default: return EOF; } /* Work out the amount to read */ bytes_to_read = afp->maxlength; if (afp->watchpoint_set) { watchpoint_distance = (afp->start + afp->bytes_to_watchpoint - afp->end); if (watchpoint_distance <= 0) { return EOF; } else if (watchpoint_distance < bytes_to_read) { bytes_to_read = watchpoint_distance; } } /* Check whether we need to start a new buffer for next read */ if ((afp->end + bytes_to_read) > (afp->start + afp->buffer_length)) { /* Check that things are consistent */ if ((afp->end - ACR_BUFFER_MARGIN) <= afp->start) { (void) fprintf(stderr, "Internal error copying afp buffer margin\n"); exit(EXIT_FAILURE); } /* Copy the data down from the top of the buffer */ margin_ptr = afp->end - ACR_BUFFER_MARGIN; for (ichar=0; ichar < ACR_BUFFER_MARGIN; ichar++) { afp->start[ichar] = margin_ptr[ichar]; } /* Update the structure */ byte_shift = margin_ptr - afp->start; afp->end = afp->start + ACR_BUFFER_MARGIN; afp->ptr = afp->end; afp->length = afp->end - afp->start; if (afp->watchpoint_set) { afp->bytes_to_watchpoint -= byte_shift; } } /* Read in another buffer-full */ nread=afp->io_routine(afp->io_data, afp->end, bytes_to_read); /* Do tracing */ if (afp->do_trace) { if (afp->tracefp == NULL) { #if HAVE_MKSTEMP (void) strcpy(trace_file, Input_trace_file); afp->tracefp = fdopen(mkstemp(trace_file), "w"); #elif HAVE_TMPNAM tmpnam(trace_file); afp->tracefp = fopen(trace_file, "w"); #else #error Must have mkstemp() or tmpnam() available. #endif if (afp->tracefp != NULL) { (void) fprintf(stderr, "Opened input trace file %s.\n", trace_file); (void) fflush(stderr); } else { (void) fprintf(stderr, "Error opening input trace file %s.\n", trace_file); (void) fflush(stderr); } } if (nread > 0) { (void) fwrite(afp->end, sizeof(char), nread, afp->tracefp); (void) fflush(afp->tracefp); } } /* Check for EOF */ if (nread <= 0) { afp->reached_eof = TRUE; return EOF; } /* Set up variables */ afp->end += nread; afp->length += nread; /* Return the next value */ return (int) *(afp->ptr++); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_write_more @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : The character or EOF. @DESCRIPTION: Writes out the buffer and the character. This routine will write past the watchpoint, but will flush the buffer when it reaches the watchpoint. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ int acr_file_write_more(Acr_File *afp, int character) { /* Check the pointer */ if (afp == NULL) { return EOF; } /* Check whether we need to flush the buffer */ if (afp->ptr < afp->end) { return (int) (*(afp->ptr++) = (unsigned char) character); } /* Flush the buffer */ if (acr_file_flush(afp) == EOF) { return EOF; } /* Write the character into the buffer */ return (int) (*(afp->ptr++) = (unsigned char) character); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_flush @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : EOF if an error occurs, otherwise 0. @DESCRIPTION: Flushes the buffer. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ int acr_file_flush(Acr_File *afp) { int length, nwritten; char trace_file[128]; /* Check the pointer */ if (afp == NULL) { return EOF; } /* Check for EOF */ if (afp->reached_eof) return EOF; /* Check the stream type */ switch (afp->stream_type) { case ACR_UNKNOWN_STREAM: afp->stream_type = ACR_WRITE_STREAM; break; case ACR_WRITE_STREAM: break; case ACR_READ_STREAM: default: return EOF; } /* Check for something to write */ length = afp->ptr - afp->start; if (length > 0) { /* Do trace, if needed */ if (afp->do_trace) { if (afp->tracefp == NULL) { #if HAVE_MKSTEMP (void) strcpy(trace_file, Output_trace_file); afp->tracefp = fdopen(mkstemp(trace_file), "w"); #elif HAVE_TMPNAM tmpnam(trace_file); afp->tracefp = fopen(trace_file, "w"); #else #error Must have mkstemp() or tmpnam() available. #endif if (afp->tracefp != NULL) { (void) fprintf(stderr, "Opened output trace file %s.\n", trace_file); (void) fflush(stderr); } else { (void) fprintf(stderr, "Error opening output trace file %s.\n", trace_file); (void) fflush(stderr); } } (void) fwrite(afp->start, sizeof(char), length, afp->tracefp); (void) fflush(afp->tracefp); } /* Write the data */ nwritten = afp->io_routine(afp->io_data, afp->start, length); if (nwritten != length) { (void) fprintf(stderr, "Output error: wrote only %d bytes of %d\n", nwritten, length); afp->reached_eof = TRUE; return EOF; } } /* Reset the buffer */ if (afp->watchpoint_set) { afp->bytes_to_watchpoint -= length; } afp->ptr = afp->start; afp->length = afp->maxlength; if ((afp->bytes_to_watchpoint > 0) && (afp->bytes_to_watchpoint < afp->length)) { afp->length = afp->bytes_to_watchpoint; } afp->end = afp->start + afp->length; return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_ungetc @INPUT : c - character to unget afp - Acr_File pointer @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Puts a character back into the input stream. This command is fragile - it will only work if it can back up on the current buffer. @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : February 5, 1997 (P.N.) ---------------------------------------------------------------------------- */ int acr_ungetc(int c, Acr_File *afp) { /* Check the pointer */ if (afp == NULL) { return EOF; } /* Check the stream type */ if (afp->stream_type != ACR_READ_STREAM) { return EOF; } /* Check to see if we can put the character back */ if (afp->ptr > afp->start) { afp->ptr--; *afp->ptr = (unsigned char) c; } else { return EOF; } return (int) *afp->ptr; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_get_io_data @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : pointer to io data @DESCRIPTION: Gets back pointer that was passed in to acr_file_initialize. @METHOD : @GLOBALS : @CALLS : @CREATED : February 17, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *acr_file_get_io_data(Acr_File *afp) { return afp->io_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_io_watchpoint @INPUT : afp - Acr_File pointer bytes_to_watchpoint - number of bytes from current position to watchpoint @OUTPUT : (none) @RETURNS : nothing @DESCRIPTION: Sets a watchpoint in the i/o stream relative to the current input position. If bytes_to_watchpoint is equal to ACR_NO_WATCHPOINT, then the watchpoint is unset. @METHOD : @GLOBALS : @CALLS : @CREATED : February 5, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_io_watchpoint(Acr_File *afp, long bytes_to_watchpoint) { if (afp == NULL) return; /* Unset watchpoint */ if (bytes_to_watchpoint == ACR_NO_WATCHPOINT) { afp->watchpoint_set = FALSE; afp->bytes_to_watchpoint = ACR_NO_WATCHPOINT; } /* Set watchpoint */ else { afp->watchpoint_set = TRUE; afp->bytes_to_watchpoint = bytes_to_watchpoint + afp->ptr - afp->start; /* For writing, check if we need to move the end of the buffer to force a flush at the watchpoint */ if ((afp->stream_type == ACR_WRITE_STREAM) && (bytes_to_watchpoint >= 0) && (afp->bytes_to_watchpoint < afp->length)) { afp->length = afp->bytes_to_watchpoint; afp->end = afp->start + afp->length; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_io_watchpoint @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : number of bytes from current position to watchpoint @DESCRIPTION: Checks the watchpoint and returns the number of bytes between the current position and the watchpoint. If the current position is past the watchpoint, then a negative number is returned. If the watchpoint is not set, then ACR_NO_WATCHPOINT is returned. This constant is a very large positive number (ie. the watchpoint is always in the future). @METHOD : @GLOBALS : @CALLS : @CREATED : February 5, 1997 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ long acr_get_io_watchpoint(Acr_File *afp) { if ((afp == NULL) || !afp->watchpoint_set) return ACR_NO_WATCHPOINT; return (afp->start + afp->bytes_to_watchpoint - afp->ptr); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_file_ismore @INPUT : afp - Acr_File pointer @OUTPUT : (none) @RETURNS : FALSE if no data is waiting on the input stream, but the ismore function has been set for that stream, and the stream is otherwise okay. TRUE in other cases (data waiting, EOF reached, no ismore function set, invalid file pointer). @DESCRIPTION: Checks to see if more data is waiting in the buffer, or calls the io-specific function to test whether more data is available. This call should be non-blocking, but that will depend on the implementation of the io ismore function. The io-specific ismore function should return 1 if there is data waiting, 0 is there is none and -1 if EOF is reached or error occurs. @METHOD : @GLOBALS : @CALLS : @CREATED : May 17, 2000 (P.N.) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_file_ismore(Acr_File *afp) { int retval; /* Check that the ismore function is set */ if (afp->ismore_function == NULL) return TRUE; /* Check that we have a read stream */ switch (afp->stream_type) { case ACR_UNKNOWN_STREAM: afp->stream_type = ACR_READ_STREAM; break; case ACR_READ_STREAM: break; case ACR_WRITE_STREAM: default: return TRUE; } /* Check if there is data in the buffer */ if (afp->ptr < afp->end) { return TRUE; } /* Call the io ismore function */ retval = afp->ismore_function(afp->io_data); /* Check for and end of file condition */ if (retval < 0) { acr_file_set_eof(afp); return TRUE; } else if (retval > 0) { return TRUE; } else { return FALSE; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_stdio_read @INPUT : io_data - should be a FILE * pointer nbytes - number of bytes to read @OUTPUT : buffer - buffer into which we will read @RETURNS : Number of bytes read. @DESCRIPTION: Acr io routine for reading from a stdio FILE pointer @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_stdio_read(void *io_data, void *buffer, int nbytes) { FILE *fp; int nread; /* Get file pointer */ if (io_data == NULL) return 0; fp = (FILE *) io_data; /* Read the data */ nread = fread(buffer, sizeof(char), (size_t) nbytes, fp); if (nread < 0) nread = 0; return nread; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_stdio_write @INPUT : io_data - should be a FILE * pointer buffer - buffer from which we will write nbytes - number of bytes to write @OUTPUT : (nothing) @RETURNS : Number of bytes written. @DESCRIPTION: Acr io routine for writing to a stdio FILE pointer @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_stdio_write(void *io_data, void *buffer, int nbytes) { FILE *fp; int nwritten; /* Get file pointer */ if (io_data == NULL) return 0; fp = (FILE *) io_data; /* Write the data */ nwritten = fwrite(buffer, sizeof(char), (size_t) nbytes, fp); if (nwritten < 0) nwritten = 0; /* Flush the buffer */ if (nwritten > 0) { (void) fflush(fp); } return nwritten; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_stdio_ismore @INPUT : io_data - should be a FILE * pointer @OUTPUT : (nothing) @RETURNS : 1 if more data is waiting, 0 if not, and -1 if EOF or error. @DESCRIPTION: Acr io routine for testing for waiting data on a stdio input FILE pointer. This function is non-blocking. @METHOD : @GLOBALS : @CALLS : @CREATED : May 17, 2000 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_stdio_ismore(void *io_data) { #ifdef O_NONBLOCK FILE *fp; int val; int old_flags, new_flags; int fd; /* Get file pointer */ if (io_data == NULL) return -1; fp = (FILE *) io_data; /* Set non-blocking i/o */ fd = fileno(fp); old_flags = fcntl(fd, F_GETFL, 0); new_flags = old_flags | O_NONBLOCK; if (fcntl(fd, F_SETFL, new_flags) < 0) return -1; /* Read in a character */ val = getc(fp); /* Reset to old flags */ if (fcntl(fd, F_SETFL, old_flags) < 0) return -1; /* Test the return value to see if anything is waiting */ if (val != EOF) { if (ungetc(val, fp) == EOF) return -1; return 1; } else if (feof(fp)) { return -1; } else if (ferror(fp) && (errno != EAGAIN)) { return -1; } #endif /* O_NONBLOCK defined */ return 0; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/Make_acrdefs0000644000175000000620000000022012574624760021775 0ustar stevestaff# Acr-nema library ACR_LIB = acrnema CC_ACR_LIB = $(ACR_LIB_DIR)/lib$(ACR_LIB).a LINT_ACR_LIB = $(ACR_LIB_DIR)/llib-l$(ACR_LIB).ln minc-tools-2.3.00+dfsg/conversion/Acr_nema/README0000644000175000000620000000000012574624760020362 0ustar stevestaffminc-tools-2.3.00+dfsg/conversion/Acr_nema/dicom_client_routines.c0000644000175000000620000015243312574624760024251 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_client_routines.c @DESCRIPTION: High-level routines to simplify the job of connecting to a dicom server. @GLOBALS : @CREATED : May 6, 1997 (Peter Neelin) @MODIFIED : * $Log: dicom_client_routines.c,v $ * Revision 6.24 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.23 2008/01/17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.22 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.21 2005/02/16 19:22:32 bert * Autoconfiscation * * Revision 6.20 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.19 2001/03/19 18:31:55 neelin * Modifications to allow opening a stream to stdout (with no input) so * that a dicom stream can be captured. * * Revision 6.18 2000/10/03 14:01:24 neelin * Changed inclusion of bstring.h to happen only on SGIs, since linux also has * bcopy, etc. in string.h. * * Revision 6.17 2000/09/29 15:06:47 neelin * Fixed conversion of port string to number so that it is in network byte * order even on little-endian machines (linux PC). * * Revision 6.16 2000/05/24 14:31:05 neelin * Modified acr_transmit_group_list to remove elements that have been added * so that the group_list is returned unchanged. * * Revision 6.15 2000/05/17 20:17:46 neelin * Added mechanism to allow testing of input streams for more data through * function acr_file_ismore. * This is used in dicom_client_routines to allow asynchronous transfer * of data, with testing for more input done before sending new messages. * Previous use of select for this was misguided, since select may report that * no data is waiting on the file descriptor while data is store in the file * pointer buffer (or Acr file pointer buffer). * * Revision 6.14 1999/10/29 17:51:50 neelin * Fixed Log keyword * * Revision 6.13 1998/11/16 19:35:51 neelin * Added include for compilation under SunOS. * * Revision 6.12 1998/11/13 15:55:27 neelin * Modifications to support asynchronous transfers. * * Revision 6.11 1998/04/01 20:56:58 neelin * Added code to set socket buffer size so that things will go faster * under SunOS. * * Revision 6.10 1998/03/23 20:22:56 neelin * Removed unnecessary include. * * Revision 6.9 1998/03/23 20:17:04 neelin * Moved some general-purpose functions to dicom_network.c. * * Revision 6.8 1998/03/17 17:05:16 neelin * Set default maximum length to 1MB for servers that do not handle * length 0 (unlimited). * * Revision 6.7 1998/02/20 17:28:42 neelin * Removed unused variables. * * Revision 6.6 1998/02/20 17:24:41 neelin * In client routines, fd must be dup'ed before fdopen or problems may * occur reading and writing to 2 file pointers that open the same descriptor. * * Revision 6.4 1997/10/20 23:22:38 neelin * Added routine acr_dicom_close_no_release to close a connection that does * not have an association. * * Revision 6.3 1997/10/20 22:52:46 neelin * Added support for implementation user information in association request. * * Revision 6.2 1997/10/20 21:46:02 neelin * Delete answering message when making association. * * Revision 6.1 1997/09/15 16:50:59 neelin * Separated out connection timeouts from i/o timeouts and added functions * to change them. * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 1.2 1997/09/11 17:19:49 neelin * Modified creation of uids. Replaced cftime with strftime. * * Revision 1.1 1997/09/08 21:52:21 neelin * Initial revision * * Revision 1.4 1997/07/11 17:35:58 neelin * Changed around send and receive routines for data once again. * * Revision 1.3 1997/07/11 13:55:41 neelin * Fixed handling of message ids. * * Revision 1.2 1997/07/11 13:23:37 neelin * Made changes so that code will compile on sun OS. * Separated out receive_reply from send_group_list. * * Revision 1.1 1997/07/10 17:39:51 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include #include #include #include #include #include #include #ifdef sgi #include #endif #include #include #include #include #ifndef INADDR_NONE # define INADDR_NONE 0xffffffff #endif /* Dicom definitions */ #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" #define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" #define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" #define ACR_APPLICATION_CONTEXT_UID "1.2.840.10008.3.1.1.1" #define ACR_C_STORE_RQ 0x0001 #define ACR_C_STORE_RSP 0x8001 #define ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC 7 #define ACR_ASSOC_RJ_NO_REASON 1 #define ACR_ASSOC_RJ_PERM 1 #define ACR_ASSOC_RJ_USER 1 #define ACR_ASSOC_PR_CN_ACCEPT 0 #define ACR_ASSOC_PR_CN_REJECT 1 #define ACR_PDU_ITEM_USER_INFORMATION 0x50 #define ACR_MESSAGE_GID 0 #define ACR_SUCCESS 0x0000 DEFINE_ELEMENT(static, ACR_Affected_SOP_class_UID , 0x0000, 0x0002, UI); DEFINE_ELEMENT(static, ACR_Command , 0x0000, 0x0100, US); DEFINE_ELEMENT(static, ACR_Message_id , 0x0000, 0x0110, US); DEFINE_ELEMENT(static, ACR_Message_id_brt , 0x0000, 0x0120, US); DEFINE_ELEMENT(static, ACR_Priority , 0x0000, 0x0700, US); DEFINE_ELEMENT(static, ACR_Dataset_type , 0x0000, 0x0800, US); DEFINE_ELEMENT(static, ACR_Status , 0x0000, 0x0900, US); DEFINE_ELEMENT(static, ACR_Affected_SOP_instance_UID , 0x0000, 0x1000, UI); #if 0 DEFINE_ELEMENT(static, ACR_Move_originator_AE_title , 0x0000, 0x1031, AE); #endif DEFINE_ELEMENT(static, ACR_SOP_class_UID , 0x0008, 0x0016, UI); DEFINE_ELEMENT(static, ACR_SOP_instance_UID , 0x0008, 0x0018, UI); DEFINE_ELEMENT(static, ACR_Study_instance_UID , 0x0020, 0x000d, UI); DEFINE_ELEMENT(static, ACR_Series_instance_UID , 0x0020, 0x000e, UI); DEFINE_ELEMENT(static, ACR_Image_type , 0x0008, 0x0008, CS); DEFINE_ELEMENT(static, ACR_Sequence_variant , 0x0018, 0x0021, CS); DEFINE_ELEMENT(static, ACR_Image_position , 0x0020, 0x0032, DS); DEFINE_ELEMENT(static, ACR_Image_orientation , 0x0020, 0x0037, DS); DEFINE_ELEMENT(static, ACR_Frame_of_reference_UID , 0x0020, 0x0052, UI); DEFINE_ELEMENT(static, ACR_Samples_per_pixel , 0x0028, 0x0002, US); DEFINE_ELEMENT(static, ACR_Photometric_interpretation , 0x0028, 0x0004, CS); /* Minimum socket buffer size that we would like to have for TCP connections */ #define MIN_SOCK_BUFLEN (50*1024) /* Default values for timeouts and outstanding responses */ #define DEFAULT_TIMEOUT (60*2) #define DEFAULT_INITIAL_TIMEOUT (10) #define DEFAULT_MAX_OUTSTANDING (-1) /* Typedefs */ typedef struct { int timeout_length; int max_outstanding_responses; int last_message_id; int last_answered_id; } Dicom_client_data; /* Globals for handling connection timeouts */ static int Initial_timeout_length = DEFAULT_INITIAL_TIMEOUT; static int Connection_timeout = FALSE; static Acr_File *Alarmed_afp = NULL; /* Private functions */ static Dicom_client_data *get_client_data_ptr(Acr_File *afp); static Acr_Message compose_assoc_request(char *called_ae, char *calling_ae, char *abstract_syntax_list[], char *transfer_syntax_list[]); static int check_reply(Acr_Message message, int *presentation_context_id, char **transfer_syntax, long *maximum_length); static Acr_Status receive_message(Acr_File *afpin, Acr_Message *message); static Acr_Status send_message(Acr_File *afpout, Acr_Message message); static void timeout_handler(int sig); static int read_replies(Acr_File *afpin); static int synchronize_input(Acr_File *afpin); static Acr_Message make_message(Acr_Group group_list); /* ----------------------------- MNI Header ----------------------------------- @NAME : get_client_data_ptr @INPUT : afp @OUTPUT : (none) @RETURNS : Pointer to dicom client data @DESCRIPTION: Routine to get the pointer to the client data. If none exists, then it is created. @METHOD : @GLOBALS : @CALLS : @CREATED : November 11, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Dicom_client_data *get_client_data_ptr(Acr_File *afp) { Dicom_client_data *client_data; /* Get the data */ client_data = (Dicom_client_data *) acr_get_dicom_client_data(afp); /* If it is NULL then initialize it */ if (client_data == NULL) { client_data = MALLOC(sizeof(*client_data)); if (client_data == NULL) { (void) fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE); } client_data->timeout_length = DEFAULT_TIMEOUT; client_data->max_outstanding_responses = DEFAULT_MAX_OUTSTANDING; client_data->last_message_id = 0; client_data->last_answered_id = client_data->last_message_id; acr_set_dicom_client_data(afp, (void *) client_data); } return client_data; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_open_dicom_connection @INPUT : host - name of host to which we should connect If host name is "-" then output is sent to standard out and input is not opened - afpin is set to NULL. port - string giving port number or name of service to which we should connect called_ae - Remote application entity requested calling_ae - Application entity making request abstract_syntax - transfer_syntax - if NULL, then send the standard 3 are proposed (implicit-little endian, explicit little, explict big) @OUTPUT : afpin - dicom file handle for input afpout - dicom file handle for output @RETURNS : TRUE if successful connection is made, FALSE otherwise. @DESCRIPTION: Routine to open a dicom connection to a remote host. @METHOD : @GLOBALS : @CALLS : @CREATED : July 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_open_dicom_connection(char *host, char *port, char *called_ae, char *calling_ae, char *abstract_syntax, char *transfer_syntax, Acr_File **afpin, Acr_File **afpout) { FILE *fpin, *fpout; char *abstract_syntax_list[2] = {NULL, NULL}; char *transfer_syntax_list[2] = {NULL, NULL}; /* Make network connection if a host name is given. If no host, then set timeout to zero to indicate infinite wait */ if (strcmp(host, "-") == 0) { *afpin = NULL; *afpout = acr_initialize_dicom_output(stdout, 0, acr_stdio_write); acr_set_client_timeout(*afpout, 0.0); } else { if (!acr_connect_to_host(host, port, &fpin, &fpout)) { return FALSE; } *afpin = acr_initialize_dicom_input(fpin, 0, acr_stdio_read); *afpout = acr_initialize_dicom_output(fpout, 0, acr_stdio_write); /* Add ismore function to input */ acr_dicom_set_ismore_function(*afpin, acr_stdio_ismore); } /* Establish association */ abstract_syntax_list[0] = abstract_syntax; transfer_syntax_list[0] = transfer_syntax; if (!acr_make_dicom_association(*afpin, *afpout, called_ae, calling_ae, abstract_syntax_list, transfer_syntax_list)) { acr_close_dicom_no_release(*afpin, *afpout); return FALSE; } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_close_dicom_no_release @INPUT : afpin - dicom file handle for input (can be NULL) afpout - dicom file handle for output @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close a dicom connection without releasing the association. @METHOD : @GLOBALS : @CALLS : @CREATED : October 20, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_close_dicom_no_release(Acr_File *afpin, Acr_File *afpout) { FILE *fpin, *fpout; /* Close the input handle */ if (afpin != NULL) { fpin = (FILE *) acr_dicom_get_io_data(afpin); acr_close_dicom_file(afpin); (void) fclose(fpin); } /* Close the output handle */ fpout = (FILE *) acr_dicom_get_io_data(afpout); acr_close_dicom_file(afpout); (void) fclose(fpout); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_close_dicom_connection @INPUT : afpin - dicom file handle for input (can be NULL) afpout - dicom file handle for output @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close a dicom connection. @METHOD : @GLOBALS : @CALLS : @CREATED : July 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_close_dicom_connection(Acr_File *afpin, Acr_File *afpout) { /* Release the association */ (void) acr_release_dicom_association(afpin, afpout); /* Close the connection */ acr_close_dicom_no_release(afpin, afpout); } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_connect_to_host @INPUT : host - name of host to which we should connect port - string giving port number or name of service to which we should connect @OUTPUT : fpin - file handle for input fpout - file handle for output @RETURNS : TRUE if successful connection is made, FALSE otherwise. @DESCRIPTION: Routine to open a connection to a remote host. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_connect_to_host(char *host, char *port, FILE **fpin, FILE **fpout) { struct servent *sp; struct hostent *hp; struct sockaddr_in server; int sock; int sockbuflen, oldsockbuflen; socklen_t sockoptlen; /* Set default file pointers */ *fpin = *fpout = NULL; /* Initialize the address structure */ bzero((char *) &server, sizeof(server)); /* Get the port, either as a number or as a service name */ if ((*port >= '0') && (*port <= '9')) { server.sin_port = htons(atoi(port)); } else if ((sp = getservbyname(port, "tcp")) != NULL) { server.sin_port = sp->s_port; } else { (void) fprintf(stderr, "Service \"%s\" not found\n", port); return FALSE; } /* Look up the host, either as a name or as an IP address */ if ((*host >= '0') && (*host <= '9')) { server.sin_addr.s_addr = inet_addr(host); server.sin_family = AF_INET; if (server.sin_addr.s_addr == INADDR_NONE) { (void) fprintf(stderr, "Badly formed IP address %s\n", host); return FALSE; } } else if ((hp = gethostbyname(host)) != NULL) { bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length); server.sin_family = hp->h_addrtype; } else { (void) fprintf(stderr, "Unknown host: %s\n", host); return FALSE; } /* Open the connection */ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("Error getting socket"); return FALSE; } (void) signal(SIGALRM, timeout_handler); (void) alarm(Initial_timeout_length); if (connect(sock, (struct sockaddr *) &server, sizeof (server)) < 0) { (void) alarm(0); (void) fprintf(stderr, "Unable to connect to %s: ", host); if (Connection_timeout) { (void) fprintf(stderr, "connection timed out\n"); } else { perror(NULL); } return FALSE; } (void) alarm(0); /* Get socket buffer size */ sockbuflen = MIN_SOCK_BUFLEN; sockoptlen = sizeof(sockbuflen); if ((getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &sockbuflen, &sockoptlen) == 0) && (sockbuflen < MIN_SOCK_BUFLEN) && (sockbuflen > 0)) { oldsockbuflen = sockbuflen; sockbuflen = MIN_SOCK_BUFLEN; sockoptlen = sizeof(sockbuflen); while ((sockbuflen > oldsockbuflen) && (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &sockbuflen, sockoptlen) != 0)) { sockbuflen = (int) ((double) sockbuflen * 0.75); } if (sockbuflen <= oldsockbuflen) { (void) setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldsockbuflen, sockoptlen); } } /* Open file handles */ if ((*fpin = fdopen(sock, "r")) == NULL) { (void) fprintf(stderr, "Error opening socket for read\n"); return FALSE; } if (((sock = dup(sock)) < 0) || ((*fpout = fdopen(sock, "w")) == NULL)) { (void) fclose(*fpin); if (sock >= 0) (void) close(sock); (void) fprintf(stderr, "Error opening socket for write\n"); return FALSE; } /* Ignore SIGPIPES in case the output connection gets closed when we are doing output. */ (void) signal(SIGPIPE, SIG_IGN); return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_make_dicom_association @INPUT : afpin - input stream (can be NULL) afpout - output stream abstract_syntax_list - NULL terminated list of abstract syntaxes transfer_syntax_list - NULL-terminated list of transfer syntaxes. If NULL or empty, then the 3 standard syntaxes are proposed. @OUTPUT : (none) @RETURNS : Pointer to appropriate abstract syntax or NULL if association not made. @DESCRIPTION: Routine to establish a dicom association. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *acr_make_dicom_association(Acr_File *afpin, Acr_File *afpout, char *called_ae, char *calling_ae, char *abstract_syntax_list[], char *transfer_syntax_list[]) { Acr_Message message; char *transfer_syntax, *abstract_syntax; Acr_Status status; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; int isyntax, nsyntax; int presentation_context_id; long maximum_length; /* Synchronize input */ if (afpin != NULL) { if (!synchronize_input(afpin)) return NULL; } /* Compose a message */ message = compose_assoc_request(called_ae, calling_ae, abstract_syntax_list, transfer_syntax_list); if (message == NULL) { return NULL; } /* Get the requested maximum length */ maximum_length = acr_find_long(acr_get_message_group_list(message), DCM_PDU_Maximum_length, 0L); /* Ship it out */ status = send_message(afpout, message); acr_delete_message(message); if (status != ACR_OK) { acr_dicom_error(status, "Error sending association request"); return NULL; } /* Wait for an answer */ if (afpin == NULL) { presentation_context_id = 1; transfer_syntax = transfer_syntax_list[0]; } else { status = receive_message(afpin, &message); if (status != ACR_OK) { acr_dicom_error(status, "Error receiving association reply"); return NULL; } /* Check it */ if (!check_reply(message, &presentation_context_id, &transfer_syntax, &maximum_length)) { return NULL; } } /* Set the presentation context id for the streams */ if (afpin != NULL) { acr_set_dicom_pres_context_id(afpin, presentation_context_id); } acr_set_dicom_pres_context_id(afpout, presentation_context_id); /* Figure out which abstract syntax was accepted */ for (nsyntax=0; abstract_syntax_list[nsyntax] != NULL; nsyntax++) {} isyntax = (presentation_context_id - 1) / 2; if ((isyntax < 0) || (isyntax >= nsyntax)) { (void) fprintf(stderr, "Invalid presentation context accepted.)\n"); return NULL; } abstract_syntax = abstract_syntax_list[isyntax]; /* Make sure that the i/o streams have the correct encoding and byte-order */ if (acr_uid_equal(transfer_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { byte_order = ACR_LITTLE_ENDIAN; vr_encoding = ACR_IMPLICIT_VR; } else if (acr_uid_equal(transfer_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { byte_order = ACR_LITTLE_ENDIAN; vr_encoding = ACR_EXPLICIT_VR; } else if (acr_uid_equal(transfer_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { byte_order = ACR_BIG_ENDIAN; vr_encoding = ACR_EXPLICIT_VR; } else { (void) fprintf(stderr, "Unrecognized transfer syntax \"%s\"\n", transfer_syntax); return NULL; } if (afpin != NULL) { acr_set_byte_order(afpin, byte_order); acr_set_vr_encoding(afpin, vr_encoding); } acr_set_byte_order(afpout, byte_order); acr_set_vr_encoding(afpout, vr_encoding); /* Set the maximum length */ acr_set_dicom_maximum_length(afpout, maximum_length); /* Delete the input message */ if (afpin != NULL) { acr_delete_message(message); } /* Return the abstract syntax */ return abstract_syntax; } /* ----------------------------- MNI Header ----------------------------------- @NAME : compose_assoc_request @INPUT : called_ae - Remote application entity requested calling_ae - Application entity making request abstract_syntax_list - NULL terminated list of abstract syntaxes transfer_syntax_list - NULL-terminated list of transfer syntaxes. If NULL or empty, then the 3 standard syntaxes are proposed. @OUTPUT : (none) @RETURNS : Message to be sent to remote host @DESCRIPTION: Routine to compose an association request message. It only allows one abstract syntax to be sent. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Message compose_assoc_request(char *called_ae, char *calling_ae, char *abstract_syntax_list[], char *transfer_syntax_list[]) { Acr_Message message; Acr_Group group; Acr_Element item_list, sublist, subitem, element; char **syntax_list; int iabstract, itransfer, num_syntax; int cur_presentation_context_id; static char *standard_transfer_syntax[] = { ACR_IMPLICIT_VR_LITTLE_END_UID, ACR_EXPLICIT_VR_LITTLE_END_UID, ACR_EXPLICIT_VR_BIG_END_UID }; static int num_standard_transfer_syntax = sizeof(standard_transfer_syntax) / sizeof(standard_transfer_syntax[0]); /* Check for an NULL or empty abstract syntax list */ if ((abstract_syntax_list == NULL) || (abstract_syntax_list[0] == NULL)) { (void) fprintf(stderr, "Error composing association request: Empty abstract syntax list\n"); return NULL; } /* Build up the request */ group = acr_create_group(DCM_PDU_GRPID); acr_group_add_element(group, acr_create_element_short(DCM_PDU_Type, ACR_PDU_ASSOC_RQ)); /* Add the caller and calling AE titles */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Called_Ap_title, called_ae)); acr_group_add_element(group, acr_create_element_string(DCM_PDU_Calling_Ap_title, calling_ae)); /* Add the application context */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Application_context, ACR_APPLICATION_CONTEXT_UID)); /* Work out the list of transfer syntaxes */ if ((transfer_syntax_list == NULL) || (transfer_syntax_list[0] == NULL)) { syntax_list = standard_transfer_syntax; num_syntax = num_standard_transfer_syntax; } else { syntax_list = transfer_syntax_list; for (num_syntax=0; transfer_syntax_list[num_syntax] != NULL; num_syntax++) {} } /* Loop over abstract syntaxes (and presentation contexts) */ item_list = NULL; for (iabstract = 0; abstract_syntax_list[iabstract] != NULL; iabstract++) { /* Work out the presentation context id */ cur_presentation_context_id = (iabstract * 2) + 1; /* Create a presentation context */ sublist = NULL; subitem = acr_create_element_short(DCM_PDU_Presentation_context_id, cur_presentation_context_id); sublist = acr_element_list_add(sublist, subitem); /* Add an abstract syntax */ subitem = acr_create_element_string(DCM_PDU_Abstract_syntax, abstract_syntax_list[iabstract]); sublist = acr_element_list_add(sublist, subitem); /* Add the transfer syntax */ for (itransfer=0; itransfer < num_syntax; itransfer++) { subitem = acr_create_element_string(DCM_PDU_Transfer_syntax, syntax_list[itransfer]); sublist = acr_element_list_add(sublist, subitem); } /* Add this presentation context to the list */ item_list = acr_element_list_add(item_list, acr_create_element_sequence(DCM_PDU_Presentation_context, sublist)); } /* End of loop over presentation contexts */ /* Create the presentation context list element */ element = acr_create_element_sequence(DCM_PDU_Presentation_context_list, item_list); acr_group_add_element(group, element); /* Add the user information */ acr_group_add_element(group, acr_create_element_long(DCM_PDU_Maximum_length, 1048576L)); acr_group_add_element(group, acr_create_element_string(DCM_PDU_Implementation_class_uid, acr_get_implementation_uid())); /* Make a message and add this group */ message = acr_create_message(); acr_message_add_group(message, group); return message; } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_reply @INPUT : message @OUTPUT : presentation_context_id transfer_syntax maximum_length -maximum length for dicom output @RETURNS : TRUE if reply is okay, FALSE otherwise. @DESCRIPTION: Routine to check the reply from the remote host. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int check_reply(Acr_Message message, int *presentation_context_id, char **transfer_syntax, long *maximum_length) { Acr_Group group; Acr_Element element, item, sublist, subitem; int pdu_type; /* Set values in case of error */ *transfer_syntax = NULL; *presentation_context_id = -1; *maximum_length = 0; /* Get the group */ group = acr_get_message_group_list(message); /* See what they say */ pdu_type = acr_find_short(group, DCM_PDU_Type, -1); switch (pdu_type) { case ACR_PDU_ASSOC_AC: break; case ACR_PDU_ASSOC_RJ: (void) fprintf(stderr, "Association rejected: code %d\n", acr_find_short(group, DCM_PDU_Reason, -1)); return FALSE; case ACR_PDU_ABORT_RQ: (void) fprintf(stderr, "Association aborted: code %d\n", acr_find_short(group, DCM_PDU_Reason, -1)); return FALSE; default: (void) fprintf(stderr, "Bad response to association request\n"); return FALSE; } /* Get the presentation context list */ element = acr_find_group_element(group, DCM_PDU_Presentation_context_reply_list); if (element == NULL) { (void) fprintf(stderr, "Invalid response to assocation request\n"); return FALSE; } /* Loop over list */ for (item = (Acr_Element) acr_get_element_data(element); item != NULL; item = acr_get_element_next(item)) { if (!acr_element_is_sequence(item)) continue; /* Get presentation context info */ sublist = (Acr_Element) acr_get_element_data(item); /* Check whether it was accepted */ subitem = acr_find_element_id(sublist, DCM_PDU_Result); if ((subitem == NULL) || (acr_get_element_short(subitem) != ACR_ASSOC_PR_CN_ACCEPT)) { continue; } /* Check the presentation context id */ subitem = acr_find_element_id(sublist, DCM_PDU_Presentation_context_id); if (subitem == NULL) continue; *presentation_context_id = acr_get_element_short(subitem); /* Grab the transfer syntax */ subitem = acr_find_element_id(sublist, DCM_PDU_Transfer_syntax); if ((subitem == NULL)) continue; *transfer_syntax = acr_get_element_string(subitem); /* If we get to here, we have an accepted syntax, so stop looping */ break; } /* Check that we found a transfer syntax */ if (*transfer_syntax == NULL) { (void) fprintf(stderr, "No presentation contexts were accepted\n"); return FALSE; } /* Get the maximum length */ *maximum_length = acr_find_long(group, DCM_PDU_Maximum_length, 0L); return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : receive_message @INPUT : afpin - input stream @OUTPUT : message - message that was read in @RETURNS : status @DESCRIPTION: Routine to receive messages @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status receive_message(Acr_File *afpin, Acr_Message *message) { Acr_Status status; Dicom_client_data *client_data; Alarmed_afp = afpin; client_data = get_client_data_ptr(afpin); Connection_timeout = FALSE; (void) signal(SIGALRM, timeout_handler); (void) alarm(client_data->timeout_length); status=acr_input_dicom_message(afpin, message); (void) alarm(0); if (Connection_timeout) { status = ACR_CONNECTION_TIMEDOUT; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : send_message @INPUT : afpout - output stream message - message to send @OUTPUT : (none) @RETURNS : status @DESCRIPTION: Routine to send messages @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Status send_message(Acr_File *afpout, Acr_Message message) { Acr_Status status; Dicom_client_data *client_data; client_data = get_client_data_ptr(afpout); Alarmed_afp = afpout; Connection_timeout = FALSE; (void) signal(SIGALRM, timeout_handler); (void) alarm(client_data->timeout_length); status = acr_output_dicom_message(afpout, message); (void) alarm(0); if (Connection_timeout) { status = ACR_CONNECTION_TIMEDOUT; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_client_timeout @INPUT : afp - stream on which to set timeout seconds - time in seconds to wait for i/o before timing out @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to set the length of network timeouts. This time is used once the connection has been made. Note that although a double-precision argument is given, it is truncated to integer before being used. A value of zero or less means that no timeout is used. @METHOD : @GLOBALS : @CALLS : @CREATED : September 15, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_client_timeout(Acr_File *afp, double seconds) { Dicom_client_data *client_data; client_data = get_client_data_ptr(afp); client_data->timeout_length = (int) seconds; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_client_initial_timeout @INPUT : seconds - time in seconds to wait for initial connection before timing out @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to set the length of initial network connection timeouts. This time is used only when the initial connection is made. Note that although a double-precision argument is given, it is truncated to integer before being used. A value of zero or less means that no timeout is used. @METHOD : @GLOBALS : @CALLS : @CREATED : September 15, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_client_initial_timeout(double seconds) { Initial_timeout_length = (int) seconds; } /* ----------------------------- MNI Header ----------------------------------- @NAME : timeout_handler @INPUT : @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to handle connection timeouts. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void timeout_handler(int sig) { Connection_timeout = TRUE; if (Alarmed_afp != NULL) { acr_dicom_set_eof(Alarmed_afp); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_set_client_max_outstanding @INPUT : afp - stream on which to set asynchronous transfer (can be NULL) max - maximum number of outstanding messages that are allowed @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set the maximum number of messages that can be sent by acr_send_group_list before a reply is received. This must be set on the input stream to take effect. Setting a negative maximum means that acr_send_group_list will immediately block until the reply is received (completely synchonous). A value of zero means that the send will return immediately after sending, but will block on the next call until the reply is recieved. Setting a non-zero positive value will allow the transmission of more messages (up to the max) without any reply (asynchronous). Once the limit is reached, acr_send_group_list will block until a reply is received. Using asynchronous transfers can greatly improve speed for small messages, but means that error reporting can occur asynchonously as well. @METHOD : @GLOBALS : @CALLS : @CREATED : November 12, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_set_client_max_outstanding(Acr_File *afp, int max) { Dicom_client_data *client_data; if (afp == NULL) return; client_data = get_client_data_ptr(afp); client_data->max_outstanding_responses = max; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_get_client_max_outstanding @INPUT : afp - stream on which to set asynchronous transfer @OUTPUT : (none) @RETURNS : maximum number of outstanding responses permitted for this stream. @DESCRIPTION: Routine to get the maximum number of messages that can be sent by acr_send_group_list before a reply is received. @METHOD : @GLOBALS : @CALLS : @CREATED : November 12, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_get_client_max_outstanding(Acr_File *afp) { Dicom_client_data *client_data; client_data = get_client_data_ptr(afp); return client_data->max_outstanding_responses; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_dicom_error @INPUT : status - status returned from dicom routine string - string to display before error message @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Prints out the string, followed by a colon, a blank and and error message based on the status. @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void acr_dicom_error(Acr_Status status, char *string) { char *error; /* Get the appropriate error string */ error = acr_status_string(status); /* Print it out */ (void) fprintf(stderr, "%s: %s\n", string, error); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_release_dicom_association @INPUT : afpin - input stream (can be NULL) afpout - output stream @OUTPUT : (none) @RETURNS : TRUE if release went smoothly. @DESCRIPTION: Routine to shut down a dicom association. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_release_dicom_association(Acr_File *afpin, Acr_File *afpout) { Acr_Message message; Acr_Group group; Acr_Status status; int pdu_type; /* Synchronize input */ if (afpin != NULL) { if (!synchronize_input(afpin)) return FALSE; } /* Compose a message */ group = acr_create_group(DCM_PDU_GRPID); acr_group_add_element(group, acr_create_element_short(DCM_PDU_Type, ACR_PDU_REL_RQ)); message = acr_create_message(); acr_message_add_group(message, group); /* Ship it out */ status = send_message(afpout, message); acr_delete_message(message); if (status != ACR_OK) { acr_dicom_error(status, "Error sending release request"); return FALSE; } if (afpin != NULL) { /* Wait for an answer */ status = receive_message(afpin, &message); if (status != ACR_OK) { acr_dicom_error(status, "Error receiving release reply"); return FALSE; } /* Check it */ pdu_type = acr_find_short(group, DCM_PDU_Type, -1); if (pdu_type != ACR_PDU_REL_RP) { (void) fprintf(stderr, "Bad reply to release request (PDU type %d)\n", pdu_type); return FALSE; } } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_send_group_list @INPUT : afpin - input stream (can be NULL) afpout - output stream group_list - dicom dataset to send sop_class_uid - this should just be abstract syntax returned by make_association (or passed in to open_dicom_connection). @OUTPUT : (none) @RETURNS : TRUE if exchange went smoothly. @DESCRIPTION: Routine to send a dicom data set and get the response. Asynchronous transmission and reply is supported in this routine only. Use function acr_set_client_max_outstanding to turn this feature on. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_send_group_list(Acr_File *afpin, Acr_File *afpout, Acr_Group group_list, char *sop_class_uid) { Dicom_client_data *client_data; if (afpin != NULL) { /* Get the client_data. Message ids are tracked on the input side unless there is no input side */ client_data = get_client_data_ptr(afpin); /* Read in replies */ if (!read_replies(afpin)) return FALSE; } else { /* Get the client_data. Since there is no input we store it on the output side. */ client_data = get_client_data_ptr(afpout); } /* Send the message */ if (!acr_transmit_group_list(afpout, group_list, sop_class_uid, ++(client_data->last_message_id))) { return FALSE; } /* Check the reply if user wants to force synchronous operation */ if (afpin != NULL) { if (client_data->max_outstanding_responses < 0) { if (!read_replies(afpin)) return FALSE; } } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_replies @INPUT : afpin - input stream @OUTPUT : (none) @RETURNS : TRUE if replies are okay. @DESCRIPTION: Routine to read in outstanding replies. If asynchronous message exchange is permitted (see function acr_set_client_max_outstanding), then this routine will not block. @METHOD : @GLOBALS : @CALLS : @CREATED : November 12, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int read_replies(Acr_File *afpin) { int result; Dicom_client_data *client_data; int num_outstanding, max_outstanding; int keep_looping; /* Get the client_data */ client_data = get_client_data_ptr(afpin); /* Check whether there are any outstanding responses */ if (client_data->last_message_id > client_data->last_answered_id) { /* Get the maximum number of outstanding responses permitted. Make sure that max_outstanding is not negative so that we don't block waiting for a responses to an unsent message. */ max_outstanding = client_data->max_outstanding_responses; if (max_outstanding < 0) max_outstanding = 0; /* Loop until there is nothing left waiting */ keep_looping = TRUE; while (keep_looping) { /* Figure out how many outstanding responses there are */ num_outstanding = client_data->last_message_id - client_data->last_answered_id; /* Get out of the loop if there is nothing to read and the number of outstanding responses is not too large */ if (!acr_file_ismore(afpin) && (num_outstanding <= max_outstanding)) { keep_looping = FALSE; break; } /* Read in a reply and keep track of id of answered message */ result = acr_receive_reply(afpin); if (result < 0) return FALSE; if (result != (client_data->last_answered_id+1)) { (void) fprintf(stderr, "Received reply to wrong message\n"); return FALSE; } client_data->last_answered_id = result; } /* End loop over responses */ } /* End if outstanding responses */ return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : synchronize_input @INPUT : afpin - input stream @OUTPUT : (none) @RETURNS : TRUE if replies are okay. @DESCRIPTION: Routine to synchronize the input stream with the output (see function acr_set_client_max_outstanding). This function will block until all replies are read in. @METHOD : @GLOBALS : @CALLS : @CREATED : November 12, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int synchronize_input(Acr_File *afpin) { int old_max_outstanding; int result; /* Set the maximum number of outstanding replies to zero to force blocking, saving the old value */ old_max_outstanding = acr_get_client_max_outstanding(afpin); acr_set_client_max_outstanding(afpin, 0); /* Read the replies */ result = read_replies(afpin); /* Restore the old value */ acr_set_client_max_outstanding(afpin, old_max_outstanding); return result; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_transmit_group_list @INPUT : afpout - output stream group_list - dicom dataset to send sop_class_uid - this should just be abstract syntax returned by make_association (or passed in to open_dicom_connection). message_id - Unique message id. @OUTPUT : (none) @RETURNS : TRUE if send went smoothly. @DESCRIPTION: Routine to send a dicom data set. To check for the reply, call acr_receive_reply. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_transmit_group_list(Acr_File *afpout, Acr_Group group_list, char *sop_class_uid, int message_id) { Acr_Status status; Acr_Message message; Acr_Group output_group_list; char uid_buffer[64] = {'\0'}; char *instance_uid; Acr_Element_Id elements_to_remove[25]; int nelements_to_remove = 0; int ielem; Acr_Group group; /* Get a UID for this object */ instance_uid = acr_find_string(group_list, ACR_SOP_instance_UID, NULL); if (instance_uid == NULL) { instance_uid = strncpy(uid_buffer, acr_create_uid(), sizeof(uid_buffer)-1); } /* Create a command */ output_group_list = acr_create_group(ACR_MESSAGE_GID); acr_group_add_element(output_group_list, acr_create_element_string(ACR_Affected_SOP_class_UID, sop_class_uid)); acr_group_add_element(output_group_list, acr_create_element_short(ACR_Command, ACR_C_STORE_RQ)); acr_group_add_element(output_group_list, acr_create_element_short(ACR_Message_id, message_id)); acr_group_add_element(output_group_list, acr_create_element_short(ACR_Priority, 0)); acr_group_add_element(output_group_list, acr_create_element_short(ACR_Dataset_type, 0)); acr_group_add_element(output_group_list, acr_create_element_string(ACR_Affected_SOP_instance_UID, instance_uid)); /* Send the command */ message = make_message(output_group_list); status = send_message(afpout, message); acr_delete_message(message); if (status != ACR_OK) { acr_dicom_error(status, "Error sending store request"); return FALSE; } /* Macro to insert a new string element into the group list and record the element id so that it can be removed later */ nelements_to_remove = 0; #define INSERT_N_SAVE_ELEM(elid, string) \ {acr_insert_string(&group_list, elid, string); \ elements_to_remove[nelements_to_remove++] = elid;} /* Add the class and instance UID's to the data group list */ if (acr_find_group_element(group_list, ACR_SOP_class_UID) == NULL) { INSERT_N_SAVE_ELEM(ACR_SOP_class_UID, sop_class_uid); } if (acr_find_group_element(group_list, ACR_SOP_instance_UID) == NULL) { INSERT_N_SAVE_ELEM(ACR_SOP_instance_UID, instance_uid); } /* Add the study and series UID's to the group list */ if (acr_find_group_element(group_list, ACR_Study_instance_UID) == NULL) { INSERT_N_SAVE_ELEM(ACR_Study_instance_UID, acr_create_uid()); } if (acr_find_group_element(group_list, ACR_Series_instance_UID) == NULL) { INSERT_N_SAVE_ELEM(ACR_Series_instance_UID, acr_create_uid()); } /* Make up some essential data if it is not already in the group list */ if (acr_find_group_element(group_list, ACR_Image_type) == NULL) { INSERT_N_SAVE_ELEM(ACR_Image_type, "ORIGINAL\\PRIMARY\\UNDEFINED"); } if (acr_find_group_element(group_list, ACR_Sequence_variant) == NULL) { INSERT_N_SAVE_ELEM(ACR_Sequence_variant, "NONE\\NONE"); } if (acr_find_group_element(group_list, ACR_Image_position) == NULL) { INSERT_N_SAVE_ELEM(ACR_Image_position, "0\\0\\0"); } if (acr_find_group_element(group_list, ACR_Image_orientation) == NULL) { INSERT_N_SAVE_ELEM(ACR_Image_orientation, "1\\0\\0\\0\\1\\0"); } if (acr_find_group_element(group_list, ACR_Frame_of_reference_UID) == NULL) { INSERT_N_SAVE_ELEM(ACR_Frame_of_reference_UID, acr_create_uid()); } if (acr_find_group_element(group_list, ACR_Samples_per_pixel) == NULL) { acr_insert_short(&group_list, ACR_Samples_per_pixel, 1); elements_to_remove[nelements_to_remove++] = ACR_Samples_per_pixel; } if (acr_find_group_element(group_list, ACR_Photometric_interpretation) == NULL) { INSERT_N_SAVE_ELEM(ACR_Photometric_interpretation, "MONOCHROME2"); } /* Send the data. Disconnect the message from the group list so that the latter is not deleted */ message = make_message(group_list); status = send_message(afpout, message); acr_message_reset(message); acr_delete_message(message); /* Delete the elements that we added */ for (ielem=0; ielem < nelements_to_remove; ielem++) { group = acr_find_group(group_list, elements_to_remove[ielem]->group_id); if (group != NULL) { acr_group_remove_element(group, elements_to_remove[ielem]->element_id); } } /* Check the status */ if (status != ACR_OK) { acr_dicom_error(status, "Error sending store data"); return FALSE; } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : acr_receive_reply @INPUT : afpin - input stream @OUTPUT : (none) @RETURNS : Message id being reponded to or -1 if an error occurs @DESCRIPTION: Routine to receive a reply to a send command. @METHOD : @GLOBALS : @CALLS : @CREATED : May 9, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int acr_receive_reply(Acr_File *afpin) { Acr_Status status; Acr_Message message; Acr_Group reply_group; int command_status; int message_id; /* Wait for an answer */ status = receive_message(afpin, &message); if (status != ACR_OK) { acr_dicom_error(status, "Error receiving store reply"); return -1; } /* Check the reply */ reply_group = acr_get_message_group_list(message); if (acr_find_short(reply_group, ACR_Command, -1) != ACR_C_STORE_RSP) { (void) fprintf(stderr, "Bad response to store request\n"); return -1; } command_status = acr_find_short(reply_group, ACR_Status, -1); switch (command_status) { case ACR_SUCCESS: break; default: (void) fprintf(stderr, "Unrecognized store status (%d)\n", command_status); return -1; } message_id = acr_find_short(reply_group, ACR_Message_id_brt, -1); /* Delete the reply message */ acr_delete_message(message); return message_id; } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_message @INPUT : group_list @OUTPUT : (nothing) @RETURNS : output message. @DESCRIPTION: Convert a group list into a message. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Acr_Message make_message(Acr_Group group_list) { Acr_Group next_group, group; Acr_Message output_message; /* Create the output message */ output_message = acr_create_message(); /* Loop through groups, adding them to the message */ group = group_list; while (group != NULL) { next_group = acr_get_group_next(group); acr_set_group_next(group, NULL); acr_message_add_group(output_message, group); group = next_group; } return output_message; } minc-tools-2.3.00+dfsg/conversion/Acr_nema/globals.c0000644000175000000620000000214112574624760021301 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : globals.c @DESCRIPTION: Global definitions for acr-nema library. @METHOD : @GLOBALS : @CALLS : @CREATED : February 12, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ #define ACR_LIBRARY_GLOBAL_ELEMENT_DEFINITION #include #include #include #include #include minc-tools-2.3.00+dfsg/conversion/Acr_nema/acr_nema.h0000644000175000000620000000624412574624760021440 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : acr_nema.h @DESCRIPTION: Header file for ACR-NEMA code. @METHOD : @GLOBALS : @CREATED : November 10, 1993 (Peter Neelin) @MODIFIED : * $Log: acr_nema.h,v $ * Revision 6.4 2011-02-17 06:41:51 rotor * * Fixed a HDF5 error output bug in testing code * * Revision 6.3 2005/02/16 19:22:32 bert * Autoconfiscation * * Revision 6.2 2004/10/29 13:08:41 rotor * * rewrote Makefile with no dependency on a minc distribution * * removed all references to the abominable minc_def.h * * I should autoconf this really, but this is old code that * is now replaced by Jon Harlaps PERL version.. * * Revision 6.1 1999/10/29 17:51:50 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:59 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/08 21:53:14 neelin * Added dicom_client_routines. * * Revision 5.0 1997/08/21 13:25:00 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:01:23 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/21 20:21:09 neelin * Updated the library to handle dicom messages. * * Revision 3.0 1995/05/15 19:32:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:36:08 neelin * Release of minc version 0.2 * * Revision 1.6 94/09/28 10:35:51 neelin * Pre-release * * Revision 1.5 94/09/23 16:42:37 neelin * Changed acr_nema_io to acr_io and acr_nema_test to acr_test. * * Revision 1.4 94/01/06 13:31:27 neelin * Changed acr_need_invert to a public function. * * Revision 1.3 93/12/08 09:07:02 neelin * * Revision 1.2 93/11/24 11:26:09 neelin * Added TRUE and FALSE. * * Revision 1.1 93/11/19 12:49:34 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. ---------------------------------------------------------------------------- */ /* Define constants */ #ifndef public # define public #endif #ifndef private # define private static #endif #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif /* Include files */ #include #include #include #include #include #include #include #include /* these are pinched from minc_def.h */ #define MALLOC(size) ((void *) malloc(size)) #define FREE(ptr) free(ptr) #define REALLOC(ptr, size) ((void *) realloc(ptr, size)) #define CALLOC(nelem, elsize) ((void *) calloc(nelem, elsize)) minc-tools-2.3.00+dfsg/conversion/ana2mnc/0002755000175000000620000000000012574624760017330 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/ana2mnc/steve_smith_ana2mnc/0002755000175000000620000000000012574624760023261 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/ana2mnc/steve_smith_ana2mnc/analyze2minc.c0000644000175000000620000002624712574624760026032 0ustar stevestaff/* {{{ Copyright etc. */ /**********************************************\ analyze2minc 0.3 by Stephen Smith Copyright 1997-1999 Stephen Smith Some elements are Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Oxford Centre for Functional Magnetic Resonance Imaging of the Brain, Department of Clinical Neurology, Oxford University, Oxford, UK Email: steve@fmrib.ox.ac.uk WWW: http://www.fmrib.ox.ac.uk/~steve This program should be considered a beta test version and must not be used for any clinical purposes. This code is written using an emacs folding mode, making moving around the different sections very easy. This is why there are various marks within comments and why comments are indented. Requires: rawtominc (setup the macro RAWTOMINC below) Compile with: cc -o analyze2minc analyze2minc.c History: 0.3 January 20 1999 fixed L-R error 0.2 July 17 1998 added 4D functionality 0.1 June 1 1998 \**********************************************/ /* }}} */ #define RAWTOMINC "/usr/local/mni/bin/rawtominc" /* {{{ includes and defines */ #include #include #include #include #include #include #include "dbh.h" typedef unsigned char uchar; /* }}} */ /* {{{ routines from avw_read */ /* {{{ swap_long */ void swap_long(pntr) unsigned char *pntr; { unsigned char b0, b1, b2, b3; b0 = *pntr; b1 = *(pntr+1); b2 = *(pntr+2); b3 = *(pntr+3); *pntr = b3; *(pntr+1) = b2; *(pntr+2) = b1; *(pntr+3) = b0; } /* }}} */ /* {{{ swap_short */ void swap_short(pntr) unsigned char *pntr; { unsigned char b0, b1; b0 = *pntr; b1 = *(pntr+1); *pntr = b1; *(pntr+1) = b0; } /* }}} */ /* {{{ swap_hdr */ void swap_hdr(pntr) struct dsr *pntr; { swap_short(&pntr->hist.originator[0]) ; swap_short(&pntr->hist.originator[1]) ; swap_short(&pntr->hist.originator[2]) ; swap_short(&pntr->hist.originator[3]) ; swap_short(&pntr->hist.originator[4]) ; swap_long(&pntr->hk.sizeof_hdr) ; swap_long(&pntr->hk.extents) ; swap_short(&pntr->hk.session_error) ; swap_short(&pntr->dime.dim[0]) ; swap_short(&pntr->dime.dim[1]) ; swap_short(&pntr->dime.dim[2]) ; swap_short(&pntr->dime.dim[3]) ; swap_short(&pntr->dime.dim[4]) ; swap_short(&pntr->dime.dim[5]) ; swap_short(&pntr->dime.dim[6]) ; swap_short(&pntr->dime.dim[7]) ; swap_short(&pntr->dime.unused1) ; swap_short(&pntr->dime.datatype) ; swap_short(&pntr->dime.bitpix) ; swap_long(&pntr->dime.pixdim[0]) ; swap_long(&pntr->dime.pixdim[1]) ; swap_long(&pntr->dime.pixdim[2]) ; swap_long(&pntr->dime.pixdim[3]) ; swap_long(&pntr->dime.pixdim[4]) ; swap_long(&pntr->dime.pixdim[5]) ; swap_long(&pntr->dime.pixdim[6]) ; swap_long(&pntr->dime.pixdim[7]) ; swap_long(&pntr->dime.vox_offset) ; swap_long(&pntr->dime.funused1) ; swap_long(&pntr->dime.funused2) ; swap_long(&pntr->dime.cal_max) ; swap_long(&pntr->dime.cal_min) ; swap_long(&pntr->dime.compressed) ; swap_long(&pntr->dime.verified) ; swap_short(&pntr->dime.dim_un0) ; swap_long(&pntr->dime.glmax) ; swap_long(&pntr->dime.glmin) ; } /* }}} */ /* {{{ ShowHdr */ void ShowHdr(fileName,hdr) struct dsr *hdr; char *fileName; { int i; char string[128]; printf("Analyze Header Dump of: <%s> \n", fileName); /* Header Key */ printf("sizeof_hdr: <%d> \n", hdr->hk.sizeof_hdr); printf("data_type: <%s> \n", hdr->hk.data_type); printf("db_name: <%s> \n", hdr->hk.db_name); printf("extents: <%d> \n", hdr->hk.extents); printf("session_error: <%d> \n", hdr->hk.session_error); printf("regular: <%c> \n", hdr->hk.regular); printf("hkey_un0: <%c> \n", hdr->hk.hkey_un0); /* Image Dimension */ for(i=0;i<8;i++) printf("dim[%d]: <%d> \n", i, hdr->dime.dim[i]); strncpy(string,hdr->dime.vox_units,4); printf("vox_units: <%s> \n", string); strncpy(string,hdr->dime.cal_units,8); printf("cal_units: <%s> \n", string); printf("unused1: <%d> \n", hdr->dime.unused1); printf("datatype: <%d> \n", hdr->dime.datatype); printf("bitpix: <%d> \n", hdr->dime.bitpix); for(i=0;i<8;i++) printf("pixdim[%d]: <%6.4f> \n",i, hdr->dime.pixdim[i]); printf("vox_offset: <%6.4> \n", hdr->dime.vox_offset); printf("funused1: <%6.4f> \n", hdr->dime.funused1); printf("funused2: <%6.4f> \n", hdr->dime.funused2); printf("funused3: <%6.4f> \n", hdr->dime.funused3); printf("cal_max: <%6.4f> \n", hdr->dime.cal_max); printf("cal_min: <%6.4f> \n", hdr->dime.cal_min); printf("compressed: <%d> \n", hdr->dime.compressed); printf("verified: <%d> \n", hdr->dime.verified); printf("glmax: <%d> \n", hdr->dime.glmax); printf("glmin: <%d> \n", hdr->dime.glmin); /* Data History */ strncpy(string,hdr->hist.descrip,80); printf("descrip: <%s> \n", string); strncpy(string,hdr->hist.aux_file,24); printf("aux_file: <%s> \n", string); printf("orient: <%d> \n", hdr->hist.orient); strncpy(string,hdr->hist.originator,10); printf("originator: <%s> \n", string); strncpy(string,hdr->hist.generated,10); printf("generated: <%s> \n", string); strncpy(string,hdr->hist.scannum,10); printf("scannum: <%s> \n", string); strncpy(string,hdr->hist.patient_id,10); printf("patient_id: <%s> \n", string); strncpy(string,hdr->hist.exp_date,10); printf("exp_date: <%s> \n", string); strncpy(string,hdr->hist.exp_time,10); printf("exp_time: <%s> \n", string); strncpy(string,hdr->hist.hist_un0,10); printf("hist_un0: <%s> \n", string); printf("views: <%d> \n", hdr->hist.views); printf("vols_added: <%d> \n", hdr->hist.vols_added); printf("start_field:<%d> \n", hdr->hist.start_field); printf("field_skip: <%d> \n", hdr->hist.field_skip); printf("omax: <%d> \n", hdr->hist.omax); printf("omin: <%d> \n", hdr->hist.omin); printf("smin: <%d> \n", hdr->hist.smax); printf("smin: <%d> \n", hdr->hist.smin); } /* }}} */ /* }}} */ main(argc,argv) int argc; char *argv[]; { /* {{{ vars */ FILE *fd, *fd2; struct dsr hdr; int int_a, size, rev=0, volumes=1, swap=0, bytepix, i, x, y, z, revxdata=1; double cmax, cmin; short datatype, short_a, short_array_a[1000], x_dim_short, y_dim_short, z_dim_short; uchar uchar_a, *buffera, *bufferb; float float_a, float_array_a[1000], x_pix_dim_float, y_pix_dim_float, z_pix_dim_float, x_origin_float, y_origin_float, z_origin_float; char data_file[1000], string_a[1000], string_a2[1000], string_array_a[100][1000]; static char type_string[100]; static int DataTypeSizes[32] = {0,1,8,0,16,0,0,0,32,0,0,0,0,0,0,0,32}; /* }}} */ /* {{{ COMMENT usage */ #ifdef FoldingComment if (argc==1) { printf("Usage: analyze2minc [-rev]\n"); printf("E.g.: if input file is image.hdr and image.img, use \"analyze2minc image\"\n"); printf("(-rev causes reversal in the x direction.)\n"); exit(0); } if (argc==3) rev=1; #endif /* }}} */ /* {{{ usage */ if (argc==1) { printf("Usage: analyze2minc \n"); printf("E.g.: if input file is image.hdr and image.img, use \"analyze2minc image\"\n"); exit(0); } /* }}} */ /* {{{ open input file and read header */ sprintf(string_a,"%s.hdr",argv[1]); if((fd=fopen(string_a,"r"))==NULL) { fprintf(stderr,"Can't open:<%s>\n", argv[1]); exit(0); } fread(&hdr,1,sizeof(struct dsr),fd); fclose(fd); if(hdr.dime.dim[0] < 0 || hdr.dime.dim[0] > 15) { printf("Byte swapping\n"); swap=1; swap_hdr(&hdr); } /* }}} */ /* {{{ read image_dimension */ x_dim_short=hdr.dime.dim[1]; y_dim_short=hdr.dime.dim[2]; z_dim_short=hdr.dime.dim[3]; volumes=hdr.dime.dim[4]; x_pix_dim_float=hdr.dime.pixdim[1]; if (x_pix_dim_float<0) { x_pix_dim_float = -x_pix_dim_float; /* revxdata=0;*/ /* don't need this */ } y_pix_dim_float=hdr.dime.pixdim[2]; z_pix_dim_float=hdr.dime.pixdim[3]; datatype=hdr.dime.datatype; sprintf(type_string,""); if (datatype==DT_UNSIGNED_CHAR) sprintf(type_string,"-byte "); if (datatype==DT_SIGNED_SHORT) sprintf(type_string,"-short -signed "); if (datatype==DT_SIGNED_INT) sprintf(type_string,"-long -signed "); if (datatype==DT_FLOAT) sprintf(type_string,"-float "); if (datatype==DT_DOUBLE) sprintf(type_string,"-double "); bytepix=DataTypeSizes[datatype]/8; buffera = (uchar*)malloc(volumes*x_dim_short*y_dim_short*z_dim_short*bytepix); bufferb = (uchar*)malloc(volumes*x_dim_short*y_dim_short*z_dim_short*bytepix); /* }}} */ /* {{{ read data_history */ /* origin */ memcpy(short_array_a,hdr.hist.originator,5*sizeof(short)); x_origin_float=((float)-short_array_a[0])*x_pix_dim_float; y_origin_float=((float)-short_array_a[1])*y_pix_dim_float; z_origin_float=((float)-short_array_a[2])*z_pix_dim_float; /* }}} */ /* {{{ debug prints */ printf("%d %d %d\n",x_dim_short,y_dim_short,z_dim_short); printf("%f %f %f\n",x_pix_dim_float,y_pix_dim_float,z_pix_dim_float); printf("%f %f %f\n",x_origin_float,y_origin_float,z_origin_float); /* }}} */ /* {{{ reverse x */ if (revxdata) { sprintf(string_a,"%s.img",argv[1]); fd=fopen(string_a,"r"); fread(buffera,bytepix,volumes*x_dim_short*y_dim_short*z_dim_short,fd); fclose(fd); for(i=0; i\n", argv[1]); exit(0); } sprintf(string_a,"%s.tmp.tmp.img",argv[1]); if((fd2=fopen(string_a,"w"))==NULL) { fprintf(stderr,"Can't open:<%s> for writing\n", argv[1]); exit(0); } for(i=0; i 1 ) sprintf(string_a2,"%d",volumes); else sprintf(string_a2,""); sprintf(string_a, "%s %s -transverse -scan_range -input %s -xstep %f -ystep %f -zstep %f -xstart %f -ystart %f -zstart %f %s.mnc %s %d %d %d", RAWTOMINC, type_string, data_file, x_pix_dim_float,y_pix_dim_float,z_pix_dim_float, x_origin_float,y_origin_float,z_origin_float, argv[1], string_a2,z_dim_short,y_dim_short,x_dim_short); system(string_a); /* }}} */ /* {{{ remove temp data file */ if (revxdata) { sprintf(string_a,"/bin/rm %s",data_file); system(string_a); } /* }}} */ } minc-tools-2.3.00+dfsg/conversion/ana2mnc/steve_smith_ana2mnc/dbh.h0000644000175000000620000000721412574624760024171 0ustar stevestaff /* * * (c) Copyright, 1986-1995 * Biomedical Imaging Resource * Mayo Foundation * * dbh.h * * * database sub-definitions */ struct header_key /* header_key */ { /* off + size*/ int sizeof_hdr; /* 0 + 4 */ char data_type[10]; /* 4 + 10 */ char db_name[18]; /* 14 + 18 */ int extents; /* 32 + 4 */ short int session_error; /* 36 + 2 */ char regular; /* 38 + 1 */ char hkey_un0; /* 39 + 1 */ }; /* total=40 */ struct image_dimension /* image_dimension */ { /* off + size*/ short int dim[8]; /* 0 + 16 */ char vox_units[4]; /* 16 + 4 */ char cal_units[8]; /* 20 + 4 */ short int unused1; /* 24 + 2 */ short int datatype; /* 30 + 2 */ short int bitpix; /* 32 + 2 */ short int dim_un0; /* 34 + 2 */ float pixdim[8]; /* 36 + 32 */ /* pixdim[] specifies the voxel dimensions: pixdim[1] - voxel width pixdim[2] - voxel height pixdim[3] - interslice distance ..etc */ float vox_offset; /* 68 + 4 */ float funused1; /* 72 + 4 */ float funused2; /* 76 + 4 */ float funused3; /* 80 + 4 */ float cal_max; /* 84 + 4 */ float cal_min; /* 88 + 4 */ int compressed; /* 92 + 4 */ int verified; /* 96 + 4 */ int glmax, glmin; /* 100 + 8 */ }; /* total=108 */ struct data_history /* data_history */ { /* off + size*/ char descrip[80]; /* 0 + 80 */ char aux_file[24]; /* 80 + 24 */ char orient; /* 104 + 1 */ char originator[10]; /* 105 + 10 */ char generated[10]; /* 115 + 10 */ char scannum[10]; /* 125 + 10 */ char patient_id[10]; /* 135 + 10 */ char exp_date[10]; /* 145 + 10 */ char exp_time[10]; /* 155 + 10 */ char hist_un0[3]; /* 165 + 3 */ int views; /* 168 + 4 */ int vols_added; /* 172 + 4 */ int start_field; /* 176 + 4 */ int field_skip; /* 180 + 4 */ int omax,omin; /* 184 + 8 */ int smax,smin; /* 192 + 8 */ }; /* total=200 */ struct dsr /* dsr */ { /* off + size*/ struct header_key hk; /* 0 + 40 */ struct image_dimension dime; /* 40 + 108 */ struct data_history hist; /* 148 + 200 */ }; /* total=348 */ #define DT_NONE 0 #define DT_UNKNOWN 0 #define DT_BINARY 1 #define DT_UNSIGNED_CHAR 2 #define DT_SIGNED_SHORT 4 #define DT_SIGNED_INT 8 #define DT_FLOAT 16 #define DT_COMPLEX 32 #define DT_DOUBLE 64 #define DT_RGB 128 #define DT_ALL 255 minc-tools-2.3.00+dfsg/conversion/ana2mnc/steve_smith_ana2mnc/minc2analyze.c0000644000175000000620000004010312574624760026015 0ustar stevestaff/* {{{ Copyright etc. */ /**********************************************\ minc2analyze 0.4 by Stephen Smith Copyright 1997-1998 Stephen Smith Some elements are Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Oxford Centre for Functional Magnetic Resonance Imaging of the Brain, Department of Clinical Neurology, Oxford University, Oxford, UK Email: steve@fmrib.ox.ac.uk WWW: http://www.fmrib.ox.ac.uk/~steve This program should be considered a beta test version and must not be used for any clinical purposes. This code is written using an emacs folding mode, making moving around the different sections very easy. This is why there are various marks within comments and why comments are indented. Requires: minctoraw (setup the macro MINCTORAW below) mincinfo (setup the macro MINCINFO below) Compile with: cc -o minc2analyze minc2analyze.c History: 0.4 January 20 1999 fixed L-R error 0.3 September 28 1998 altered reversing and added reordering 0.2 July 17 1998 added 4D functionality and more data types 0.1 June 1 1998 \**********************************************/ /* }}} */ #define MINCTORAW "/usr/local/mni/bin/minctoraw" #define MINCINFO "/usr/local/mni/bin/mincinfo" /* {{{ includes and defines */ #include #include #include #include #include #include #include "dbh.h" typedef unsigned char uchar; #define FTOI(a) ( (a) < 0 ? ((int)((a)-0.5)) : ((int)((a)+0.5)) ) /* }}} */ /* {{{ num_test */ float num_test(sinput,fdefault) char *sinput; float fdefault; { float finput=atof(sinput); if ( (finite(finput)==1) && (strcmp(sinput,"unknown")!=0) ) { /*printf("string=%s atof=%f default=%f return=%f\n",sinput,finput,fdefault,finput);*/ return(finput); } else { /*printf("string=%s atof=%f default=%f return=%f\n",sinput,finput,fdefault,fdefault);*/ return(fdefault); } } /* }}} */ /* {{{ usage */ void usage() { printf("Usage: minc2analyze [-z]\n"); printf("E.g.: if input file is image.mnc, use \"minc2analyze image\"\n"); printf("By default, co-ordinates and voxel dimensions will be the same after conversion;\nalternatively, -z zeros origin offset and makes all voxel dimensions positive\n"); exit(1); } /* }}} */ int main(argc,argv) int argc; char *argv[]; { /* {{{ vars */ uchar *buffera, *bufferb; char string_a[1000], string_array_a[100][1000], type_string[1000], *tcp; short short_array_a[1000], x_dim_short, y_dim_short, z_dim_short; int x,y,z, i, ko=1, datatype, volumes=1, bitpix, bytepix, argindex, order[3], glmin, glmax, pos=0; static int DataTypeSizes[32] = {0,1,8,0,16,0,0,0,32,0,0,0,0,0,0,0,32}; float x_pix_dim_float, y_pix_dim_float, z_pix_dim_float, x_origin_float, y_origin_float, z_origin_float; struct dsr hdr; FILE *fd, *fd2; /* }}} */ /* {{{ usage */ if (argc==1) usage(); argindex=2; while (argindex < argc) { tcp = argv[argindex]; if (*tcp == '-') switch (*++tcp) { case 'z': /* zero origin offset */ ko=0; break; case 'p': /* make voxel dimensions positive */ pos=1; break; default: usage(); } else usage(); argindex++; } /* }}} */ /* {{{ read file type */ sprintf(string_a,"%s %s.mnc | tail +2 | head -1 > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1]); fclose(fd2); datatype=DT_SIGNED_SHORT; sprintf(type_string," "); if (strncmp(string_array_a[1],"byt",3)==0) { if (strncmp(string_array_a[0],"sig",3)==0) { datatype=DT_SIGNED_SHORT; sprintf(type_string,"-short -signed ");} else { datatype=DT_UNSIGNED_CHAR; sprintf(type_string,"-byte ");} } if (strncmp(string_array_a[1],"sho",3)==0) { if (strncmp(string_array_a[0],"sig",3)!=0) { datatype=DT_SIGNED_INT; sprintf(type_string,"-long -signed ");} else { datatype=DT_SIGNED_SHORT; sprintf(type_string,"-short -signed ");} } if (strncmp(string_array_a[1],"lon",3)==0) { if (strncmp(string_array_a[0],"sig",3)==0) { datatype=DT_SIGNED_INT; sprintf(type_string,"-long -signed ");} else { datatype=DT_FLOAT; sprintf(type_string,"float ");} } if (strncmp(string_array_a[1],"flo",3)==0) { datatype=DT_FLOAT; sprintf(type_string,"-float "); } printf("datatype=%d\n",datatype); bitpix=DataTypeSizes[datatype]; bytepix=bitpix/8; /* }}} */ /* {{{ read x,y,z,t dimensions and image range etc. */ sprintf(string_a,"%s %s.mnc | grep image: > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s %s %s",(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2], (char *)&string_array_a[3],(char *)&string_array_a[4],(char *)&string_array_a[5]); if (strcmp("to",string_array_a[4])==0) i=4; else i=3; glmin=atoi(string_array_a[i-1]); glmax=atoi(string_array_a[i+1]); printf("Image range = %d to %d\n",glmin,glmax); fscanf(fd2,"%s %s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2]); z_dim_short=num_test(string_array_a[0],(float)256); z_pix_dim_float=num_test(string_array_a[1],(float)1); z_origin_float=-((float)num_test(string_array_a[2],(float)(z_dim_short/2)))/fabs(z_pix_dim_float); fclose(fd2); sprintf(string_a,"%s %s.mnc | tail -3 | grep zspace > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2]); z_dim_short=num_test(string_array_a[0],(float)256); z_pix_dim_float=num_test(string_array_a[1],(float)1); z_origin_float=-((float)num_test(string_array_a[2],(float)(z_dim_short/2)))/fabs(z_pix_dim_float); fclose(fd2); sprintf(string_a,"%s %s.mnc | tail -3 | grep yspace > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2]); y_dim_short=num_test(string_array_a[0],(float)256); y_pix_dim_float=num_test(string_array_a[1],(float)1); y_origin_float=-((float)num_test(string_array_a[2],(float)(y_dim_short/2)))/fabs(y_pix_dim_float); fclose(fd2); sprintf(string_a,"%s %s.mnc | tail -3 | grep xspace > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2]); x_dim_short=num_test(string_array_a[0],(float)256); x_pix_dim_float=num_test(string_array_a[1],(float)1); x_origin_float=-((float)num_test(string_array_a[2],(float)(x_dim_short/2)))/fabs(x_pix_dim_float); fclose(fd2); sprintf(string_a,"%s %s.mnc | grep dimensions | grep time > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1]); if (strncmp(string_array_a[1],"time",4)==0) { sprintf(string_a,"%s %s.mnc | tail -4 | grep time > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s",string_a,(char *)&string_array_a[0],(char *)&string_array_a[1],(char *)&string_array_a[2]); volumes=num_test(string_array_a[0],(float)1); fclose(fd2); /*printf("4D file of %d volumes\n",volumes);*/ } sprintf(string_a,"/bin/rm %s.tmp",argv[1]); system(string_a); /* }}} */ /* {{{ create raw image */ sprintf(string_a,"%s -normalize %s %s.mnc > %s.img",MINCTORAW,type_string,argv[1],argv[1]); printf("%s\n",string_a); system(string_a); /* }}} */ /* {{{ input image */ buffera = (uchar*)malloc(volumes*x_dim_short*y_dim_short*z_dim_short*bytepix); sprintf(string_a,"%s.img",argv[1]); fd=fopen(string_a,"r"); fread(buffera,bytepix,volumes*x_dim_short*y_dim_short*z_dim_short,fd); fclose(fd); bufferb = (uchar*)malloc(volumes*x_dim_short*y_dim_short*z_dim_short*bytepix); /* }}} */ /* {{{ reorder x,y,z ? */ /* {{{ read x,y,z,t ordering */ sprintf(string_a,"%s %s.mnc | tail -3 > %s.tmp",MINCINFO,argv[1],argv[1]); system(string_a); sprintf(string_a,"%s.tmp",argv[1]); fd2=fopen(string_a,"r"); fscanf(fd2,"%s %s %s %s",(char *)&string_array_a[2],string_a,string_a,string_a); fscanf(fd2,"%s %s %s %s",(char *)&string_array_a[1],string_a,string_a,string_a); fscanf(fd2,"%s %s %s %s",(char *)&string_array_a[0],string_a,string_a,string_a); fclose(fd2); for(i=0; i<3; i++) { if (strncmp(string_array_a[i],"z",1)==0) order[i]=2; else { if (strncmp(string_array_a[i],"y",1)==0) order[i]=1; else order[i]=0; } } printf("Order: %s (%d) %s (%d) %s (%d)\n",string_array_a[0],order[0],string_array_a[1],order[1],string_array_a[2],order[2]); sprintf(string_a,"/bin/rm %s.tmp",argv[1]); system(string_a); /* }}} */ if ( (order[0]!=0) || (order[1]!=1) || (order[2]!=2) ) { int counters[3]; short dims[3]; dims[0]=x_dim_short; dims[1]=y_dim_short; dims[2]=z_dim_short; printf("Reordering dimensions.\n"); for(i=0; i0) /* this is necessary because of the MINC<->MEDx L<->R inconsistency */ { printf("Reversing x direction (data).\n"); for(i=0; iMEDx L<->R inconsistency */ x_pix_dim_float = -x_pix_dim_float; /* }}} */ /* {{{ reverse y ? */ if (y_pix_dim_float<0) { printf("Reversing y direction.\n"); y_origin_float += ((float)y_dim_short); y_pix_dim_float = -y_pix_dim_float; for(i=0; i_ J**4N2iFn)Q&Jc*Vub.u_kPJ[s:M0ɖqGqwVhM9&*rv7ijy2u)W~6#H6Vt/F_EJzكUhHQ&dGk,ʥk^שezvܚߚ*-;Ykaz[CW9A7U0\%?o H 0aSzٱf*& [[lh: ͨRU'kyy>Z =|ERW^T*+ƴ׌nFGjvN v5Nc"xu*WzkGrjmx܈RT"x;>iWǘcwxNDghggOD?zcӜO8k:ܤl;؋{ŷ~6.}s}:Åg_ֻ^kdz=޶{y5+_͟1?:TZA'k'^:xNv1 M`"AOw `0L]YX-6{>s4 ?9rߣN57qzK tdpPtC!NъUbI}puY2mȯP' H$QT!C8mc8 ;1=TX?Fu#Y7G CU0c{C!搇tVƔuQ74P/Ú"=>RRbX ) qce/5?X*166d gd{Ӝd58n2!RϚtFb^37xoo%{4:&CzT1AL)-Di.(*jQnJp7(=2듥tH鉤u!WDFX24XJW]Qݴ)~Jՠ'S@ 2O. JL*Aex(i_*U_KyQ^%)JfUNhuzl\e- -UX@Tl3Fy֏v&*Ubt%<;L-YjV hG@OMm"YŚiJu-_Vnw pKȥfNPzux\3ujn{,v"4Mz>qz ꔾز/_%˪m[WH6LSH7='L [ ;minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/html/images/Figure4.gif0000644000175000000620000002056512574624760024310 0ustar stevestaffGIF87a~H """333DDDfffwww,~HI8ͻ`(dihlp,tmx|pH,Ȥrl:ШttX Jz̖ՆtnnY 'zMb%~}z0V]/sP-t>*c:T8prmȼwelm  Ӿin panm^`oˁЭ#is(0 c@B%Xwl$0 '_:Xa@_ 9(4ˠ;A%3ـ[0v}Mp˖jG0s޽U؛0hέo%h`n]7{i-L*Y8`|Z (ϭ4X 3#܈,̍VCeʇ<ƍEA ,0`Rk#C]'umV@*sdtDʄJ $ -C X0=kRJsEQN\0w:17DA tEѲN zxlA k@pw0~Q$6 I>EͻRaX.9`G"g dL,0?P"l a:}1͒ !%R{3!79ҤM\_u+FQ:7;Z9Ts$.,`AEQXUVA+N6@n %RL p)@*Քʯ+ Q@8:{: WK@)7l5=iQ5D+=e'džXqMʜmģV]GQER-aI^ƬJq( )ZTtf%V9j136ԡb+Ne&:f0KI9S1bǮl kOZռE@ҽ6XIЁv^Պ³d*s•>,* ϵ4eA$ZhLңJLbV: 5N&+ ƾ'\))GetQ n0F+.H P* W?5){eOM˃ e^UcYù%Ӽї!U6ߝ\>|d.L\I>02dٲŷ2ܹrNI-UV ) )Q*\:6Z #ID"ae-˂\dYle4.' Z;l,{ |-A d1u_ׂtd۸8.ú֍],C; f\Sl^:<;JD # ZZrH77|(x&R`! v.q[t:7%U]d!t983\W\&#OǡԳL!k_“/Itklxos1 )2E&&~VDӓ2LSaU1UFfÅK؟1@UפuS«G3y괾?~fe6! a3ߓvll5hXݹٖ'汦I;O[maܮDcyǔ U璞teclufݫ6 fKI|5)ǐ~U-IrCa>,;`EM~ %yb6@VP|5/! .2 VsurvAWGݒβdtq06"Q}$8plё`4s'G%&}NS WYFB3q N F8*1J"\hU$OIpG8 ?WYLXw3 dx(@]KE8_ oh(#.~H!X`1Xa8mp8Xx؊8Xx؋tP4}& {Y˰c(Dc` x@=ܘ>a&9gj R TEQhwxy w ׎.XRLMlD%APxR;c6! rU,4zE ."i`Wph+L!ǀ%(,>_ޔVEnRPoX& Q.+T8C4uP1T$&mѓ\( ::jxiw[]T#8^b=$Y+@I v}b Y~j2EV6Xa ii8 cbp1ofLOzv>|*.?X4&6c< pYHL4"!B)*b{4aiƀ#? oVys,C}b]t>$F%{% TYL,vKJtwv:6`jfjTmՖm%-サ JYLDg?9dV"C)bvj@hhC>ďP7̩_bZxE7;h+F< [ ɟ_YK!'2|T3kȅTKfFe&TET 2RGgDq[{$QRشee12S; fub,\Mu956jLRGsiW Q̭̮>26$[u!-.7)?ʫDᩁ%IecTpG<%u6zpWa6d_fnٝ-e@s1a'-8L?6d) 4zr(ITs릳[Q&u-s3,!<\6uY6Fzx)Ie]2|%1'.iRw,UIuu&`D&c2&"?$_&(*,.02?4_69A ^ol@?L[BFJҶOoNJ%.$'+W#DB#"%ҿO_`aЊQGg%i&T*^nopc >!+)(V*-i,TB%q3;֣Ԕ)(U!zr85nQ< @$@]]ALr@YF9u$͍ trIZQd<'aUɭX]YhYL5Ϭq]@R|H:{pU"U.fX 6bZvaԌ&IX /ȝ (5bǩ?xdw_ QG8Qetu38㪕%V,UT!ђoH7s 5b"/9@=M-/BLpcPRabϪ hX)4/.:6blAFh-xBO+!yd`mE%6 Cޕ:0$À/0"yg3 e2u歐Lg{I;L.$$ۭm9$"Έ"l̖0k>{;6|{ kNa& A cb!(l3s|G+O*X @h8vqfg6b1sr/cDPŜU"eJ9.)@shXg;}3dTC=xP SPp>QKR $8aY ,W`A%8 Y~RL S?_Dwi"ЇEe}@Dht `bD!&Bo%&*ZJF<*(k>)n( }JciUN\FUFfX) ?q c1'i+&HC< *S*Qđ(hA2F U+$ͱ:V ,RE6".bGIFx8Pqʚe5HJܦ*?vSdžK"hrb I#LcZbS04A} hOtw>փ4 A˂B)4 BƊ 5ʀ`aTKbU9\BSa/Ct j$ЇS`7)ʌAʤ7,D|$Ou %:@*4Mej"xMsA㖆@Tn S6aBqU_N<3ZqU@ +9, A(*^zYnh`U@b$x68YDJQ ,XW]B  3 іiw8X-H8s\U.e.]x௔8iA^){fC&ks;$KI@"%ait\_6aW) &-]X*C(9~6g8}(rJsRAh%SI 8TlVVxer{8«;-D%KH!|'umJlg +gY`8\W^@gzw) & O?n5cIC( &lp5 pZVӳ @ d+ &qs  "ݐ櫠”bO sŷED4 b%>fH\ iF!R2s2q\/!n|>x1J' ,°Sv,1[CP*q$[tPZ9J^߉|m=$^QRMXhk>^$[~a}sp Pݒpk;l_ZjY[/э^J^IUZ9#a@rJ@L gGWP}cZA'Ա;@`q46FIxEH8|G_ӧ~}g_~}_'~_g~__ @@,@F!;/iqPi>Y#=1P"X"lM[@ 8%+ <9 B=(@£= (* 2T )qr.lyAjz 3M|(;A zD ;VlESY.ȝXU|@ @@h@^R-&a3^)a\^iefՠY 4# ,` @@bI@, a:ǜ+QiPYX܅@)CAz@0@t)ʒa`fj1VYry9@ %2!`J_Z(=Z?>^9o)f Ш|hc&`K%sFmXf.&g {Ł xwъ9%_h "ls.6''#%Hcz)8w\mɠFdj _[;@? \P$VٱaCy*XnȡlC^0Ѹ  h.(ڀ?"[%-_`7>;h K LL٩LWd._in ,@miob[?m~_> ryMտFY2V@}5F 6˙K4粬! PEW}ĀA ]ķ/ea`=dy IܘScS^XZ0k 2Ϊ+P5Ch6+Hg,bRr)ą4I*4abbJIHi6\3Xa&%i_aL-mu LzeF8Zm̖c>(QceeHHΕOD 5)$QDNnɓ';9!Q~҄L%(=yJURa&qN"%€kdMi PDJ̤ͨ(WJRҔδf8OrpwWXp?U4$\P bMj4 U~ d'vR$sbiӫ1(GPқ=x-fzubC260R~94E+:k$1b32z(rh>Y; gʎ1eYV8q[:C,nu”# bTjBeIZrcNVHRS,Lꆜ]r!+wNb.M!>j ѲTu D4ΨÑuei4Yl;H-\=hhnb9N ]3Y16 pw4܂ ;An8D2*f\Ս W1l h!C'1o.k|cu&DqjLS0s-n!1S 1!Sy%v8"n1m{u %:5\czEYzSHXjǘ9 eA΂G `1 #F Y ,^1zp`6AqkX:nULy#v2$t;'={ yiLO4ҒStiC"-MSZҤaՔD&]:=I2{^7j9\V1U~S=/7M-^5\U9V2Z)]-8 iQӅ\)D{~{lTI:pj,x75"9z0Mtb.RML_oh3[}kd>ЗvL,=7@A ariZ!9g;VzIzS*h?5|.7zv2+62p )LrGsQ~آry_0~Z$ex|mRИ*VS%oӑ>UR&vUS 0f\u27Yr"-e6Pqغ0ILJ}!/X `蹬k뚻k8W߁5-}ʏ9]XV0b qg^Q"euQ vieEq]qT X^b=86C&Z.>F3:%V2Ǔ}@H7&\ATo1'H:5;&=ld XV:2'0>'w@(;dQ=(Cqzb g,^d7S&7eC~k߁~?0&(f8++2)6clq~VC,ry1XD'6R36e2.-+&0 e&='K=/iex9#Kn#40s pe&hĂ&D3&l-c#qc,FW׀\htPRH͆i&qV7L; ;h{NdP',B_ȏ j#R!WCؐSrq~HF0d';ΡlpgN5H,ْ3%ѓDi@yPKKɔEG . v/`VR]apZ\Y!^a9Q҂l}ny^ZXt 8vu{:9g}$CfhD.zGYcF.(9Yyٚ9YyVa8fSY陥'A9f5.eP|"|RdUӶmLGzM[__$c&QEs=39,CƁuݶ&R G~ȋ*Tf(N"LVF2CCvUT4*'ˢXr1DAAA;ed2v$=c|eb0)z"CWFG)uKċJ8+֍X@uk?̖IZ(bM0>&l "$]Bυ`C؁fwaXC!G8CRVZ<~"Ɉ~m:S&,Vۧ_V=1 Fg-J868;ˈZ}nQYGƄ\RJN~2eա#i| [|?%L<;&#,߹'jWYywRZ10.3!bi!kͭ,E s:j(DF/ol B#@@ DPB >QD-^Ę pH%MDRJ"StRL5mI0&J;sTК>M~4JTRMꜙTU0W~V`WHVڟHݾ#Clm^}W`… FXbƍ?Ydʕ-_ƜYfΝ=ZhҥMFZj֭][lڵmMRn H@A 01Ef 8 |$ؾ@?>9Fo^˟W- ,r *0-̃ (<`@ǂJln/\lAB0 fOBh@ *W4%>Q q;r Hh|I'IOPB (K(}ÒCؔ͞I9-eQ1`G L tG5GQh7H#I(a3W]2K0ձKB};UTA)TtG{YKE\/LmĜQ %NeM9:kZyۜO@ POcM;s8۸e,H@B>ŜpYUSP/X|}wG(.!} YBV'PgW1U@w uPfScehNU{pt[sHc.}CMtzj8RZ`+_ E1 TѴӆK|2BV_ėT/PFeƮUp/Pji{ˀOn5}vcZ٣K(F;`G]XnSu[]ٕb .klFincY;%)`O2DA"IqGdSʂK8(%PzbZ!ĺ NNID(Αv jX {!ҕŮ 2v]K4U[t8D B('R#̘=0}4C 7T?"ҨFKJ;jғQmn}K+UMHenPlґl5lQJIբSH5IP>bLNUصgSrT&[B|a\@1EnUg=/.]M?'ёKʹ躐1-v74'HSUsYTuTi"$yF <9MT3x̣InbYC$ kK ЙrB;k muZV8&n̲̜԰ k.[y'{V %mvXCZů"LHZ쀖z9-=xM e(j T` iD:l7IEu<ڋJ>ij̇f%- LiG1TZPNU2%< ^G6R>.jXBJ;;8:Y@KLњ '7Znʶ%;kU/  8k.O+JRH9\Ei,7.p2&.yibr.n1;Ybxl9 OJ䀪WT ~[׸` l1icӉV2xmOEC"Vk}Qpb 㨀93sw5byc&9cA;8T~1 J‡Nj< Z(pTOaɆ_`Y1 BJ~FrB{ fE+\^tm]Zɍ.MΪ!2An;"LK)$mTOޔ_2k{ '1Qɀ"@XN:!,*;P>rj,18C:of#,$Hqxr,Oώf썟KKS:whОO#^''MθF (S1C]TgJ`W=]U˄ZQWk4>JU\ڸ`D Uqfaؿ|8.;9rj^, ~ ǀN4ռ:jlٱRrm|Ӻ3XCd\F"|~{ilj, .6Us6'E4*Xev%I8H3 &yHr|SmR|=8EQwAdM#G-%M,"۸192Y/q?9#{ѿca9bi,4T۴)Kd&+3- yԹ,hwI125ԣك г T*Lӑ0Z>"!rA Ծ 9a+ T)/ )ɑQ4+k),t!$+J8<"Q` $)ˁA+( s:H 󈲈{+(*.z{:xAEي#- 6gXA 5*(3 7 Aa<_LŮƸH ,BǛB$+,h,œZ>l FBg7*?똓3-Imtp3{*ul,D9")łdƆdȅ )M[.SR+5!*4GښJIHD ;X-kL; k+)ɔDɆ °J7{BtSJI\ˮt$QGd s3qIyF˚9ˁ31|TLθ,&Ԭ!.Ҥx1Ԭ=ՌKCׄ4לX۔ ([I4DΤt$4NɣSErs2"N8Ni2{,.+4t4NkkN8 ?o:V-{P*\'(%k?{PIO JIo2{O2*: 9@^ ! =Q]1Fk72;'Yj"!'Zآ=0PMV5=z2H+0Dx1@$n/R\3R4Oj$*1`m2"SDXS3H95$%!>S#7DO+}=625Y_"B͵GU#ѼͧJYPls5HVuQ7t{usE#!I@ $EO eּhm[mno΀;minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/html/images/Figure3.gif0000644000175000000620000004026012574624760024301 0ustar stevestaffGIF87au """333DDDfffwww,uI8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn `N~ov|upx  |gws y^x}TvsLsCs  ٷ8ضß Ķ1  E(A'Q*v0V Tl_J Ԍ@'mq1ŕqL0ID6rT Nˁ1@TR:N,#O6shN i Hm(T2kIw.ҺL+.((h,8@7D(K 0TօRNG5NZtA}m 46V_k1A7uM] MjJZy=6ga;(@ T-KY07}׮ƨЧa7E]aHi$K 5`#%H$!($!XwO*jXg'a%/bvn yՕl)&x wGc,.XfYٱM7)܈s&])@XL"#Oh&40o aWfA#>kYImaA.{UGjܱ@ZU52#yƧ@ 8(BZV8 e%fc:b]WZn 4'6DHƲy0Mɽk0u M6en噉:d0f Յ  +lB$9YĂ4oK_<, Jд$2ut da P\xǚG0O^tӣ u@iq"X! e,|_-6JMg|z'KN!:yTWCfڭ8Yfa,QJqO5u,50@T_SyC6#2d]dzydr^7sallŗtn<oI/%ןEhyk0]X םJZk{5F$Ny?#,Xq`B5QunG<3 'Ew8;)s@ E[!~LxB4C€TэXQ.:`LzӚYh:Rs7 C{s ZrBR Y-/``j2i(" plIJG)TRN7Ę0fA Y^I*L)@}ڥBzm8CkDT8f84US]aGH4 GǸXY¦<2'dL&clB?L#y7φ^ (E>'s'PS7O&sN ljM) Kl1}90#Ђ'JR-:sFXFRUaƯTJ dKz5th'Rn4 ⠽K?@~'zA.Fxxl)nō#EW V)2fEiC2O$"\(c(Og #ȥQe5j-##_,<T|LtcUY'LQ3!Y*9qP4ՉM!gix̧v0w]~!֧QL89:6':$_T<s s`!jb~V|H2#2t$ wcá1^c)Un-%iI[uhKLEv(?O֚&õ_Ҕm 6yS0j,l pRo1ulb䍻zJ@2UzÔoSO$/ 0bmɤX~0LIhF*j{5 x\nh5'IX좼CƺtQZ2N#%/YAD& uўR=bSoH ̄D.K8UWw~GbЇÏձ*g{xƟ*\DXI}ߧh栏چr1v;܀{>0_:K a83i#t†~Ww|!^.2E,IkLkyĪ0) dimjטGB-B-c8@MVDdw'M7!Cqxz0$h v=.*>Ra5F`78=uvpYVNgn`{8@Z $Av XTH:Y4P`P3Jhlhh+jHsHm8 ox0nO~xp8KH؈w>W"x1BhEX]W}`Vc/7ե*%2LƊbhvBeXEgB.]Ul9n\艙h:hi`HXبD؍8Xx(m$08옆h `!lb#Q x.cc(!kY UP i-r 0XCqaP X d 1k X 6AUigp}A z050=!pAgH]3k%fp"]q_)(:g1`U`me[^D_HaG@¸RWO q"jQ$ >-d1A ӰZIYECwT!{@p@#GRX5\ 䊰b}v S&`Ol_#_x6ydmyGc9&A1X?V fКCCC*D,5#.IvWc eK@[> \c7FVl)*gBd&@6XjUe ]kY pd'+gAIw`R]Iᐂ|k2T_xg\fT4E8e.K)4Mn ij\@4+rK[*j%ޫLQ@LLqtؠ ՄC伒gMTuB氒$G25ڝVsDϦ9 (M4X2NC~ZNN7Bԓq>B) OOjOP)XSP2h`J $vӾH Q*0u2QoRB#$EUr ĸK';',5W/5 7SŵSQlJS"aTIeL5)SPcPUzbFeV"i{lEn5-=YW6WW"XUL3vΧU*YQ yZfZXR%Pgˤ *L'\\7\rLVB ty1]FEڐͣ C_a1a?v*CaDRV 6,79$>kA7, FVyb| f eN'Nf4ug:Vu$sIϖi*B@ ljkkd$m$%qJk5ǺoVfcvvL @o^E6sKi64ӺsD ?1] pu@{ qw|Ԡv C5RA,{jrr sߴ5 Kح"jԳjFؐ] Dg`t Dt_ NEuMX7?\W;^7rCd`ںvtIq7w7 y!] xdra׳CdC} D'y|y tydwn@XgzoLzGs#$we{<y{5|+Ǘ?|| C}i !1" ogrL*Ї"8 V$0Hwg (MB.h'h2%SA x~Qn%U),(:1 2b>ey[@24t/L4@DR^̈XsiudR_+ B8u>{(߀Ғ.>+Ş~ *ܾ#kN㾨-Բ홰[Y>xʞpά^~?_6׳ /bA;^v=4|/sAN*&9W=mӴ<(KOd>?Q6I{_DAW >RLNB xe51۠ VS>v1~\ʈfI#9F_:rF!Sda%eXq*az_ؑ1 &adf.&ak9 `ӔOijEޙ aN ULODkA4&B;ZDE!{R:JHV\V+Nok̀E=KLxOַa,o]_t =:~?4bVt/^=q4O4UWug 0^@(}13.>&+(4 ͈JtbtZfDCF }`AH Ɵ$"Ÿ,6/- -MJLPMRQLVWX5,?9:#@%=ߢ'bF9ؓQQh iPkmnqrr!&A"azaƄéxi捚4NU"5n|AR|$PIbToX CLd G 6 pD.e8 ==wz2@ 6XTOpB)VRQ/eo~@Ju^1q ɽK @``YG-*dSU:hҥkifa@0VE3sUcxe< _ѥa@ȶӷ;)Ǡ_U~B*vz?WWKlH~v0V쇰 ̰6pBj8::^ڥT z ƿ8@#Ect𐫼EY&EeTrI&јx <ɧF*+krL2Hz$E0+*&-dȡ4TO@+xl'Y, ўBBiE RL3u&;Y,ur0,&Tb!4uVZXѬm,_,yPyʬ=bigrhVl RV;5yX'#H*)̬֌lZ)\ܲ"X=gnXsXx sQb}e5^Kª8F4]r s÷mА}*-اTHG>Y,@X5ˮi.#}SN6s@IlQM>sSm5()9=a 0@HlУyjrp?2Ia=]P6ma lp Jcb,/$һxP7B: /@:QbE׀#>TDd1G/%*p!A6b84aхz(dud~vXʃX2uc%-ّ,( HD*\ɒ(N" ՄT@%myKd! h@r0Tb< F܏kDfw}p^'\fS$" hU-R "L5B tMyO|lX&G8 b $AEP,Mv^Zcay[d h@$~;H"gKHKࠟP1QZrb$)37Q/̃,d'cI1iUX,*̩y2nHR)RNjUV\:ZJ(`_5-=T6ڒd%YjX n`bR٤/E% {Gp?ʼnīu¶ e,eMEa1]"V fնVMr +,Ü 6J[ny˄FeIDl $ȶi^;wXrM߆7Ajj%\s p8Kv[h< qe2Z;}3Pq/K +*=zFl `B=c,'Ǎ 0QNa XKblIoUd$V=`V)e<\- 0SrR+ 8{:E*zTA%r-=d_6^TY@|aKCKZ1 ,`:6RrUB92&Vaenel4dv= *;IQ??TxPvnu9՞e_F_ Zg°Q)ɦ6L ^1WIPQ}懿ﲐ* G"($ gX?\дF/[![Øz幤٨LЩxGv7%y8y`L-Ozԧ^g}6o!Zu?ۚ(^9ɽc՗$ܧV Km>uDp4vaIy߾[5OVJx.O-O{88x0Z >0!ھ仉a,p⛭ "X0!x t> eP`8+c쒳;AK Q;x_X&-94 _z=>GТDɛSB?K ;1ܗYZa~ < 7dQA-2ѣ#;=sD =@H0I9Q=D30/l8(';1خ>!1)tDXL [ h 1=lbCԛ˟[`@\ؐF)~=>3CtP$saƠxP|(-9L:;aɻF` o!qq (D z /ԻxyGᆇ{ qѸ 4h9hY|ƖL%6ˆ ('C1 ~t3Ͱ"XGPWtU* ʜjGp/ IR QIh  $P0@8OCqj3˳Ҹ?BD}4+KhJKIIK 8 :,#@AkgVFCВ.aC4M)#Q-{{qI1=ؕFƤQ2O#=ѼrW왰jhJi[ţlһky)b8?JeS߁YtƕHC}ƘӶ1 tٚ¡>HV]/B'12ʯ5r1z8rћe-zי2j敡\%H)(*A!ە2+ .o-_*1*-* >+%j2^r a1ૂ1&J+p10T,ƪ D}lE`f k2V9ޗ~m٦mSuQ^< .n>nNn^nnn~nnnnnnnnnoo.o[m)=8-s4hm&\Yk18+yֽP:"Xe tL>f&yIߋof 6Ue~S %j β qZtS7ݼ+Pj6Qw;,Q5V6cfآqQ@[[K )g ,7YENh$۫1Oj5 S9BbaL0!0r'=. U0B80 : -I_`P͍m&|srH ASGڰTǠ,hyEPx\eHbI5>9PDc8 $_lLOv&iA6PQ"l:7ёAjDuuLnx9G1w }?:,pVA mvӚ*Qʱob,,߸w)YyC/;Ņ,;!!ҳXl XKU+`20ހ9` 3"0&7!&:"k0 p"*Ú+jvāfDmeqr,cIfKvL ((:F@m4m`/RmRbT$AMYas7e EzA$ d*Wcze @/HsZ#P 4$1U,dx!g#V2=_V8nqof@eɌ9Z(0@T!)jXNz Q#\[D*)ЍMA{L~^2=l H =r6(],)>ch+\&MidqJMjqJ 9@9Ǽet.(FJa)յNzbJ@`i8RR;8PY$\>-R68-w c.{HYKJ<azѾ$xtlcc 7U"Kes6A5܆iZ >BV ͚ QHu*oI ]j u~U%HҊYުӭڽ/K҉ņDtdžLX5PFkt߬Jfz +EGpØ$8 WF050Kb|H+>V K"PUA*)Ǧ豫|L9LW]Q,w2yʯ@r<)Z.ϚreADTo]4n2$Y3w:3@І>4E3ю~4#-iE2ɇ:O'=e@3Hb"QԀfb\:dZQVϻ> ҁD.H#.Cd [ ͺDlH[i,2 `R!Jox^1<{v``4#>vO)uM^X6 [e跄h7=z$/¨Ai'98ِpb&cyiQ5i58^\JfjFz{@bf,?bbσsĜ̔ XF $Qe! `+I 7zsfAa&%agґ| /._恎qpe$kU2isY^< $Up !c^P5̡Ep ajE0/YAaQ!Kl JI׾+'[gJDѨpuuސ=T] ^݁ݿـ _}BT)[bAPX@G6\HGLMAYàh`LtE@p[܃Gt][@"DƱlIߜјؠ.L(|hMyLTI MU,ӔP!`}NDFn B=,<ֵ U̐$[UM]w`b<FF&^¤])FZUMG"BX$Й/R,1AFta/X Qǐ,ȃ#ZyT-Ȃ(8vHRc A,-R/LM-18CF?Vb4N?l! # (F>6]Pॅ^dŰ^ȀL= D`X2*am\Hr MFe@F %Qdu6jcA\$mn'GAo5k-=lxtg`[v2a Ud5Nj44e~臌FFSxtD@lF!aaMA\lQ ( 55< I Nlϥ܏F<ͧخˢK O,/ȃQ͌ܞ˼싽PIOa(YM(0BjlR`Ph\ ML#ndۉꃦ&5 լHBx vɍՍ"ђMߐOD%R9U=\_pxGelUםըyʚU1:edl\v]NT\ 0 oq*b0@dQBT df1 'uIXqL~1jWk l !@tMs2Qpq9@ 2:2s-0EJ=$XWDe&K'w-xF}L W8ėƲG"'+%0Ջˣ(X@'f22#"puAQ-Fnnv+iz.3%1Y?4, 9}9B#qa>t3EYf4G4HH4II4JJ4KK4LǴL4M״M4NN4OO4PP5QQ5R'R/5S7S?5TGTˉYь(hx+GBۙXLfLo q>C qD?YNgf5<J^^"'Z8246`' q44ȱ% 2GhlX2 Je5[H~XsHdIḨif{v_搼_lJLuKLڶhCq3 q|Vffp`*vhmx{u'rK!u%xJX sJ8oey/hq$VV l~hڤj+k7{`ʶGv0fɤw s˝$8tKwnwwpH__g8ly r"+wrֶLⷎ&Y͞Lsxc7q:c^zjlf$79j/g{gy{北ws&q^%܁W8ew}K~ 7{ƶ}.3F {{N8r7gN'g8h!ygl{{_ Ay ggxXuA|:u_>ںzzc4Y;T;'/ D;minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/html/AnalyzeFileInfo.html0000644000175000000620000007673112574624760024762 0ustar stevestaff ANALYZE (TM) Coordinate system / File Format

ANALYZETM 7.5 Coordinate System

The coordinate system employed by the ANALYZETM programs is left-handed, with the coordinate origin in the lower left corner. Thus, with the subject lying supine, the coordinate origin is on the right side of the body (x), at the back (y), and at the feet (z).

A major advantage of this convention is that the coordinate origin of each orthogonal orientation (transverse, coronal, and sagittal) lies in the lower left corner of the slice as it is displayed.

Orthogonal slices are numbered from one to the number of slices in that orientation. For example, a volume (x, y, z) dimensioned 128, 256, 48 has:

  • 48 transverse slices numbered 1 through 48
  • 128 sagittal slices numbered 1 through 128
  • 256 coronal slices numbered 1 through 256

Pixel coordinates are made with reference to the slice numbers from which the pixels come. Thus, the first pixel in the volume is referenced p(1,1,1) and not at p(0,0,0).

The names of the orthogonal planes can be changed in the Configure program.

This system is illustrated in the following diagrams.

Volume

  • Transverse slices are in the XY plane.
  • Sagittal slices are in the ZY plane.
  • Coronal slices are in the ZX plane.

Origin/Order Consistency

We refer to the ANALYZETM coordinate system as an origin/order system because it is based on an unambiguous single 3-D origin in the image data, and on the idea that the order of pixels in one line of a section, the order of lines in a section, and the order of sections in a volume all proceed from the origin. As shown in Figure 2, the projection of the origin is always displayed at the lower left of the screen, and the order of slices is always from the origin outward. Image data in any of these three orientations (if properly identified) will be correctly rendered as a 3D image (i.e. left-sided defects will appear on the left side of the 3-D rendering). Flipping data in any of the three orientations about two axes will also result in a correctly rendered 3-D image, but the two axis flip will move the effective location of the origin, and the orthogonal sections may appear upside down or left/right reversed when displayed. Flipping the data about one or three axes will ruin the integrity of the data and cause 3-D renderings to appear mirror-reversed (i.e. right-sided lesions will appear to be on the left). Mislabeling transverse data sets as sagittal or vice-versa will not destroy the coherence of the data, and renderings will be correct although the labelling of axes will be in error. However, mislabeling transverse or sagittal images as coronal (or vice-versa) will cause renderings to be mirror reversed.

It should be noted that the choice of a particular origin/order system is largely arbitrary, and that there are 16 possible origin/order systems based on the transverse orientation, and another 16 possible systems based on each of the other two orientations.

Other Consistent Systems

Origin/order systems provide many conveniences to the programmer, because the complete 3-D orientation of a volume image is coded into the order of pixels in the image file. Origin/order is not, however, the only logically consistent way of thinking about 3-D images made up of 2-D sections. Those who work with sectional images which are not inherently registered by the imaging modality (such as serial microscopic sections or photographs of gross tissue specimens) may naturally think of the sections in a view/order system.

View/order consistency is not concerned with the maintenance of a single unambiguous 3-D origin, it simply posits a relationship which should hold between the orientation of the sectional images and their order in the data file. A view/order based system would accept sectional images in any of six orientations, and would assign the x, y, and z axis to the row, column, and slice directions of the image data regardless of the physical section orientation. The main advantage of the view/order concept is that it is highly intuitive and is completely insensitive to orientation. Its major drawback is that the location of the origin and orientations of the axes will vary depending upon the section orientation. Figure 3 illustrates the view order system which most closely matches the origin/order system of ANALYZETM, i.e., it is a left-handed coordinate system based on the transverse ANALYZETM specification. Note carefully the nconsistencies between this system and the one used in ANALYZETM. For example, the first possible transverse orientation of Figure 3 is completely compatible with the ANALYZETM format, while the second transverse format is not. If images in this second transverse format are loaded into ANALYZETM, the 3-D coherence of the image will remain intact, but the initial 3-D rendering will be upside down. On the other hand, neither of the Coronal orientations of Figure 3 are compatible with ANALYZETM,and loading images in this order will cause a mirror-reversed 3-D rendering.

Other Systems

Finally, other systems of data order may be completely arbitrary. Figure 4 illustrates the conventions currently in use at the Mayo Foundation for 3-D MR images. The transverse standard is also the current convention for CT. Note that the conventions describe a consistently right-handed system, but it is not an origin/order system because that the location of the 3-D origin is ambiguous. The order of coronal images implies that the origin is at the back of the head, while the orientation of the transverse and sagittal images clearly indicates it is at the front. Note also that it is not a consistent view/order system, and to add further confusion, the sagittal orientation is standard only for GE and Siemens and is not followed by other manufacturers.

The ANALYZETM functions for reading GE and Siemens MR tapes correctly convert the above orientations into the ANALYZETM system, but this means the orientation and order of sections in ANALYZE TM differs from that seen on the MR console.

We strongly suggest a carefully review of data order conventions whenever integrating a new source of images into the ANALYZETM system. Only properly labelled images in the correct orientation can be accurately rendered by ANALYZETM, and mirror-reversed images are often almost impossible to detect.


ANALYZETM 7.5 File Format

The image database is the system of files that the ANALYZETM package uses to organize and access image data on the disk. Facilities are provided for converting data from a number of sources for use with the package. A description of the database format is provided to aid developers in porting images from other sources for use with the ANALYZE TM system. An ANALYZETM image database consists of at least two files:

  • an image file
  • a header file

The files have the same name being distinguished by the extensions .img for the image file and .hdr for the header file. Thus, for the image database heart, there are the UNIX files heart.img and heart.hdr. The ANALYZETM programs all refer to this pair of files as a single entity named heart.

Image File

The format of the image file is very simple containing usually uncompressed pixel data for the images in one of several possible pixel formats:

  • 1 bit              packed binary (slices must begin on byte boundaries)
  • 8 bit             8 bits per pixel (unsigned char)
  • 16 bit           16 bits per pixel (signed short)
  • 32 bit           32 bits per pixel signed integers, or floating point
  • 64 bit           64 bits per pixel; double precision, floating point, or complex.
  • 24 bit           RGB , 8-bits per channel Red, Green, Blue.

Header File

The header file is represented here as a `C' structure which describes the dimensions and history of the pixel data. The header structure consists of three substructures:

header_key               describes the header
image_dimension    describes image sizes
data_history             optional

ANALYZETM Header File Format (analyze_db.h)

struct header_key       /* header key   */ 
       {                                /* off + size      */
       int sizeof_hdr                   /* 0 + 4           */
       char data_type[10];              /* 4 + 10          */
       char db_name[18];                /* 14 + 18         */
       int extents;                     /* 32 + 4          */
       short int session_error;         /* 36 + 2          */
       char regular;                    /* 38 + 1          */
       char hkey_un0;                   /* 39 + 1          */
       };                               /* total=40 bytes  */
       struct image_dimension 
       {                                /* off + size      */
       short int dim[8];                /* 0 + 16          */
       short int unused8;               /* 16 + 2          */
       short int unused9;               /* 18 + 2          */
       short int unused10;              /* 20 + 2          */
       short int unused11;              /* 22 + 2          */
       short int unused12;              /* 24 + 2          */
       short int unused13;              /* 26 + 2          */
       short int unused14;              /* 28 + 2          */
       short int datatype;              /* 30 + 2          */
       short int bitpix;                /* 32 + 2          */
       short int dim_un0;               /* 34 + 2          */
       float pixdim[8];                 /* 36 + 32         */
       float funused8;                  /* 68 + 4          */
       float funused9;                  /* 72 + 4          */
       float funused10;                 /* 76 + 4          */
       float funused11;                 /* 80 + 4          */
       float funused12;                 /* 84 + 4          */
       float funused13;                 /* 88 + 4          */
       float compressed;                /* 92 + 4          */
       float verified;                  /* 96 + 4          */
       int glmax,glmin;                 /* 100 + 8         */
       };                               /* total=108 bytes */
struct data_history       
       {                                /* off + size      */
       char descrip[80];                /* 0 + 80          */
       char aux_file[24];               /* 80 + 24         */
       char orient;                     /* 104 + 1         */
       char originator[10];             /* 105 + 10        */
       char generated[10];              /* 115 + 10        */
       char scannum[10];                /* 125 + 10        */
       char patient_id[10];             /* 135 + 10        */
       char exp_date[10];               /* 145 + 10        */
       char exp_time[10];               /* 155 + 10        */
       char hist_un0[3];                /* 165 + 3         */
       int views                        /* 168 + 4         */
       int vols_added;                  /* 172 + 4         */
       int start_field;                 /* 176 + 4         */
       int field_skip;                  /* 180 + 4         */
       int omax, omin;                  /* 184 + 8         */
       int smax, smin;                  /* 192 + 8         */
       };
struct dsr
       { 
       struct header_key hk;            /* 0 + 40          */
       struct image_dimension dime;     /* 40 + 108        */
       struct data_history hist;        /* 148 + 200       */
       };                               /* total= 348 bytes*/   


Comments

The header format is flexible and can be extended for new user-defined data types. The essential structures of the header are the header_key and the image_dimension.

The required elements in the header_key substructure are:

int sizeof_header      Must indicate the byte size of the header file.
int extents              Should be 16384, the image file is created as contiguous with a minimum extent size.
char regular              Must be `r' to indicate that all images and volumes are the same size.

The image_dimension substructure describes the organization and size of the images. These elements enable the database to reference images by volume and slice number. Explanation of each element follows:

short int dim[];      /* array of the image dimensions */
dim[0]      Number of dimensions in database; usually 4
dim[1]      Image X dimension; number of pixels in an image row
dim[2]      Image Y dimension; number of pixel rows in slice
dim[3]      Volume Z dimension; number of slices in a volume
cdim[4]      Time points, number of volumes in database.
char vox_units[4]     specifies the spatial units of measure for a voxel
char cal_units[4]      specifies the name of the calibration unit
short int datatype      /* datatype for this image set */
/*Acceptable values for datatype are*/
#define DT_NONE                        0
#define DT_UNKNOWN               0      /*Unknown data type*/
#define DT_BINARY                    1      /*Binary (1 bit per voxel)*/
#define DT_UNSIGNED_CHAR     2      /*Unsigned character (8 bits per voxel)*/
#define DT_SIGNED_SHORT        4      /*Signed short (16 bits per voxel)*/
#define DT_SIGNED_INT             8      /*Signed integer (32 bits per voxel)*/
#define DT_FLOAT                     16     /*Floating point (32 bits per voxel)*/
#define DT_COMPLEX                32     /*Complex (64 bits per voxel; 2 floating point numbers)
#define DT_DOUBLE                   64     /*Double precision (64 bits per voxel)*/
#define DT_RGB                         128    /* */
#define DT_ALL                         255    /* */
short int bitpix;        /* number of bits per pixel; 1, 8, 16, 32, or 64. */
short int dim_un0;   /* unused */
float pixdim[];       Parallel array to dim[], giving real world measurements in mm. and ms.
pixdim[1];      voxel width in mm.
pixdim[2];      voxel height in mm.
pixdim[3];      slice thickness in mm.
float vox_offset;      byte offset in the .img file at which voxels start. This value can be
                                   negative to specify that the absolute value is applied for every image
                                   in the file.
float calibrated Max, Min    specify the range of calibration values
int glmax, glmin;    The maximum and minimum pixel values for the entire database.

The data_history substructure is not required, but the orient field is used to indicate individual slice orientation and determines whether the Movie program will attempt to flip the images before displaying a movie sequence.

orient:       slice orientation for this dataset.
0      transverse unflipped
1      coronal unflipped
2      sagittal unflipped
3      transverse flipped
4      coronal flipped
5      sagittal flipped

Sample Program

Any image data can be ported to the ANALYZETM system by creating the appropriate image and header files. Although header files can be created with the Header Edit program, the following C program is provided to illustrate how to make an ANALYZETM image database header file given the critical image dimensions as parameters. For example;

make_header heart.hdr 128 128 97 3 CHAR 255 0

Makes the header file heart.hdr with the following dimensions:

  • x dimension = 128
  • y dimension = 128
  • slices/volume = 97
  • volumes in file = 3
  • bits/pixel = 8
  • global max = 255
  • global min = 0

/* This program creates an ANALYZETM database header  */
/*
 * (c) Copyright, 1986-1995
 * Biomedical Imaging Resource
 * Mayo Foundation
 *
 * to compile:
 *
 *    cc -o make_hdr make_hdr.c
 *
 */
#include <stdio.h>
#include "dbh.h"

main(argc,argv) /* file x y z t datatype max min */
int argc;
char **argv;
{
    int i;
    struct dsr hdr;
    FILE *fp;
    static char DataTypes[9][12] = {"UNKNOWN", "BINARY",
          "CHAR", "SHORT", "INT","FLOAT", "COMPLEX", 
          "DOUBLE", "RGB"};
                                                           
    static int DataTypeSizes[9] = {0,1,8,16,32,32,64,64,24};
    
    if(argc != 9)
    {
                       usage();
        exit(0);
    }
    memset(&hdr,0, sizeof(struct dsr));
    for(i=0;i<8;i++)
               hdr.dime.pixdim[i] = 0.0;
   
    hdr.dime.vox_offset  = 0.0;
    hdr.dime.funused1    = 0.0;
    hdr.dime.funused2    = 0.0;
    hdr.dime.funused3    = 0.0;
    hdr.dime.cal_max     = 0.0;
    hdr.dime.cal_min     = 0.0;
  
    
    hdr.dime.datatype = -1;

    for(i=1;i<=8;i++)
               if(!strcmp(argv[6],DataTypes[i]))
               {
                       hdr.dime.datatype = (1<<(i-1));
                       hdr.dime.bitpix = DataTypeSizes[i];
                       break;
               }
               
    if(hdr.dime.datatype <= 0)
    {
               printf("<%s> is an unacceptable datatype \n\n", argv[6]);
               usage();
        exit(0);
    }
 
    if((fp=fopen(argv[1],"w"))==0)
    {
        printf("unable to create: %s\n",argv[1]);
        exit(0);
    }

    hdr.dime.dim[0] = 4;  /* all Analyze images are taken as 4 dimensional */
    hdr.hk.regular = 'r';
    hdr.hk.sizeof_hdr = sizeof(struct dsr);

    hdr.dime.dim[1] = atoi(argv[2]);  /* slice width  in pixels */
    hdr.dime.dim[2] = atoi(argv[3]);  /* slice height in pixels */
    hdr.dime.dim[3] = atoi(argv[4]);  /* volume depth in slices */
    hdr.dime.dim[4] = atoi(argv[5]);  /* number of volumes per file */

    hdr.dime.glmax  = atoi(argv[7]);  /* maximum voxel value  */
    hdr.dime.glmin  = atoi(argv[8]);  /* minimum voxel value */
    



/*     Set the voxel dimension fields: 
       A value of 0.0 for these fields implies that the value is unknown.
         Change these values to what is appropriate for your data
         or pass additional command line arguments     */      
         
    hdr.dime.pixdim[1] = 0.0; /* voxel x dimension */
    hdr.dime.pixdim[2] = 0.0; /* voxel y dimension */
    hdr.dime.pixdim[3] = 0.0; /* pixel z dimension, slice thickness */
    
/*   Assume zero offset in .img file, byte at which pixel
       data starts in the image file */

    hdr.dime.vox_offset = 0.0; 
    
/*   Planar Orientation;    */
/*   Movie flag OFF: 0 = transverse, 1 = coronal, 2 = sagittal
     Movie flag ON:  3 = transverse, 4 = coronal, 5 = sagittal  */  

    hdr.hist.orient     = 0;  
    
/*   up to 3 characters for the voxels units label; i.e. mm., um., cm. */               */

    strcpy(hdr.dime.vox_units," ");
   
/*   up to 7 characters for the calibration units label; i.e. HU */

    strcpy(hdr.dime.cal_units," ");  
    
/*     Calibration maximum and minimum values;  
       values of 0.0 for both fields imply that no 
       calibration max and min values are used    */

    hdr.dime.cal_max = 0.0; 
    hdr.dime.cal_min = 0.0;

    fwrite(&hdr,sizeof(struct dsr),1,fp);
    fclose(fp);
}

usage()
{
   printf("usage:  make_hdr name.hdr x y z t datatype max min \n\n");
   printf("  name.hdr = the name of the header file\n");
   printf("  x = width, y = height,  z = depth,  t = number of volumes\n");
   printf("  acceptable datatype values are: BINARY, CHAR, SHORT,\n");
   printf("                 INT, FLOAT, COMPLEX, DOUBLE, and RGB\n");
   printf("  max = maximum voxel value,  min = minimum voxel value\n");
}

The following program displays information in an Analyze header file.

#include <stdio.h>
#include "dbh.h"

void ShowHdr(char *, struct dsr *);
void swap_long(unsigned char *);
void swap_short(unsigned char *);

main(argc,argv) 
int argc;
char **argv;
    {
    struct dsr hdr;
    int size;
    double cmax, cmin;
    FILE *fp;
    
       if((fp=fopen(argv[1],"r"))==NULL)
    {
        fprintf(stderr,"Can't open:<%s>\n", argv[1]);
        exit(0);
    }
    fread(&hdr,1,sizeof(struct dsr),fp);

       if(hdr.dime.dim[0] < 0 || hdr.dime.dim[0] > 15)
               swap_hdr(&hdr);
    
     ShowHdr(argv[1], &hdr);
    

     }
     
        


void ShowHdr(fileName,hdr)
struct dsr *hdr;
char *fileName;
{
int i;
char string[128];
printf("Analyze Header Dump of: <%s> \n", fileName);
/* Header Key */
printf("sizeof_hdr: <%d> \n", hdr->hk.sizeof_hdr);
printf("data_type:  <%s> \n", hdr->hk.data_type);
printf("db_name:    <%s> \n", hdr->hk.db_name);
printf("extents:    <%d> \n", hdr->hk.extents);
printf("session_error: <%d> \n", hdr->hk.session_error);
printf("regular:  <%c> \n", hdr->hk.regular);
printf("hkey_un0: <%c> \n", hdr->hk.hkey_un0);

/* Image Dimension */
for(i=0;i<8;i++)
       printf("dim[%d]: <%d> \n", i, hdr->dime.dim[i]);
       
       strncpy(string,hdr->dime.vox_units,4);
       printf("vox_units:  <%s> \n", string);
       
       strncpy(string,hdr->dime.cal_units,8);
       printf("cal_units: <%s> \n", string);
       printf("unused1:   <%d> \n", hdr->dime.unused1);
       printf("datatype:  <%d> \n", hdr->dime.datatype);
       printf("bitpix:    <%d> \n", hdr->dime.bitpix);
       
for(i=0;i<8;i++)
       printf("pixdim[%d]: <%6.4f> \n",i, hdr->dime.pixdim[i]);
       
printf("vox_offset: <%6.4> \n",  hdr->dime.vox_offset);
printf("funused1:   <%6.4f> \n", hdr->dime.funused1);
printf("funused2:   <%6.4f> \n", hdr->dime.funused2);
printf("funused3:   <%6.4f> \n", hdr->dime.funused3);
printf("cal_max:    <%6.4f> \n", hdr->dime.cal_max);
printf("cal_min:    <%6.4f> \n", hdr->dime.cal_min);
printf("compressed: <%d> \n", hdr->dime.compressed);
printf("verified:   <%d> \n", hdr->dime.verified);
printf("glmax:      <%d> \n", hdr->dime.glmax);
printf("glmin:      <%d> \n", hdr->dime.glmin);

/* Data History */
strncpy(string,hdr->hist.descrip,80);
printf("descrip:  <%s> \n", string);
strncpy(string,hdr->hist.aux_file,24);
printf("aux_file: <%s> \n", string);
printf("orient:   <%d> \n", hdr->hist.orient);

strncpy(string,hdr->hist.originator,10);
printf("originator: <%s> \n", string);

strncpy(string,hdr->hist.generated,10);
printf("generated: <%s> \n", string);


strncpy(string,hdr->hist.scannum,10);
printf("scannum: <%s> \n", string);

strncpy(string,hdr->hist.patient_id,10);
printf("patient_id: <%s> \n", string);

strncpy(string,hdr->hist.exp_date,10);
printf("exp_date: <%s> \n", string);

strncpy(string,hdr->hist.exp_time,10);
printf("exp_time: <%s> \n", string);

strncpy(string,hdr->hist.hist_un0,10);
printf("hist_un0: <%s> \n", string);

printf("views:      <%d> \n", hdr->hist.views);
printf("vols_added: <%d> \n", hdr->hist.vols_added);
printf("start_field:<%d> \n", hdr->hist.start_field);
printf("field_skip: <%d> \n", hdr->hist.field_skip);
printf("omax: <%d> \n", hdr->hist.omax);
printf("omin: <%d> \n", hdr->hist.omin);
printf("smin: <%d> \n", hdr->hist.smax);
printf("smin: <%d> \n", hdr->hist.smin);

}


swap_hdr(pntr)
struct dsr *pntr;
       {
       swap_long(&pntr->hk.sizeof_hdr) ;
       swap_long(&pntr->hk.extents) ;
       swap_short(&pntr->hk.session_error) ;
       swap_short(&pntr->dime.dim[0]) ;
       swap_short(&pntr->dime.dim[1]) ;
       swap_short(&pntr->dime.dim[2]) ;
       swap_short(&pntr->dime.dim[3]) ;
       swap_short(&pntr->dime.dim[4]) ;
       swap_short(&pntr->dime.dim[5]) ;
       swap_short(&pntr->dime.dim[6]) ;
       swap_short(&pntr->dime.dim[7]) ;
       swap_short(&pntr->dime.unused1) ;
       swap_short(&pntr->dime.datatype) ;
       swap_short(&pntr->dime.bitpix) ;
       swap_long(&pntr->dime.pixdim[0]) ;
       swap_long(&pntr->dime.pixdim[1]) ;
       swap_long(&pntr->dime.pixdim[2]) ;
       swap_long(&pntr->dime.pixdim[3]) ;
       swap_long(&pntr->dime.pixdim[4]) ;
       swap_long(&pntr->dime.pixdim[5]) ;
       swap_long(&pntr->dime.pixdim[6]) ;
       swap_long(&pntr->dime.pixdim[7]) ;
       swap_long(&pntr->dime.vox_offset) ;
       swap_long(&pntr->dime.funused1) ;
       swap_long(&pntr->dime.funused2) ;
       swap_long(&pntr->dime.cal_max) ;
       swap_long(&pntr->dime.cal_min) ;
       swap_long(&pntr->dime.compressed) ;
       swap_long(&pntr->dime.verified) ;
       swap_short(&pntr->dime.dim_un0) ;
       swap_long(&pntr->dime.glmax) ;
       swap_long(&pntr->dime.glmin) ;
       }
       
swap_long(pntr)
unsigned char *pntr;
        {
        unsigned char b0, b1, b2, b3;

        b0 = *pntr;
        b1 = *(pntr+1);
        b2 = *(pntr+2);
        b3 = *(pntr+3);

        *pntr = b3;
        *(pntr+1) = b2;
        *(pntr+2) = b1;
        *(pntr+3) = b0;
        }
        
swap_short(pntr)
unsigned char *pntr;
        {
        unsigned char b0, b1;

        b0 = *pntr;
        b1 = *(pntr+1);

        *pntr = b1;
        *(pntr+1) = b0;
        }

minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/spm_hwrite.m0000644000175000000620000001053512574624760022436 0ustar stevestafffunction [s] = spm_hwrite(P,DIM,VOX,SCALE,TYPE,OFFSET,ORIGIN,DESCRIP) % writes a header % FORMAT [s] = spm_hwrite(P,DIM,VOX,SCALE,TYPE,OFFSET,ORIGIN,DESCRIP); % % P - filename (e.g 'spm' or 'spm.img') % DIM - image size [i j k [l]] (voxels) % VOX - voxel size [x y z [t]] (mm [sec]) % SCALE - scale factor % TYPE - datatype (integer - see spm_type) % OFFSET - offset (bytes) % ORIGIN - [i j k] of origin (default = [0 0 0]) % DESCRIP - description string (default = 'spm compatible') % % s - number of elements successfully written (should be 348) %___________________________________________________________________________ % % spm_hwrite writes variables from working memory into a SPM/ANALYZE % compatible header file. The 'originator' field of the ANALYZE format has % been changed to ORIGIN in the SPM version of the header. funused1 % of the ANALYZE format is used for SCALE % % see also dbh.h (ANALYZE) spm_hread.m and spm_type.m % %__________________________________________________________________________ % @(#)spm_hwrite.m 2.2 99/10/29 % ensure correct suffix {.hdr} and open header file %--------------------------------------------------------------------------- P = P(P ~= ' '); q = length(P); if q>=4 & P(q - 3) == '.', P = P(1:(q - 4)); end; P = [P '.hdr']; % For byte swapped data-types, also swap the bytes around in the headers. mach = 'native'; if spm_type(TYPE,'swapped'), if spm_platform('bigend'), mach = 'ieee-le'; else, mach = 'ieee-be'; end; TYPE = spm_type(spm_type(TYPE)); end; fid = fopen(P,'w',mach); if (fid == -1), error(['Error opening ' P '. Check that you have write permission.']); end; %--------------------------------------------------------------------------- data_type = ['dsr ' 0]; P = [P ' ']; db_name = [P(1:17) 0]; % set header variables %--------------------------------------------------------------------------- DIM = DIM(:)'; if size(DIM,2) < 4; DIM = [DIM 1]; end VOX = VOX(:)'; if size(VOX,2) < 4; VOX = [VOX 0]; end dim = [4 DIM(1:4) 0 0 0]; pixdim = [0 VOX(1:4) 0 0 0]; vox_offset = OFFSET; funused1 = SCALE; glmax = 1; glmin = 0; bitpix = 0; descrip = zeros(1,80); aux_file = ['none ' 0]; origin = [0 0 0 0 0]; %--------------------------------------------------------------------------- if TYPE == 1; bitpix = 1; glmax = 1; glmin = 0; end if TYPE == 2; bitpix = 8; glmax = 255; glmin = 0; end if TYPE == 4; bitpix = 16; glmax = 32767; glmin = 0; end if TYPE == 8; bitpix = 32; glmax = (2^31-1); glmin = 0; end if TYPE == 16; bitpix = 32; glmax = 1; glmin = 0; end if TYPE == 64; bitpix = 64; glmax = 1; glmin = 0; end %--------------------------------------------------------------------------- if nargin >= 7; origin = [ORIGIN(:)' 0 0]; end if nargin < 8; DESCRIP = 'spm compatible'; end d = 1:min([length(DESCRIP) 79]); descrip(d) = DESCRIP(d); fseek(fid,0,'bof'); % write (struct) header_key %--------------------------------------------------------------------------- fwrite(fid,348, 'int32'); fwrite(fid,data_type, 'char' ); fwrite(fid,db_name, 'char' ); fwrite(fid,0, 'int32'); fwrite(fid,0, 'int16'); fwrite(fid,'r', 'char' ); fwrite(fid,'0', 'char' ); % write (struct) image_dimension %--------------------------------------------------------------------------- fseek(fid,40,'bof'); fwrite(fid,dim, 'int16'); fwrite(fid,'mm', 'char' ); fwrite(fid,0, 'char' ); fwrite(fid,0, 'char' ); fwrite(fid,zeros(1,8), 'char' ); fwrite(fid,0, 'int16'); fwrite(fid,TYPE, 'int16'); fwrite(fid,bitpix, 'int16'); fwrite(fid,0, 'int16'); fwrite(fid,pixdim, 'float'); fwrite(fid,vox_offset, 'float'); fwrite(fid,funused1, 'float'); fwrite(fid,0, 'float'); fwrite(fid,0, 'float'); fwrite(fid,0, 'float'); fwrite(fid,0, 'float'); fwrite(fid,0, 'int32'); fwrite(fid,0, 'int32'); fwrite(fid,glmax, 'int32'); fwrite(fid,glmin, 'int32'); % write (struct) image_dimension %--------------------------------------------------------------------------- fwrite(fid,descrip, 'char'); fwrite(fid,aux_file, 'char'); fwrite(fid,0, 'char'); fwrite(fid,origin, 'int16'); if fwrite(fid,zeros(1,85), 'char')~=85 fclose(fid); spm_unlink(P); error(['Error writing ' P '. Check your disk space.']); end s = ftell(fid); fclose(fid); minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/make_hdr.c0000644000175000000620000000763512574624760022024 0ustar stevestaff/* This program creates an ANALYZETM database header */ /* http://www.mayo.edu/bir/analyze/AnalyzeFileInfo.html */ /* * (c) Copyright, 1986-1995 * Biomedical Imaging Resource * Mayo Foundation * * to compile: * * cc -o make_hdr make_hdr.c * */ #include #include #include "dbh.h" void usage(); main(int argc, char *argv[]){ int i; struct dsr hdr; FILE *fp; static char DataTypes[9][12] = {"UNKNOWN", "BINARY", "CHAR", "SHORT", "INT","FLOAT", "COMPLEX", "DOUBLE", "RGB"}; static int DataTypeSizes[9] = {0,1,8,16,32,32,64,64,24}; if(argc != 12){ usage(); exit(0); } memset(&hdr,0, sizeof(struct dsr)); hdr.dime.vox_offset = 0.0; hdr.dime.funused1 = 0.0; hdr.dime.funused2 = 0.0; hdr.dime.funused3 = 0.0; hdr.dime.cal_max = 0.0; hdr.dime.cal_min = 0.0; hdr.dime.datatype = -1; for(i=1;i<=8;i++) if(!strcmp(argv[9],DataTypes[i])) { hdr.dime.datatype = (1<<(i-1)); hdr.dime.bitpix = DataTypeSizes[i]; break; } if(hdr.dime.datatype <= 0) { printf("<%s> is an unacceptable datatype \n\n", argv[9]); usage(); exit(0); } if((fp=fopen(argv[1],"w"))==0) { printf("unable to create: %s\n",argv[1]); exit(0); } hdr.dime.dim[0] = 4; /* all Analyze images are taken as 4 dimensional */ hdr.hk.regular = 'r'; hdr.hk.sizeof_hdr = sizeof(struct dsr); hdr.dime.dim[1] = atoi(argv[2]); /* slice width in pixels */ hdr.dime.dim[2] = atoi(argv[3]); /* slice height in pixels */ hdr.dime.dim[3] = atoi(argv[4]); /* volume depth in slices */ hdr.dime.dim[4] = atoi(argv[5]); /* number of volumes per file */ /* Set the voxel dimension fields: A value of 0.0 for these fields implies that the value is unknown. Change these values to what is appropriate for your data or pass additional command line arguments */ hdr.dime.pixdim[0] = 4; /* all Analyze images are taken as 4 dimensional */ hdr.dime.pixdim[1] = atof(argv[6]); /* voxel x dimension */ hdr.dime.pixdim[2] = atof(argv[7]); /* voxel y dimension */ hdr.dime.pixdim[3] = atof(argv[8]); /* pixel z dimension, slice thickness */ hdr.dime.glmax = atoi(argv[10]); /* maximum voxel value */ hdr.dime.glmin = atoi(argv[11]); /* minimum voxel value */ /* Assume zero offset in .img file, byte at which pixel data starts in the image file */ hdr.dime.vox_offset = 0.0; /* Planar Orientation; */ /* Movie flag FALSE: 0 = transverse, 1 = coronal, 2 = sagittal Movie flag TRUE: 3 = transverse, 4 = coronal, 5 = sagittal */ hdr.hist.orient = 0; /* up to 3 characters for the voxels units label; i.e. mm., um., cm. */ strcpy(hdr.dime.vox_units," "); /* up to 7 characters for the calibration units label; i.e. HU */ strcpy(hdr.dime.cal_units," "); /* Calibration maximum and minimum values; values of 0.0 for both fields imply that no calibration max and min values are used */ hdr.dime.cal_max = 0.0; hdr.dime.cal_min = 0.0; fwrite(&hdr,sizeof(struct dsr),1,fp); fclose(fp); } void usage(){ printf("usage: make_hdr name.hdr x y z t xs ys zs datatype max min \n\n"); printf(" name.hdr = the name of the header file\n"); printf(" x = width, y = height, z = depth, t = number of volumes\n"); printf(" xs = x step, ys = y step, zs = z step\n"); printf(" acceptable datatype values are: BINARY, CHAR, SHORT,\n"); printf(" INT, FLOAT, COMPLEX, DOUBLE, and RGB\n"); printf(" max = maximum voxel value, min = minimum voxel value\n"); } minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/spm_realign.m0000644000175000000620000004042512574624760022556 0ustar stevestafffunction spm_realign(P,flags) % Estimation of within modality rigid body movement parameters % FORMAT spm_realign(P,flags) % % P - matrix of filenames {one string per row} % All operations are performed relative to the first image. % ie. Coregistration is to the first image, and resampling % of images is into the space of the first image. % For multiple sessions, P should be a cell array, where each % cell should be a matrix of filenames. % % flags - a structure containing various options. The fields are: % quality - Quality versus speed trade-off. Highest quality % (1) gives most precise results, whereas lower % qualities gives faster realignment. % The idea is that some voxels contribute little to % the estimation of the realignment parameters. % This parameter is involved in selecting the number % of voxels that are used. % % fwhm - The FWHM of the Gaussian smoothing kernel (mm) % applied to the images before estimating the % realignment parameters. % % sep - the default separation (mm) to sample the images. % % rtm - Register to mean. If field exists then a two pass % procedure is to be used in order to register the % images to the mean of the images after the first % realignment. % % PW - a filename of a weighting image (reciprocal of % standard deviation). If field does not exist, then % no weighting is done. % % hold - hold for interpolation (see spm_slice_vol and % spm_sample_vol). % %__________________________________________________________________________ % % Inputs % A series of *.img conforming to SPM data format (see 'Data Format'). % % Outputs % The parameter estimation part writes out ".mat" files for each of the % input images. The details of the transformation are displayed in the % results window as plots of translation and rotation. % A set of realignment parameters are saved for each session, named: % realignment_params_*.txt. %__________________________________________________________________________ % % The `.mat' files. % % This simply contains a 4x4 affine transformation matrix in a variable `M'. % These files are normally generated by the `realignment' and % `coregistration' modules. What these matrixes contain is a mapping from % the voxel coordinates (x0,y0,z0) (where the first voxel is at coordinate % (1,1,1)), to coordinates in millimeters (x1,y1,z1). By default, the % the new coordinate system is derived from the `origin' and `vox' fields % of the image header. % % x1 = M(1,1)*x0 + M(1,2)*y0 + M(1,3)*z0 + M(1,4) % y1 = M(2,1)*x0 + M(2,2)*y0 + M(2,3)*z0 + M(2,4) % z1 = M(3,1)*x0 + M(3,2)*y0 + M(3,3)*z0 + M(3,4) % % Assuming that image1 has a transformation matrix M1, and image2 has a % transformation matrix M2, the mapping from image1 to image2 is: M2\M1 % (ie. from the coordinate system of image1 into millimeters, followed % by a mapping from millimeters into the space of image2). % % These `.mat' files allow several realignment or coregistration steps to be % combined into a single operation (without the necessity of resampling the % images several times). The `.mat' files are also used by the spatial % normalisation module. %__________________________________________________________________________ % Ref: % Friston KJ, Ashburner J, Frith CD, Poline J-B, Heather JD & Frackowiak % RSJ (1995) Spatial registration and normalization of images Hum. Brain % Map. 2:165-189 %__________________________________________________________________________ % @(#)spm_realign.m 2.27 John Ashburner 99/10/26 if nargin==0, spm_realign_ui; return; end; def_flags = struct('quality',1,'fwhm',6,'sep',4.5,'hold',-8); if nargin < 2, flags = def_flags; else, fnms = fieldnames(def_flags); for i=1:length(fnms), if ~isfield(flags,fnms{i}), flags = setfield(flags,fnms{i},getfield(def_flags,fnms{i})); end; end; end; linfun = inline('fprintf('' %-60s%s'', x,sprintf(''\b'')*ones(1,60))'); if isempty(P), warning('Nothing to do'); return; end; if ~iscell(P), tmp = cell(1); tmp{1} = P; P = tmp; end; P = spm_vol(P); if isfield(flags,'PW'), flags.PW = spm_vol(flags.PW); end; if length(P)==1, linfun('Registering images..'); P{1} = realign_series(P{1},flags); save_parameters(P{1}); else, linfun('Registering together the first image of each session..'); Ptmp = P{1}(1); for s=2:prod(size(P)), Ptmp = [Ptmp ; P{s}(1)]; end; Ptmp = realign_series(Ptmp,flags); for s=1:prod(size(P)), M = Ptmp(s).mat*inv(P{s}(1).mat); for i=1:prod(size(P{s})), P{s}(i).mat = M*P{s}(i).mat; end; end; for s=1:prod(size(P)), linfun(['Registering together images from session ' num2str(s) '..']); P{s} = realign_series(P{s},flags); save_parameters(P{s}); end; end; % Save Realignment Parameters %--------------------------------------------------------------------------- linfun('Saving parameters..'); for s=1:prod(size(P)), for i=1:prod(size(P{s})), spm_get_space(P{s}(i).fname, P{s}(i).mat); end; end; plot_parameters(P); return; %_______________________________________________________________________ %_______________________________________________________________________ function P = realign_series(P,flags) % Realign a time series of 3D images to the first of the series. % FORMAT P = realign_series(P,flags) % P - a vector of volumes (see spm_vol) %----------------------------------------------------------------------- % P(i).mat is modified to reflect the modified position of the image i. % The scaling (and offset) parameters are also set to contain the % optimum scaling required to match the images. %_______________________________________________________________________ if prod(size(P))<2, return; end; lkp = [1 2 3 4 5 6]; if P(1).dim(3) < 3, lkp = [1 2 6]; end; % Points to sample in reference image %----------------------------------------------------------------------- skip = sqrt(sum(P(1).mat(1:3,1:3).^2)).^(-1)*flags.sep; d = P(1).dim(1:3); [x1,x2,x3]=ndgrid(1:skip(1):d(1),1:skip(2):d(2),1:skip(3):d(3)); x1 = x1(:); x2 = x2(:); x3 = x3(:); % Possibly mask an area of the sample volume. %----------------------------------------------------------------------- if isfield(flags,'PW'), [y1,y2,y3]=coords([0 0 0 0 0 0],P(1).mat,flags.PW.mat,x1,x2,x3); wt = spm_sample_vol(flags.PW,y1,y2,y3,1); msk = find(wt>0.01); x1 = x1(msk); x2 = x2(msk); x3 = x3(msk); wt = wt(msk); else, wt = []; end; n = prod(size(x1)); % Compute rate of change of chi2 w.r.t changes in parameters (matrix A) %----------------------------------------------------------------------- V = smooth_vol(P(1),flags.fwhm); [G,dG1,dG2,dG3] = spm_sample_vol(V,x1,x2,x3,flags.hold); clear V A0 = make_A(P(1).mat,x1,x2,x3,dG1,dG2,dG3,wt,lkp); b = G; if ~isempty(wt), b = b.*wt; end; %----------------------------------------------------------------------- if prod(size(P)) > 2, % Remove voxels that contribute very little to the final estimate. % Simulated annealing or something similar could be used to % eliminate a better choice of voxels - but this way will do for % now. It basically involves removing the voxels that contribute % least to the determinant of the inverse covariance matrix. spm_chi2_plot('Init','Eliminating Unimportant Voxels',... 'Fractional loss of quality','Iteration'); Alpha = spm_atranspa([A0 b]); det0 = det(Alpha); det1 = det0; spm_chi2_plot('Set',det1/det0); while det1/det0 > flags.quality, dets = zeros(size(A0,1),1); for i=1:size(A0,1), dets(i) = det(Alpha - spm_atranspa([A0(i,:) b(i)])); end; [junk,msk] = sort(det1-dets); msk = msk(1:round(length(dets)/10)); A0(msk,:) = []; b(msk,:) = []; G(msk,:) = []; x1(msk,:) = []; x2(msk,:) = []; x3(msk,:) = []; dG1(msk,:) = []; dG2(msk,:) = []; dG3(msk,:) = []; if ~isempty(wt), wt(msk,:) = []; end; Alpha = spm_atranspa([A0 b]); det1 = det(Alpha); spm_chi2_plot('Set',det1/det0); end; spm_chi2_plot('Clear'); end; %----------------------------------------------------------------------- if isfield(flags,'rtm'), count = ones(size(b)); ave = G; grad1 = dG1; grad2 = dG2; grad3 = dG3; end; spm_progress_bar('Init',length(P)-1,'Registering Images'); % Loop over images %----------------------------------------------------------------------- for i=2:length(P), V=smooth_vol(P(i),flags.fwhm); ss = Inf; countdown = -1; Hold = 1; % Begin with bi-linear interpolation. for iter=1:64, [y1,y2,y3] = coords([0 0 0 0 0 0],P(1).mat,P(i).mat,x1,x2,x3); msk = find((y1>=1 & y1<=d(1) & y2>=1 & y2<=d(2) & y3>=1 & y3<=d(3))); if length(msk)<32, error_message(P(i)); end; F = spm_sample_vol(V, y1(msk),y2(msk),y3(msk),Hold); if ~isempty(wt), F = F.*wt(msk); end; A = [A0(msk,:) F]; Alpha = spm_atranspa(A); Beta = A'*b(msk); soln = Alpha\Beta; p = [0 0 0 0 0 0 1 1 1 0 0 0]; p(lkp) = soln(1:(end-1)); P(i).mat = inv(spm_matrix(p))*P(i).mat; pss = ss; ss = sum((F*soln(end)-b(msk)).^2)/length(msk); if (pss-ss)/pss < 1e-8 & countdown == -1, % Stopped converging. % Switch to a better (slower) interpolation % and do two final iterations Hold = flags.hold; countdown = 2; end; if countdown ~= -1, if countdown==0, break; end; countdown = countdown -1; end; end; if isfield(flags,'rtm'), % Generate mean and derivatives of mean tiny = 5e-2; % From spm_vol_utils.c msk = find((y1>=(1-tiny) & y1<=(d(1)+tiny) &... y2>=(1-tiny) & y2<=(d(2)+tiny) &... y3>=(1-tiny) & y3<=(d(3)+tiny))); count(msk) = count(msk) + 1; [G,dG1,dG2,dG3] = spm_sample_vol(V,y1(msk),y2(msk),y3(msk),flags.hold); ave(msk) = ave(msk) + G.*soln(end); grad1(msk) = grad1(msk) + dG1.*soln(end); grad2(msk) = grad2(msk) + dG2.*soln(end); grad3(msk) = grad3(msk) + dG3.*soln(end); end; spm_progress_bar('Set',i-1); end; spm_progress_bar('Clear'); if ~isfield(flags,'rtm'), return; end; %_______________________________________________________________________ M=P(1).mat; A0 = make_A(M,x1,x2,x3,grad1./count,grad2./count,grad3./count,wt,lkp); if ~isempty(wt), b = (ave./count).*wt; else, b = (ave./count); end clear ave grad1 grad2 grad3 % Loop over images %----------------------------------------------------------------------- spm_progress_bar('Init',length(P),'Registering Images to Mean'); for i=1:length(P), V=smooth_vol(P(i),flags.fwhm); ss = Inf; countdown = -1; for iter=1:64, [y1,y2,y3] = coords([0 0 0 0 0 0],M,P(i).mat,x1,x2,x3); msk = find((y1>=1 & y1<=d(1) & y2>=1 & y2<=d(2) & y3>=1 & y3<=d(3))); if length(msk)<32, error_message(P(i)); end; F = spm_sample_vol(V, y1(msk),y2(msk),y3(msk),flags.hold); if ~isempty(wt), F = F.*wt(msk); end; A = [A0(msk,:) F]; Alpha = spm_atranspa(A); Beta = A'*b(msk); soln = Alpha\Beta; p = [0 0 0 0 0 0 1 1 1 0 0 0]; p(lkp) = soln(1:(end-1)); P(i).mat = inv(spm_matrix(p))*P(i).mat; pss = ss; ss = sum((F*soln(end)-b(msk)).^2)/length(msk); if (pss-ss)/pss < 1e-8 & countdown == -1 % Stopped converging. % Do three final iterations to finish off with countdown = 2; end; if countdown ~= -1 if countdown==0, break; end; countdown = countdown -1; end; end; spm_progress_bar('Set',i); end; spm_progress_bar('Clear'); % Since we are supposed to be aligning everything to the first % image, then we had better do so %----------------------------------------------------------------------- M = M/P(1).mat; for i=1:length(P) P(i).mat = M*P(i).mat; end return; %_______________________________________________________________________ %_______________________________________________________________________ function [y1,y2,y3]=coords(p,M1,M2,x1,x2,x3) % Rigid body transformation of a set of coordinates. M = (inv(M2)*inv(spm_matrix(p(1:6)))*M1); y1 = M(1,1)*x1 + M(1,2)*x2 + M(1,3)*x3 + M(1,4); y2 = M(2,1)*x1 + M(2,2)*x2 + M(2,3)*x3 + M(2,4); y3 = M(3,1)*x1 + M(3,2)*x2 + M(3,3)*x3 + M(3,4); return; %_______________________________________________________________________ %_______________________________________________________________________ function V = smooth_vol(P,fwhm) % Convolve the volume in memory. s = sqrt(sum(P.mat(1:3,1:3).^2)).^(-1)*(fwhm/sqrt(8*log(2))); x = round(6*s(1)); x = [-x:x]; y = round(6*s(2)); y = [-y:y]; z = round(6*s(3)); z = [-z:z]; x = exp(-(x).^2/(2*(s(1)).^2)); y = exp(-(y).^2/(2*(s(2)).^2)); z = exp(-(z).^2/(2*(s(3)).^2)); x = x/sum(x); y = y/sum(y); z = z/sum(z); i = (length(x) - 1)/2; j = (length(y) - 1)/2; k = (length(z) - 1)/2; V = zeros(P.dim(1:3)); spm_conv_vol(P,V,x,y,z,-[i j k]); return; %_______________________________________________________________________ %_______________________________________________________________________ function A = make_A(M,x1,x2,x3,dG1,dG2,dG3,wt,lkp) % Matrix of rate of change of weighted difference w.r.t. parameter changes p0 = [0 0 0 0 0 0 1 1 1 0 0 0]; A = zeros(prod(size(x1)),length(lkp)); for i=1:length(lkp) pt = p0; pt(lkp(i)) = pt(i)+1e-6; [y1,y2,y3] = coords(pt,M,M,x1,x2,x3); tmp = sum([y1-x1 y2-x2 y3-x3].*[dG1 dG2 dG3],2)/(-1e-6); if ~isempty(wt), A(:,i) = tmp.*wt; else, A(:,i) = tmp; end end return; %_______________________________________________________________________ %_______________________________________________________________________ function error_message(P) str = { 'There is not enough overlap in the images',... 'to obtain a solution.',... ' ',... 'Offending image:',... P.fname,... ' ',... 'Please check that your header information is OK.'}; spm('alert*',str,mfilename,sqrt(-1)); error('insufficient image overlap') return %_______________________________________________________________________ %_______________________________________________________________________ function plot_parameters(P) fg=spm_figure('FindWin','Graphics'); if ~isempty(fg), P = cat(1,P{:}); if length(P)<2, return; end; Params = zeros(prod(size(P)),12); for i=1:prod(size(P)), Params(i,:) = spm_imatrix(P(i).mat/P(1).mat); end % display results % translation and rotation over time series %------------------------------------------------------------------- spm_figure('Clear','Graphics'); ax=axes('Position',[0.1 0.65 0.8 0.2],'Parent',fg,'Visible','off'); set(get(ax,'Title'),'String','Image realignment','FontSize',16,'FontWeight','Bold','Visible','on'); x = 0.1; y = 0.9; for i = 1:min([prod(size(P)) 12]) text(x,y,[sprintf('%-4.0f',i) P(i).fname],'FontSize',10,'Interpreter','none','Parent',ax); y = y - 0.08; end if prod(size(P)) > 12 text(x,y,'................ etc','FontSize',10,'Parent',ax); end ax=axes('Position',[0.1 0.35 0.8 0.2],'Parent',fg,'XGrid','on','YGrid','on'); plot(Params(:,1:3),'Parent',ax) s = ['x translation';'y translation';'z translation']; text([2 2 2], Params(2, 1:3), s, 'Fontsize',10,'Parent',ax) set(get(ax,'Title'),'String','translation','FontSize',16,'FontWeight','Bold'); set(get(ax,'Xlabel'),'String','image'); set(get(ax,'Ylabel'),'String','mm'); ax=axes('Position',[0.1 0.05 0.8 0.2],'Parent',fg,'XGrid','on','YGrid','on'); plot(Params(:,4:6)*180/pi,'Parent',ax) s = ['pitch';'roll ';'yaw ']; text([2 2 2], Params(2, 4:6)*180/pi, s, 'Fontsize',10,'Parent',ax) set(get(ax,'Title'),'String','rotation','FontSize',16,'FontWeight','Bold'); set(get(ax,'Xlabel'),'String','image'); set(get(ax,'Ylabel'),'String','degrees'); % print realigment parameters spm_print end return; %_______________________________________________________________________ %_______________________________________________________________________ function save_parameters(V) fname = [spm_str_manip(prepend(V(1).fname,'realignment_params_'),'s') '.txt']; n = length(V); Q = zeros(n,6); for j=1:n, qq = spm_imatrix(V(j).mat/V(1).mat); Q(j,:) = qq(1:6); end; save(fname,'Q','-ascii'); return; %_______________________________________________________________________ %_______________________________________________________________________ function PO = prepend(PI,pre) [pth,nm,xt,vr] = fileparts(deblank(PI)); PO = fullfile(pth,[pre nm xt vr]); return; %_______________________________________________________________________ minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/spm_dbh.h0000644000175000000620000000736312574624760021671 0ustar stevestaff/* * * (c) Copyright, 1986-1995 * Biomedical Imaging Resource * Mayo Foundation * * dbh.h * * * database sub-definitions */ struct header_key /* header_key */ { /* off + size*/ int sizeof_hdr; /* 0 + 4 */ char data_type[10]; /* 4 + 10 */ char db_name[18]; /* 14 + 18 */ int extents; /* 32 + 4 */ short int session_error; /* 36 + 2 */ char regular; /* 38 + 1 */ char hkey_un0; /* 39 + 1 */ }; /* total=40 */ struct image_dimension /* image_dimension */ { /* off + size*/ short int dim[8]; /* 0 + 16 */ char vox_units[4]; /* 16 + 4 */ char cal_units[8]; /* 20 + 4 */ short int unused1; /* 24 + 2 */ short int datatype; /* 30 + 2 */ short int bitpix; /* 32 + 2 */ short int dim_un0; /* 34 + 2 */ float pixdim[8]; /* 36 + 32 */ /* pixdim[] specifies the voxel dimensions: pixdim[1] - voxel width pixdim[2] - voxel height pixdim[3] - interslice distance ..etc */ float vox_offset; /* 68 + 4 */ float roi_scale; /* 72 + 4 */ float funused1; /* 76 + 4 */ float funused2; /* 80 + 4 */ float cal_max; /* 84 + 4 */ float cal_min; /* 88 + 4 */ int compressed; /* 92 + 4 */ int verified; /* 96 + 4 */ int glmax, glmin; /* 100 + 8 */ }; /* total=108 */ struct data_history /* data_history */ { /* off + size*/ char descrip[80]; /* 0 + 80 */ char aux_file[24]; /* 80 + 24 */ char orient; /* 104 + 1 */ char originator[10]; /* 105 + 10 */ char generated[10]; /* 115 + 10 */ char scannum[10]; /* 125 + 10 */ char patient_id[10]; /* 135 + 10 */ char exp_date[10]; /* 145 + 10 */ char exp_time[10]; /* 155 + 10 */ char hist_un0[3]; /* 165 + 3 */ int views; /* 168 + 4 */ int vols_added; /* 172 + 4 */ int start_field; /* 176 + 4 */ int field_skip; /* 180 + 4 */ int omax,omin; /* 184 + 8 */ int smax,smin; /* 192 + 8 */ }; /* total=200 */ struct dsr /* dsr */ { /* off + size*/ struct header_key hk; /* 0 + 40 */ struct image_dimension dime; /* 40 + 108 */ struct data_history hist; /* 148 + 200 */ }; /* total=348 */ /* Acceptable values for hdr.dime.datatype */ #define DT_NONE 0 #define DT_UNKNOWN 0 #define DT_BINARY 1 #define DT_UNSIGNED_CHAR 2 #define DT_SIGNED_SHORT 4 #define DT_SIGNED_INT 8 #define DT_FLOAT 16 #define DT_COMPLEX 32 #define DT_DOUBLE 64 #define DT_RGB 128 #define DT_ALL 255 typedef struct { float real; float imag; } COMPLEX; minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/spm_hread.m0000644000175000000620000001052312574624760022214 0ustar stevestafffunction [DIM,VOX,SCALE,TYPE,OFFSET,ORIGIN,DESCRIP] = spm_hread(P) % reads a header % FORMAT [DIM VOX SCALE TYPE OFFSET ORIGIN DESCRIP] = spm_hread(P); % % P - filename (e.g spm or spm.img) % DIM - image size [i j k [l]] (voxels) % VOX - voxel size [x y z [t]] (mm [secs]) % SCALE - scale factor % TYPE - datatype (integer - see spm_type) % OFFSET - offset (bytes) % ORIGIN - origin [i j k] % DESCRIP - description string %___________________________________________________________________________ % % spm_hread reads variables into working memory from a SPM/ANALYZE % compatible header file. If the header does not exist global defaults % are used. The 'originator' field of the ANALYZE format has been % changed to ORIGIN in the SPM version of the header. funused1 of the % ANALYZE format is used for SCALE % % see also dbh.h (ANALYZE) spm_hwrite.m and spm_type.m % %__________________________________________________________________________ % @(#)spm_hread.m 2.7 99/10/29 % ensure correct suffix {.hdr} %--------------------------------------------------------------------------- P = deblank(P); q = length(P); if q>=4 & P(q - 3) == '.'; P = P(1:(q - 4)); end P = [P '.hdr']; % open header file %--------------------------------------------------------------------------- fid = fopen(P,'r','native'); if (fid > 0) % read (struct) header_key %--------------------------------------------------------------------------- fseek(fid,0,'bof'); otherendian = 0; sizeof_hdr = fread(fid,1,'int32'); if sizeof_hdr==1543569408, % Appears to be other-endian % Re-open other-endian fclose(fid); if spm_platform('bigend'), fid = fopen(P,'r','ieee-le'); else, fid = fopen(P,'r','ieee-be'); end; fseek(fid,0,'bof'); sizeof_hdr = fread(fid,1,'int32'); otherendian = 1; end; data_type = mysetstr(fread(fid,10,'uchar'))'; db_name = mysetstr(fread(fid,18,'uchar'))'; extents = fread(fid,1,'int32'); session_error = fread(fid,1,'int16'); regular = mysetstr(fread(fid,1,'uchar'))'; hkey_un0 = mysetstr(fread(fid,1,'uchar'))'; % read (struct) image_dimension %--------------------------------------------------------------------------- fseek(fid,40,'bof'); dim = fread(fid,8,'int16'); vox_units = mysetstr(fread(fid,4,'uchar'))'; cal_units = mysetstr(fread(fid,8,'uchar'))'; unused1 = fread(fid,1,'int16'); datatype = fread(fid,1,'int16'); bitpix = fread(fid,1,'int16'); dim_un0 = fread(fid,1,'int16'); pixdim = fread(fid,8,'float'); vox_offset = fread(fid,1,'float'); funused1 = fread(fid,1,'float'); funused2 = fread(fid,1,'float'); funused3 = fread(fid,1,'float'); cal_max = fread(fid,1,'float'); cal_min = fread(fid,1,'float'); compressed = fread(fid,1,'int32'); verified = fread(fid,1,'int32'); glmax = fread(fid,1,'int32'); glmin = fread(fid,1,'int32'); % read (struct) data_history %--------------------------------------------------------------------------- fseek(fid,148,'bof'); descrip = mysetstr(fread(fid,80,'uchar'))'; aux_file = mysetstr(fread(fid,24,'uchar'))'; orient = fread(fid,1,'uchar'); origin = fread(fid,5,'int16'); generated = mysetstr(fread(fid,10,'uchar'))'; scannum = mysetstr(fread(fid,10,'uchar'))'; patient_id = mysetstr(fread(fid,10,'uchar'))'; exp_date = mysetstr(fread(fid,10,'uchar'))'; exp_time = mysetstr(fread(fid,10,'uchar'))'; hist_un0 = mysetstr(fread(fid,3,'uchar'))'; views = fread(fid,1,'int32'); vols_added = fread(fid,1,'int32'); start_field = fread(fid,1,'int32'); field_skip = fread(fid,1,'int32'); omax = fread(fid,1,'int32'); omin = fread(fid,1,'int32'); smax = fread(fid,1,'int32'); smin = fread(fid,1,'int32'); fclose(fid); if isempty(smin) error(['There is a problem with the header file ' P '.']); end % convert to SPM global variables %--------------------------------------------------------------------------- DIM = dim(2:4)'; VOX = pixdim(2:4)'; SCALE = funused1; SCALE = ~SCALE + SCALE; TYPE = datatype; if otherendian == 1 & datatype ~= 2, TYPE = TYPE*256; end; OFFSET = vox_offset; ORIGIN = origin(1:3)'; DESCRIP = descrip(1:max(find(descrip))); else global DIM VOX SCALE TYPE OFFSET ORIGIN DESCRIP = ['defaults']; end return; %_______________________________________________________________________ function out = mysetstr(in) tmp = find(in == 0); tmp = min([min(tmp) length(in)]); out = setstr(in(1:tmp)); return; minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/show_hdr.c0000644000175000000620000001217312574624760022060 0ustar stevestaff/* http://www.mayo.edu/bir/analyze/AnalyzeFileInfo.html */ /* if this blows up remember to check byte-alignment for originator */ #include #include #include "analyze_db.h" void ShowHdr(char *fileName, struct dsr *hdr); void swap_hdr(struct dsr *pntr); void swap_long(unsigned char *); void swap_short(unsigned char *); main(int argc, char **argv){ struct dsr hdr; FILE *fp; if((fp=fopen(argv[1],"r"))==NULL) { fprintf(stderr,"Can't open:<%s>\n", argv[1]); exit(0); } fread(&hdr,1,sizeof(struct dsr),fp); if(hdr.dime.dim[0] < 0 || hdr.dime.dim[0] > 15) swap_hdr(&hdr); ShowHdr(argv[1], &hdr); } void ShowHdr(char *fileName, struct dsr *hdr){ int i; char string[128]; printf("Analyze Header Dump of: <%s> \n", fileName); /* Header Key */ printf("sizeof_hdr: <%d> \n", hdr->hk.sizeof_hdr); printf("data_type: <%s> \n", hdr->hk.data_type); printf("db_name: <%s> \n", hdr->hk.db_name); printf("extents: <%d> \n", hdr->hk.extents); printf("session_error: <%d> \n", hdr->hk.session_error); printf("regular: <%c> \n", hdr->hk.regular); printf("hkey_un0: <%c> \n", hdr->hk.hkey_un0); /* Image Dimension */ for(i=0;i<8;i++) printf("dim[%d]: <%d> \n", i, hdr->dime.dim[i]); strncpy(string,hdr->dime.vox_units,4); printf("vox_units: <%s> \n", string); strncpy(string,hdr->dime.cal_units,8); printf("cal_units: <%s> \n", string); printf("unused1: <%d> \n", hdr->dime.unused1); printf("datatype: <%d> \n", hdr->dime.datatype); printf("bitpix: <%d> \n", hdr->dime.bitpix); for(i=0;i<8;i++) printf("pixdim[%d]: <%6.4f> \n",i, hdr->dime.pixdim[i]); printf("vox_offset: <%6.4> \n", hdr->dime.vox_offset); printf("funused1: <%6.4f> \n", hdr->dime.funused1); printf("funused2: <%6.4f> \n", hdr->dime.funused2); printf("cal_max: <%6.4f> \n", hdr->dime.cal_max); printf("cal_min: <%6.4f> \n", hdr->dime.cal_min); printf("compressed: <%d> \n", hdr->dime.compressed); printf("verified: <%d> \n", hdr->dime.verified); printf("glmax: <%d> \n", hdr->dime.glmax); printf("glmin: <%d> \n", hdr->dime.glmin); /* Data History */ strncpy(string,hdr->hist.descrip,80); printf("descrip: <%s> \n", string); strncpy(string,hdr->hist.aux_file,24); printf("aux_file: <%s> \n", string); printf("orient: <%d> \n", hdr->hist.orient); for(i=0;i<5;i++){ swap_short(&hdr->hist.originator[i]); printf("originator[%d]: <%u> \n", i, hdr->hist.originator[i]); } strncpy(string,hdr->hist.generated,10); printf("generated: <%s> \n", string); strncpy(string,hdr->hist.scannum,10); printf("scannum: <%s> \n", string); strncpy(string,hdr->hist.patient_id,10); printf("patient_id: <%s> \n", string); strncpy(string,hdr->hist.exp_date,10); printf("exp_date: <%s> \n", string); strncpy(string,hdr->hist.exp_time,10); printf("exp_time: <%s> \n", string); strncpy(string,hdr->hist.hist_un0,3); printf("hist_un0: <%s> \n", string); printf("views: <%d> \n", hdr->hist.views); printf("vols_added: <%d> \n", hdr->hist.vols_added); printf("start_field:<%d> \n", hdr->hist.start_field); printf("field_skip: <%d> \n", hdr->hist.field_skip); printf("omax: <%d> \n", hdr->hist.omax); printf("omin: <%d> \n", hdr->hist.omin); printf("smin: <%d> \n", hdr->hist.smax); printf("smin: <%d> \n", hdr->hist.smin); } void swap_hdr(struct dsr *pntr){ swap_long(&pntr->hk.sizeof_hdr) ; swap_long(&pntr->hk.extents) ; swap_short(&pntr->hk.session_error) ; swap_short(&pntr->dime.dim[0]) ; swap_short(&pntr->dime.dim[1]) ; swap_short(&pntr->dime.dim[2]) ; swap_short(&pntr->dime.dim[3]) ; swap_short(&pntr->dime.dim[4]) ; swap_short(&pntr->dime.dim[5]) ; swap_short(&pntr->dime.dim[6]) ; swap_short(&pntr->dime.dim[7]) ; swap_short(&pntr->dime.unused1) ; swap_short(&pntr->dime.datatype) ; swap_short(&pntr->dime.bitpix) ; swap_long(&pntr->dime.pixdim[0]) ; swap_long(&pntr->dime.pixdim[1]) ; swap_long(&pntr->dime.pixdim[2]) ; swap_long(&pntr->dime.pixdim[3]) ; swap_long(&pntr->dime.pixdim[4]) ; swap_long(&pntr->dime.pixdim[5]) ; swap_long(&pntr->dime.pixdim[6]) ; swap_long(&pntr->dime.pixdim[7]) ; swap_long(&pntr->dime.vox_offset) ; swap_long(&pntr->dime.funused1) ; swap_long(&pntr->dime.funused2) ; swap_long(&pntr->dime.cal_max) ; swap_long(&pntr->dime.cal_min) ; swap_long(&pntr->dime.compressed) ; swap_long(&pntr->dime.verified) ; swap_short(&pntr->dime.dim_un0) ; swap_long(&pntr->dime.glmax) ; swap_long(&pntr->dime.glmin) ; } void swap_long(unsigned char *pntr){ unsigned char b0, b1, b2, b3; b0 = *pntr; b1 = *(pntr+1); b2 = *(pntr+2); b3 = *(pntr+3); *pntr = b3; *(pntr+1) = b2; *(pntr+2) = b1; *(pntr+3) = b0; } void swap_short(unsigned char *pntr){ unsigned char b0, b1; b0 = *pntr; b1 = *(pntr+1); *pntr = b1; *(pntr+1) = b0; } minc-tools-2.3.00+dfsg/conversion/ana2mnc/doc/dbh.h0000644000175000000620000002117612574624760021010 0ustar stevestaff/* dbh.h - Analyze 7.5 header file */ /* */ /* Compiled by Andrew Janke (a.janke@gmail.com) */ /* from http://www.mayo.edu/bir/analyze/AnalyzeFileInfo.html */ /* http://homepage2.nifty.com/peco/gpetview/gpetview.html */ /* Chris Rorden - chris.rorden@nottingham.ac.uk */ /* Matthew Brett - matthew.brett@mrc-cbu.cam.ac.uk */ #define DT_NONE 0 /* No data type */ #define DT_UNKNOWN 0 /* Unknown data type */ #define DT_BINARY 1 /* Binary ( 1 bit per voxel) */ #define DT_UNSIGNED_CHAR 2 /* Unsigned character ( 8 bits per voxel) */ #define DT_SIGNED_SHORT 4 /* Signed short (16 bits per voxel) */ #define DT_SIGNED_INT 8 /* Signed integer (32 bits per voxel) */ #define DT_FLOAT 16 /* Floating point (32 bits per voxel) */ #define DT_COMPLEX 32 /* Complex (64 bits per voxel; 2 floating points) */ #define DT_DOUBLE 64 /* Double precision (64 bits per voxel) */ #define DT_RGB 128 /* Uchar x 3 (24 bits per voxel) */ #define DT_ALL 255 /* */ struct header_key{ /* off + size */ int sizeof_hdr; /* 0 + 4 - the byte size of the header file */ char data_type[10]; /* 4 + 10 - the data type of the file */ char db_name[18]; /* 14 + 18 - */ int extents; /* 32 + 4 - should be 16384 */ short int session_error; /* 36 + 2 - */ char regular; /* 38 + 1 - 'r' indicating all images/volumes are the same size */ char hkey_un0; /* 39 + 1 - */ }; /* total=40 bytes */ struct image_dimension{ /* off + size */ short int dim[8]; /* 0 + 16 - array of the image dimensions */ /* dim[0] # of dimensions in database; usually 4 */ /* dim[1] X dim - pixels in an image row */ /* dim[2] Y dim - pixel rows in slice */ /* dim[3] Z dim - slices in a volume */ /* dim[4] Time dim - volumes in database */ char vox_units[4]; /* 16 + 4 - specifies the spatial units of measure for a voxel */ char cal_units[8]; /* 20 + 8 - specifies the name of the calibration unit */ short int unused1; /* 28 + 2 */ short int datatype; /* 30 + 2 - datatype for this image set */ short int bitpix; /* 32 + 2 - # of bits per pixel 1, 8, 16, 32, or 64. */ short int dim_un0; /* 34 + 2 - */ float pixdim[8]; /* 36 + 32 - pixdim[] specifies the voxel dimensions: */ /* pixdim[1] - voxel width */ /* pixdim[2] - voxel height */ /* pixdim[3] - interslice distance */ /* ..etc */ float vox_offset; /* 68 + 4 - byte offset in the .img file at which voxels start. */ /* This value can be negative to specify that the */ /* absolute value is applied for every image */ float scale_factor; /* 72 + 4 = funused1; scale factor used by SPM; non standard */ float funused1; /* 76 + 4 */ float funused2; /* 80 + 4 */ float cal_max, cal_min; /* 84 + 8 - calibrated max and min: */ /* www.mailbase.ac.uk/lists/spm/2000-09/0099.html */ float compressed; /* 92 + 4 */ float verified; /* 96 + 4 */ int glmax, glmin; /* 100 + 8 - global max and min pixel values (entire database) */ }; /* total=108 bytes */ struct data_history{ /* off + size */ char descrip[80]; /* 0 + 80 */ char aux_file[24]; /* 80 + 24 */ char orient; /* 104 + 1 - slice orientation for this database | */ /* 0 transverse unflipped | */ /* 1 coronal unflipped | disregarded */ /* 2 sagittal unflipped | by SPM */ /* 3 transverse flipped | */ /* 4 coronal flipped | */ /* 5 sagittal flipped | */ short int originator[5]; /* 105 + 10 - origin | */ /* originator[0] x-origin | non standard */ /* originator[1] y-origin | SPM use only */ /* originator[2] z-origin | */ 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 */ }; /* total=200 bytes */ struct dsr{ struct header_key hk; /* 0 + 40 */ struct image_dimension dime; /* 40 + 108 */ struct data_history hist; /* 148 + 200 */ }; /* total= 348 bytes */ typedef struct{ float real; float imag; } COMPLEX; minc-tools-2.3.00+dfsg/conversion/ana2mnc/make_links0000755000175000000620000000021112574624760021363 0ustar stevestaff#! /bin/sh for i in mnc2ana ana_show spm2mnc spm2xfm spm_show; do echo "Making link from $i to ana2mnc"; ln -s ana2mnc $i; done minc-tools-2.3.00+dfsg/conversion/ana2mnc/ana2mnc0000755000175000000620000010020212574624760020566 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - a.janke@gmail.com # Center for Magnetic Resonance # The University of Queensland # http://www.cmr.uq.edu.au/~rotor # # Copyright Andrew Janke, The University of Queensland. # 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 the University of Queensland make no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Inspired by Peter Neelins' (neelin@bic.mni.mcgill.ca) ana2mnc for ANALYZE 4.x. # Since then it has grown into a veritable swiss-army chainsaw of # SPM, ANALYZE 7.5 and MINC conversion. # # Fri Oct 8 12:01:49 EST 2004 - Added -noflip and skipping of .mat file if not found # with thanks to Mark Daglish - mark.daglish@bris.ac.uk # Thu Dec 2 13:00:38 EST 2004 - Added -version argument (Thanks again to mark) # and more std do_cmd() function # Thu Dec 9 09:36:30 EST 2004 - added -swap_data use strict; use warnings "all"; use Getopt::Tabular; use File::Basename; my($Help, $Usage, $me, @files, $history, %opt, @opt_table); my($anabase, $anahdr, $anaimg, $mncfile, $matfile, $xfmfile); my $version="1.3d"; $me = basename($0); my $versioninfo = "print '$me version $version\n\n'; exit 0;"; %opt = ( 'verbose' => 0, 'clobber' => 0, 'fake' => 0, 'orientation' => undef, 'flip' => 1, 'byte_swap_data' => 0, ); $Help = <', ''); } elsif($me eq "spm2mnc"){ @files = ('', ''); } elsif($me eq "mnc2ana"){ @files = ('', ''); } elsif($me eq "spm2xfm"){ @files = ('', ''); } elsif($me eq "ana_show"){ @files = (''); } elsif($me eq "spm_show"){ @files = (''); } $Usage = "Usage: $me [options] @files\n" . " $me -help to list options\n" . " $me -version to list version information\n\n"; # Check arguments &Getopt::Tabular::SetHelp($Help, $Usage); &GetOptions(\@opt_table, \@ARGV) || exit 1; die $Usage if ($#ARGV != $#files); # Get the history string chomp($history = `date`); $history .= '>>>> ' . join(' ', $me, @ARGV) . "\n"; # global byte-swapping flag my $bs = 0; # then do whatever we are supposed to be doing if($me eq "ana2mnc"){ ($anabase, $mncfile) = @ARGV[0..1]; $anabase =~ s/\.(hdr|img)$//; if(!-e "$anabase.hdr"){ die "$me: Couldn't find $anabase.hdr\n\n"; } if(!-e "$anabase.img" && !-e "$anabase.img.gz"){ die "$me: Couldn't find $anabase.img or $anabase.img.gz\n\n"; } if(-e $mncfile && !$opt{clobber}){ die "$me: $mncfile exists! use -clobber to overwrite\n\n"; } # Read in the header file my($ana_hdr) = read_analyze_header("$anabase.hdr"); print STDOUT dump_analyze_header($ana_hdr) if $opt{verbose}; # convert to the general header format my($gen_hdr) = analyze_to_general_header($ana_hdr, \%opt); print STDOUT dump_general_header($gen_hdr) if $opt{verbose}; # check for a compressed img file if(!-e "$anabase.img" && -e "$anabase.img.gz"){ &do_cmd('gunzip', "$anabase.img.gz"); } write_minc($gen_hdr, $mncfile, "$anabase.img"); if(!$opt{flip} && $opt{verbose}){ warn "$me: Remember this image has NOT been flipped\n\n"; } } elsif($me eq "spm2mnc"){ ($anabase, $mncfile) = @ARGV[0..1]; $anabase =~ s/\.(hdr|img|mat)$//; if(!-e "$anabase.hdr"){ die "$me: Couldn't find $anabase.hdr\n\n"; } if(!-e "$anabase.img" && !-e "$anabase.img.gz"){ die "$me: Couldn't find $anabase.img or $anabase.img.gz\n\n"; } if(!-e "$anabase.mat"){ warn "$me: Couldn't find $anabase.mat, continuing anyway...\n\n"; } if(-e $mncfile && !$opt{clobber}){ die "$me: $mncfile exists! use -clobber to overwrite\n\n"; } # Read in the header file my($ana_hdr) = read_analyze_header("$anabase.hdr"); print STDOUT dump_analyze_header($ana_hdr) if $opt{verbose}; # convert to the general header format my($gen_hdr) = spm_to_general_header($ana_hdr, \%opt); print STDOUT dump_general_header($gen_hdr) if $opt{verbose}; # check for a compressed img file if(!-e "$anabase.img" && -e "$anabase.img.gz"){ &do_cmd('gunzip', "$anabase.img.gz"); } write_minc($gen_hdr, $mncfile, "$anabase.img"); if(!$opt{flip} && $opt{verbose}){ warn "$me: Remember this image has NOT been flipped\n\n"; } } elsif($me eq "mnc2ana"){ ($mncfile, $anabase) = @ARGV[0..1]; $anabase =~ s/\.(hdr|img)$//; if(!-e $mncfile){ die "$me: Couldn't find $mncfile\n\n"; } if((-e "$anabase.hdr" || -e "$anabase.img") && !$opt{clobber}){ die "$me: $anabase.hdr or $anabase.img exist! -clobber to overwrite\n\n"; } my($h) = read_minc($mncfile); print STDOUT dump_analyze_header($h) if $opt{verbose}; write_analyze_header($h, "$anabase.hdr"); # Set up mincextract command my($args) = "mincextract -normalize -positive_direction -filetype "; $args .= ($h->{bitpix} == 8) ? '-unsigned ' : '-signed '; $args .= "$mncfile > $anabase.img\n"; &do_cmd($args); } elsif($me eq "spm2xfm"){ ($matfile, $xfmfile) = @ARGV[0..1]; chomp(my $date = `date`); my ($xfm_text, @dump, $c); if(!-e $matfile){ die "$me: Couldn't find file: $matfile\n\n"; } if(-e $xfmfile && !$opt{clobber}){ die "$me: $xfmfile exists! use -clobber to overwrite\n\n"; } # read in .mat file, convert to sensical format, remove first and last line @dump = `mat1dump $matfile`; for($c=1; $c<4; $c++){ $dump[$c] =~ s/\t/\ /g; if($c == 3){ $dump[$c] =~ s/\n/\;\n/g; } } open(SPM_XFM, ">$xfmfile"); print SPM_XFM "MNI Transform File\n". "%$date>>> Created by $me from $matfile\n\n". "Transform_Type = Linear;\n". "Linear_Transform =\n". "@dump[1..3]"; close(SPM_XFM); } elsif($me eq "ana_show"){ $anahdr = $ARGV[0]; if(!-e "$anahdr"){ die "$me: Couldn't find file: $anahdr\n\n"; } print STDOUT dump_analyze_header(read_analyze_header($anahdr)); } elsif($me eq "spm_show"){ $matfile = $ARGV[0]; if(!-e $matfile){ die "$me: Couldn't find $matfile\n\n"; } print STDOUT `mat1dump $matfile`; } # Subroutines ####################################################### # Unpack a value from a string (passed by reference) # Legendary bit of code of peter neelins nicked from mri_to_minc sub destruct{ my($stringref, $offset, $type, $byte_swap) = @_; my(%Unpack_codes, $code, $number, $size, $tempstring, $iloop, $max); %Unpack_codes = ('a', 1, 'A', 1, 's', 2, 'S', 2, 'i', 4, 'I', 4, 'f', 4, 'd', 8, ); # Check for byte swapping if($byte_swap){ if($type !~ /^\s*([a-zA-Z])(\d+)?\s*$/){ die "$me: unrecognized data type \"$type\" on little-endian machine.\n\n"; } $code = $1; $number = (defined($2) ? $2 : 1); if(!defined($Unpack_codes{$code})){ die "$me: unrecognized unpack code \"$code\" on little-endian machine.\n\n"; } $size = $Unpack_codes{$code}; $tempstring = substr($$stringref, $offset, $number * $size); if($size > 1){ $max = $number * $size; for($iloop=0; $iloop < $max; $iloop+=$size){ substr($tempstring, $iloop, $size) = reverse(substr($tempstring, $iloop, $size)); } } return unpack("$type", $tempstring); } # No byte swapping required else{ return unpack("x$offset $type", $$stringref); } } sub analyze_to_general_header{ my($ana_hdr, $settings) = @_; my($gen_hdr) = {}; my(%ana_dtypes) = ( 2 => ['-byte', '-unsigned'], 4 => ['-short', '-signed'], 8 => ['-long', '-signed'], 16 => ['-float'], 64 => ['-double'], 128 => ['-byte', '-unsigned', '-vector', '3'] ); my(%Orientation_to_world_order) = ( '-transverse' => [0, 1, 2], '-sagittal' => [2, 0, 1], '-coronal' => [0, 2, 1], ); # begin the conversions.. if(!defined($ana_dtypes{$ana_hdr->{datatype}})) { die "$me: unknown data type:$ana_hdr->{data_type}\n\n"; } $gen_hdr->{datatype} = $ana_dtypes{$ana_hdr->{datatype}}; $gen_hdr->{voxel_min} = $ana_hdr->{glmin}; $gen_hdr->{voxel_max} = $ana_hdr->{glmax}; $gen_hdr->{real_min} = $ana_hdr->{cal_min}; $gen_hdr->{real_max} = $ana_hdr->{cal_max}; # get the dimension sizes $gen_hdr->{xsize} = @{$ana_hdr->{dim}}[1]; $gen_hdr->{ysize} = @{$ana_hdr->{dim}}[2]; $gen_hdr->{zsize} = @{$ana_hdr->{dim}}[3]; $gen_hdr->{ntime} = @{$ana_hdr->{dim}}[4]; # get orientation if(($ana_hdr->{orient} == 0) || ($ana_hdr->{orient} == 3)) { # Transverse $gen_hdr->{orientation} = '-transverse'; } elsif(($ana_hdr->{orient} == 2) || ($ana_hdr->{orient} == 5)) { # Sagittal $gen_hdr->{orientation} = '-sagittal'; } elsif(($ana_hdr->{orient} == 1) || ($ana_hdr->{orient} == 4)) { # Coronal $gen_hdr->{orientation} = '-coronal'; } else{ # Unknown warn "$me: unknown data orientation: assuming transverse\n"; $gen_hdr->{orientation} = '-transverse'; } if(defined($settings->{orientation})){ warn "$me: overriding file orientation with $settings->{orientation}\n"; $gen_hdr->{orientation} = $settings->{orientation}; } # get step info my(@steps) = @{$ana_hdr->{pixdim}}[1..3]; my(@world_order) = @{$Orientation_to_world_order{$gen_hdr->{orientation}}}; ($gen_hdr->{xstep}, $gen_hdr->{ystep}, $gen_hdr->{zstep}) = @steps[@world_order]; # flip the xstep if required $gen_hdr->{xstep} *= -1 if $opt{flip}; return $gen_hdr; } sub spm_to_general_header{ my($ana_hdr, $settings) = @_; my($gen_hdr) = {}; my(%ana_dtypes) = ( 2 => ['-byte', '-unsigned'], 4 => ['-short', '-signed'], 8 => ['-long', '-signed'], 16 => ['-float'], 64 => ['-double'], 128 => ['-byte', '-unsigned', '-vector', '3'] ); my(%Orientation_to_world_order) = ( '-transverse' => [0, 1, 2], '-sagittal' => [2, 0, 1], '-coronal' => [0, 2, 1], ); # begin the conversions.. if(!defined($ana_dtypes{$ana_hdr->{datatype}})) { die "$me: unknown data type:$ana_hdr->{data_type}\n\n"; } $gen_hdr->{datatype} = $ana_dtypes{$ana_hdr->{datatype}}; $gen_hdr->{voxel_min} = $ana_hdr->{glmin}; $gen_hdr->{voxel_max} = $ana_hdr->{glmax}; $gen_hdr->{real_min} = $gen_hdr->{voxel_min}*$ana_hdr->{scale_factor}; $gen_hdr->{real_max} = $gen_hdr->{voxel_max}*$ana_hdr->{scale_factor}; # get the dimension sizes $gen_hdr->{xsize} = @{$ana_hdr->{dim}}[1]; $gen_hdr->{ysize} = @{$ana_hdr->{dim}}[2]; $gen_hdr->{zsize} = @{$ana_hdr->{dim}}[3]; $gen_hdr->{ntime} = @{$ana_hdr->{dim}}[4]; # get orientation if(($ana_hdr->{orient} == 0) || ($ana_hdr->{orient} == 3)) { # Transverse $gen_hdr->{orientation} = '-transverse'; } elsif(($ana_hdr->{orient} == 2) || ($ana_hdr->{orient} == 5)) { # Sagittal $gen_hdr->{orientation} = '-sagittal'; } elsif(($ana_hdr->{orient} == 1) || ($ana_hdr->{orient} == 4)) { # Coronal $gen_hdr->{orientation} = '-coronal'; } else{ # Unknown warn "$me: unknown data orientation: assuming transverse\n"; $gen_hdr->{orientation} = '-transverse'; } if(defined($settings->{orientation})){ warn "$me: overriding file orientation with $settings->{orientation}\n"; $gen_hdr->{orientation} = $settings->{orientation}; } # get step info my(@steps) = @{$ana_hdr->{pixdim}}[1..3]; my(@world_order) = @{$Orientation_to_world_order{$gen_hdr->{orientation}}}; ($gen_hdr->{xstep}, $gen_hdr->{ystep}, $gen_hdr->{zstep}) = @steps[@world_order]; # flip the xstep if required $gen_hdr->{xstep} *= -1 if $opt{flip}; # get origin information my(@starts) = @{$ana_hdr->{originator}}[0..2]; @world_order = @{$Orientation_to_world_order{$gen_hdr->{orientation}}}; ($gen_hdr->{xstart}, $gen_hdr->{ystart}, $gen_hdr->{zstart}) = @starts[@world_order]; $gen_hdr->{xstart} *= -$gen_hdr->{xstep}; $gen_hdr->{ystart} *= -$gen_hdr->{ystep}; $gen_hdr->{zstart} *= -$gen_hdr->{zstep}; return $gen_hdr; } # Read an analyze 7.5 header file and return a hash sub read_analyze_header{ open(HDR, $_[0]) or die "$me: error opening file: $_[0]\n\n"; read(HDR, my($hdr_s), 40); read(HDR, my($dim_s), 108); read(HDR, my($his_s), 200); close(HDR); my($extents, $c, $h) = 0; # check if we need to byteswap $h->{sizeof_hdr} = destruct(\$hdr_s, 0,'i', 0); if($h->{sizeof_hdr} != 348){ warn "$me: sizeof header: $h->{sizeof_hdr}\n"; warn "$me: Hrm, attempting byte-swapping on $_[0]\n"; $bs = 1; $h->{sizeof_hdr} = destruct(\$hdr_s, 0,'i', $bs); } if($h->{sizeof_hdr} != 348){ warn "$me: sizeof header: $h->{sizeof_hdr}\n"; die "$me: $_[0] doesn't appear to be a ANALYZE file\n". " Who would really know though......\n\n"; } print STDERR "sizeof_header: $h->{sizeof_hdr} -- Byte Swap: $bs\n" if $opt{verbose}; # $h->{sizeof_hdr} = destruct(\$hdr_s, 0,'i', $bs); # 4 - the byte size of the header file $h->{data_type} = destruct(\$hdr_s, 4,'a10', $bs); # 10 - $h->{db_name} = destruct(\$hdr_s, 14,'a18', $bs); # 18 - $h->{extents} = destruct(\$hdr_s, 32,'i', $bs); # 4 - should be 16384 $h->{session_error} = destruct(\$hdr_s, 36,'s', $bs); # 2 - $h->{regular} = destruct(\$hdr_s, 38,'a', $bs); # 1 - 'r' indicates volumes are the same size $h->{hkey_un0} = destruct(\$hdr_s, 39,'c', 0); # 1 - # 40 bytes $h->{dim} =[destruct(\$dim_s, 0,'s8', $bs)]; # 16 - array of the image dimensions # dim[0] # of dimensions; usually 4 # dim[1] X dim - pixels in an image row # dim[2] Y dim - pixel rows in slice # dim[3] Z dim - slices in a volume # dim[4] Time dim - volumes in database $h->{vox_units} = destruct(\$dim_s, 16,'a4', $bs); # 4 - spatial units of measure for a voxel $h->{cal_units} = destruct(\$dim_s, 20,'a8', $bs); # 8 - name of the calibration unit $h->{unused1} = destruct(\$dim_s, 28,'s', $bs); # 2 $h->{datatype} = destruct(\$dim_s, 30,'s', $bs); # 2 - datatype for this image set $h->{bitpix} = destruct(\$dim_s, 32,'s', $bs); # 2 - # of bits per voxel 1, 8, 16, 32, or 64. $h->{dim_un0} = destruct(\$dim_s, 34,'s', $bs); # 2 - $h->{pixdim} =[destruct(\$dim_s, 36,'f8', $bs)]; # 32 - pixdim[] specifies the voxel dimensions: # pixdim[1] - voxel width # pixdim[2] - voxel height # pixdim[3] - interslice distance # ..etc $h->{vox_offset} = destruct(\$dim_s, 68,'f', $bs); # 4 - byte offset in .img at which voxels start. # This value can be negative to specify # that the absolute value is applied for # every image $h->{scale_factor} = destruct(\$dim_s, 72,'f', $bs); # 4 = funused1; scale factor used by SPM $h->{funused1} = destruct(\$dim_s, 76,'f', $bs); # 4 $h->{funused2} = destruct(\$dim_s, 80,'f', $bs); # 4 $h->{cal_max} = destruct(\$dim_s, 84,'f', $bs); # 4 - calibrated max & min: $h->{cal_min} = destruct(\$dim_s, 88,'f', $bs); # 4 www.mailbase.ac.uk/lists/spm/2000-09/0099.html $h->{compressed} = destruct(\$dim_s, 92,'f', $bs); # 4 $h->{verified} = destruct(\$dim_s, 96,'f', $bs); # 4 $h->{glmax} = destruct(\$dim_s,100,'i', $bs); # 4 - global max | pixel values $h->{glmin} = destruct(\$dim_s,104,'i', $bs); # 4 - global min | (entire database) #108 bytes $h->{descrip} = destruct(\$his_s, 0,'a80', $bs); # 80 $h->{aux_file} = destruct(\$his_s, 80,'a24', $bs); # 24 $h->{orient} = destruct(\$his_s,104,'c', 0); # 1 - slice orientation | disregarded # 0 - transverse unflipped | by SPM # 1 - coronal unflipped | instead a # 2 - sagittal unflipped | .mat file is # 3 - transverse flipped | written out # 4 - coronal flipped | with this # 5 - sagittal flipped | information $h->{originator} =[destruct(\$his_s,105,'s5', $bs)]; # 10 - origin # originator[0] x-origin | non standard # originator[1] y-origin | SPM use only # originator[2] z-origin | $h->{generated} = destruct(\$his_s,115,'a10', $bs); # 10 $h->{scannum} = destruct(\$his_s,125,'a10', $bs); # 10 $h->{patient_id} = destruct(\$his_s,135,'a10', $bs); # 10 $h->{exp_date} = destruct(\$his_s,145,'a10', $bs); # 10 $h->{exp_time} = destruct(\$his_s,155,'a10', $bs); # 10 $h->{hist_un0} = destruct(\$his_s,165,'a3', $bs); # 3 $h->{views} = destruct(\$his_s,168,'i', $bs); # 4 $h->{vols_added} = destruct(\$his_s,172,'i', $bs); # 4 $h->{start_field} = destruct(\$his_s,176,'i', $bs); # 4 $h->{field_skip} = destruct(\$his_s,180,'i', $bs); # 4 $h->{omax} = destruct(\$his_s,184,'i', $bs); # 4 $h->{omin} = destruct(\$his_s,188,'i', $bs); # 4 $h->{smax} = destruct(\$his_s,192,'i', $bs); # 4 $h->{smin} = destruct(\$his_s,196,'i', $bs); # 4 #200 bytes return $h; } # return a ASCII dump of a general header sub dump_general_header{ my($h) = shift; my($tmp); $tmp = "General Header\n"; foreach (sort(keys(%{$h}))){ if($h->{$_} =~ /ARRAY/){ $tmp .= " $_\t<". join(' ' , @{$h->{$_}}) . ">\n"; } else{ $tmp .= " $_\t<$h->{$_}>\n"; } } $tmp .= "\n"; return $tmp; } # return a ASCII dump of an ANALYZE 7.5 header sub dump_analyze_header{ my($h) = shift; return "HDR:\n". " sizeof_hdr <$h->{sizeof_hdr}>\n". " data_type <$h->{data_type}>\n". " db_name <$h->{db_name}>\n". " extents <$h->{extents}>\n". " session_error <$h->{session_error}>\n". " regular <$h->{regular}>\n". " hkey_un0 <$h->{hkey_un0}>\n". "DIMENSION:\n". " dim: <" . join(' ', @{$h->{dim}}) . ">\n". " vox_units <$h->{vox_units}>\n". " cal_units <$h->{cal_units}>\n". " unused1 <$h->{unused1}>\n". " datatype <$h->{datatype}>\n". " bitpix <$h->{bitpix}>\n". " dim_un0 <$h->{dim_un0}>\n". " pixdim <" . join(' ', @{$h->{pixdim}}) . ">\n". " vox_offset <$h->{vox_offset}>\n". " scale_factor <$h->{scale_factor}>\n". " funused1 <$h->{funused1}>\n". " funused2 <$h->{funused2}>\n". " cal_max <$h->{cal_max}>\n". " cal_min <$h->{cal_min}>\n". " compressed <$h->{compressed}>\n". " verified <$h->{verified}>\n". " glmax <$h->{glmax}>\n". " glmin <$h->{glmin}>\n". "HISTORY:\n". " descrip <$h->{descrip}>\n". " aux_file <$h->{aux_file}>\n". " orient <$h->{orient}>\n". " originator <" . join(' ', @{$h->{originator}}) . ">\n". " generated <$h->{generated}>\n". " scannum <$h->{scannum}>\n". " patient_id <$h->{patient_id}>\n". " exp_date <$h->{exp_date}>\n". " exp_time <$h->{exp_time}>\n". " hist_un0 <$h->{hist_un0}>\n". " views <$h->{views}>\n". " vols_added <$h->{vols_added}>\n". " start_field <$h->{start_field}>\n". " field_skip <$h->{field_skip}>\n". " omax <$h->{omax}>\n". " omin <$h->{omin}>\n". " smax <$h->{smax}>\n". " smin <$h->{smin}>\n". "\n"; } # Write an ANALYZE 7.5 header to a file sub write_analyze_header{ my($h, $hdrfile) = @_; my($hdr_s, $dim_s, $his_s); $hdr_s = pack("i", $h->{sizeof_hdr} ); $hdr_s .= pack("a10", $h->{data_type} ); $hdr_s .= pack("a18", $h->{db_name} ); $hdr_s .= pack("i", $h->{extents} ); $hdr_s .= pack("s", $h->{session_error} ); $hdr_s .= pack("a", $h->{regular} ); $hdr_s .= pack("c", $h->{hkey_un0} ); $dim_s = pack("s8", @{$h->{dim}}[0..7] ); $dim_s .= pack("a4", $h->{vox_units} ); $dim_s .= pack("a8", $h->{cal_units} ); $dim_s .= pack("s", $h->{unused1} ); $dim_s .= pack("s", $h->{datatype} ); $dim_s .= pack("s", $h->{bitpix} ); $dim_s .= pack("s", $h->{dim_un0} ); $dim_s .= pack("f8", @{$h->{pixdim}}[0..7] ); $dim_s .= pack("f", $h->{vox_offset} ); $dim_s .= pack("f", $h->{scale_factor} ); $dim_s .= pack("f", $h->{funused1} ); $dim_s .= pack("f", $h->{funused2} ); $dim_s .= pack("f", $h->{cal_max} ); $dim_s .= pack("f", $h->{cal_min} ); $dim_s .= pack("f", $h->{compressed} ); $dim_s .= pack("f", $h->{verified} ); $dim_s .= pack("i", $h->{glmax} ); $dim_s .= pack("i", $h->{glmin} ); $his_s = pack("a80", $h->{descrip} ); $his_s .= pack("a24", $h->{aux_file} ); $his_s .= pack("c", $h->{orient} ); $his_s .= pack("s5", @{$h->{originator}}[0..4] ); $his_s .= pack("a10", $h->{generated} ); $his_s .= pack("a10", $h->{scannum} ); $his_s .= pack("a10", $h->{patient_id} ); $his_s .= pack("a10", $h->{exp_date} ); $his_s .= pack("a10", $h->{exp_time} ); $his_s .= pack("a3", $h->{hist_un0} ); $his_s .= pack("i", $h->{views} ); $his_s .= pack("i", $h->{vols_added} ); $his_s .= pack("i", $h->{start_field} ); $his_s .= pack("i", $h->{field_skip} ); $his_s .= pack("i", $h->{omax} ); $his_s .= pack("i", $h->{omin} ); $his_s .= pack("i", $h->{smax} ); $his_s .= pack("i", $h->{smin} ); open(HDR, ">$hdrfile") or die "$me: error opening file: $hdrfile\n\n"; syswrite(HDR, $hdr_s, 40); syswrite(HDR, $dim_s, 108); syswrite(HDR, $his_s, 200); close(HDR); } # Create and return an ANALYZE 7.5 as a hash sub create_analyze_header{ my($h) = {}; $h->{sizeof_hdr} = 348; $h->{data_type} = 0; $h->{db_name} = ''; $h->{extents} = 16384; $h->{session_error} = 0; $h->{regular} = 'r'; $h->{hkey_un0} = 0; $h->{dim} = [1,1,1,1,1,1,1,1]; $h->{vox_units} = ''; $h->{cal_units} = ''; $h->{unused1} = 0; $h->{datatype} = 0; $h->{bitpix} = 0; $h->{dim_un0} = 0; $h->{pixdim} = [0,0,0,0,0,0,0,0]; $h->{vox_offset} = 0; $h->{scale_factor} = 1.0; $h->{funused1} = 0; $h->{funused2} = 0; $h->{cal_max} = 0; $h->{cal_min} = 0; $h->{compressed} = 0; $h->{verified} = 0; $h->{glmax} = 0; $h->{glmin} = 0; $h->{descrip} = ''; $h->{aux_file} = ''; $h->{orient} = 0; $h->{originator} = [0,0,0,0,0]; $h->{generated} = ''; $h->{scannum} = ''; $h->{patient_id} = ''; $h->{exp_date} = ''; $h->{exp_time} = ''; $h->{hist_un0} = ''; $h->{views} = 0; $h->{vols_added} = 0; $h->{start_field} = 0; $h->{field_skip} = 0; $h->{omax} = 0; $h->{omin} = 0; $h->{smax} = 0; $h->{smin} = 0; return $h; } # Write out a MINC file based upon a given ANALYZE 7.5 header and a raw data file sub write_minc{ my($gen_hdr, $mncfile, $rawfile) = @_; # Set up rawtominc command my(@args) = ('rawtominc'); push(@args, '-clobber') if $opt{clobber}; # Swap bytes? if($bs || $opt{byte_swap_data}){ push(@args, '-swap_bytes'); } # datatype and ranges push(@args, @{$gen_hdr->{datatype}}); push(@args, '-range', $gen_hdr->{voxel_min}, $gen_hdr->{voxel_max}); if($gen_hdr->{real_min} < $gen_hdr->{real_max}){ push(@args, '-real_range', $gen_hdr->{real_min}, $gen_hdr->{real_max}); } else{ push(@args, '-scan_range'); } # orientation and step information push(@args, $gen_hdr->{orientation}); push(@args, '-xstep', $gen_hdr->{xstep}, '-ystep', $gen_hdr->{ystep}, '-zstep', $gen_hdr->{zstep}); # if no start info center on the volume if(!defined($gen_hdr->{xstart})){ warn "$me: No x origin info found, guessing..\n"; $gen_hdr->{xstart} = -$gen_hdr->{xsize}/2*$gen_hdr->{xstep}; } if(!defined($gen_hdr->{ystart})){ warn "$me: No y origin info found, guessing..\n"; $gen_hdr->{ystart} = -$gen_hdr->{ysize}/2*$gen_hdr->{ystep}; } if(!defined($gen_hdr->{zstart})){ warn "$me: No z origin info found, guessing..\n"; $gen_hdr->{zstart} = -$gen_hdr->{zsize}/2*$gen_hdr->{zstep}; } push(@args, '-xstart', $gen_hdr->{xstart}, '-ystart', $gen_hdr->{ystart}, '-zstart', $gen_hdr->{zstart}); # history push(@args, '-sattribute', ":history=$history"); # files and sizes push(@args, '-input', $rawfile, $mncfile); if($gen_hdr->{ntime} > 1){ push(@args, $gen_hdr->{ntime}); } push(@args, @{$gen_hdr}{'zsize','ysize','xsize'}); &do_cmd(@args); } # Read a minc file header and return an appropriate ANALYZE 7.5 header sub read_minc{ my($mncfile) = shift; my(@data, $args, @start, @step, @length, @dims); my($h) = create_analyze_header(); # ($h->{cal_min}, $h->{cal_max}) = volume_minmax($mncfile); # $h->{glmin} = 0; # $h->{glmax} = 65535; $args = "mincinfo -error_string 0 ". "-attval xspace:start -attval yspace:start -attval zspace:start ". "-attval xspace:step -attval yspace:step -attval zspace:step ". "-dimlength xspace -dimlength yspace -dimlength zspace ". "-dimnames $mncfile"; @data = split(/\n/, `$args`); @start = @data[0..2]; @step = @data[3..5]; @length = @data[6..8]; @dims = split(" ", $data[9]); print "Start: ". join(" - ", @start) . "\n"; print "Step: ". join(" - ", @step) . "\n"; print "Length: ". join(" - ", @length) . "\n"; print "Dims: ". join(" - ", @dims) . "\n"; my $c = 0; $h->{dim}[$c] = $h->{pixdim}[$c] = $#dims + 2; foreach (@dims){ $h->{dim}[$c+1] = $length[$c]; $h->{pixdim}[$c+1] = abs($step[$c]); $c ++; } # contains: 0 corresponding ANALYZE datatype # 1 corresponding ANALYZE bitpix # 2 min # 3 max my(%dtypes) = ( 'byte' => [ 2, 8, 0, 256], # unsigned 'short' => [ 4, 16, -32767, 32767], # signed 'long' => [ 8, 32, ], # signed 'float' => [ 16, 32, ], 'double' => [ 64, 64, ], 'vector' => [128, 24, ], ); chomp(my $dt = `mincinfo -vartype image $mncfile`); $h->{datatype} = $dtypes{$dt}[0]; $h->{bitpix} = $dtypes{$dt}[1]; return $h; } sub do_cmd { print STDOUT "@_\n" if $opt{verbose}; if(!$opt{fake}){ system(@_) == 0 or die "$me: Died executing command: '@_'\n\n"; } } minc-tools-2.3.00+dfsg/conversion/ana2mnc/ana_dump.pl0000755000175000000620000002126212574624760021455 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - a.janke@gmail.com # Center for Magnetic Resonance # The University of Queensland # # ANALYZE is a commercial file format from the mayo clinic: www.mayo.edu/bir # This script merely dumps the contents of a header file # # Tue Feb 6 09:49:25 EST 2001 - created use warnings "all"; use strict; chomp(my($prog) = `basename $0`); my($Usage) = "Usage: $prog \n"; # Check arguments if ($#ARGV < 0){ die $Usage; } my $anafile = $ARGV[0]; $anafile =~ s/\....$//; if (!-e "$anafile.hdr"){ die "$prog: Couldn't find $anafile.hdr\n"; } print STDOUT dump_analyze_header(read_analyze_header("$anafile.hdr")); # Subroutines ####################################################### # Unpack a value from a string (passed by reference) sub destruct{ my($stringref, $offset, $type) = @_; return unpack("x$offset $type", $$stringref); } # Read an analyze 7.5 header file and return a hash sub read_analyze_header{ open(HDR, $_[0]) or die "Error opening file: $_[0]\n"; read(HDR, my($hdr_s), 40); read(HDR, my($dim_s), 108); read(HDR, my($his_s), 200); close(HDR); my($h) = {}; $h->{sizeof_hdr} = destruct(\$hdr_s, 0,'i' ); # 4 - the byte size of the header file $h->{data_type} = destruct(\$hdr_s, 4,'a10'); # 10 - $h->{db_name} = destruct(\$hdr_s, 14,'a18'); # 18 - $h->{extents} = destruct(\$hdr_s, 32,'i' ); # 4 - should be 16384 $h->{session_error} = destruct(\$hdr_s, 36,'s' ); # 2 - $h->{regular} = destruct(\$hdr_s, 38,'a' ); # 1 - 'r' indicates volumes are the same size $h->{hkey_un0} = destruct(\$hdr_s, 39,'c' ); # 1 - # 40 bytes $h->{dim} =[destruct(\$dim_s, 0,'s8' )]; # 16 - array of the image dimensions # dim[0] # of dimensions; usually 4 # dim[1] X dim - pixels in an image row # dim[2] Y dim - pixel rows in slice # dim[3] Z dim - slices in a volume # dim[4] Time dim - volumes in database $h->{vox_units} = destruct(\$dim_s, 16,'a4' ); # 4 - spatial units of measure for a voxel $h->{cal_units} = destruct(\$dim_s, 20,'a8' ); # 8 - name of the calibration unit $h->{unused1} = destruct(\$dim_s, 28,'s' ); # 2 $h->{datatype} = destruct(\$dim_s, 30,'s' ); # 2 - datatype for this image set $h->{bitpix} = destruct(\$dim_s, 32,'s' ); # 2 - # of bits per voxel 1, 8, 16, 32, or 64. $h->{dim_un0} = destruct(\$dim_s, 34,'s' ); # 2 - $h->{pixdim} =[destruct(\$dim_s, 36,'f8' )]; # 32 - pixdim[] specifies the voxel dimensions: # pixdim[1] - voxel width # pixdim[2] - voxel height # pixdim[3] - interslice distance # ..etc $h->{vox_offset} = destruct(\$dim_s, 68,'f' ); # 4 - byte offset in .img at which voxels start. # This value can be negative to specify # that the absolute value is applied for # every image $h->{scale_factor} = destruct(\$dim_s, 72,'f' ); # 4 = funused1; scale factor used by SPM $h->{funused1} = destruct(\$dim_s, 76,'f' ); # 4 $h->{funused2} = destruct(\$dim_s, 80,'f' ); # 4 $h->{cal_max} = destruct(\$dim_s, 84,'f' ); # 4 - calibrated max & min: $h->{cal_min} = destruct(\$dim_s, 88,'f' ); # 4 www.mailbase.ac.uk/lists/spm/2000-09/0099.html $h->{compressed} = destruct(\$dim_s, 92,'f' ); # 4 $h->{verified} = destruct(\$dim_s, 96,'f' ); # 4 $h->{glmax} = destruct(\$dim_s,100,'i' ); # 4 - global max | pixel values $h->{glmin} = destruct(\$dim_s,104,'i' ); # 4 - global min | (entire database) #108 bytes $h->{descrip} = destruct(\$his_s, 0,'a80'); # 80 $h->{aux_file} = destruct(\$his_s, 80,'a24'); # 24 $h->{orient} = destruct(\$his_s,104,'c' ); # 1 - slice orientation | disregarded # 0 - transverse unflipped | by SPM # 1 - coronal unflipped | instead a # 2 - sagittal unflipped | .mat file is # 3 - transverse flipped | written out # 4 - coronal flipped | with this # 5 - sagittal flipped | information $h->{originator} =[destruct(\$his_s,105,'s5' )]; # 10 - origin # originator[0] x-origin | non standard # originator[1] y-origin | SPM use only # originator[2] z-origin | $h->{generated} = destruct(\$his_s,115,'a10'); # 10 $h->{scannum} = destruct(\$his_s,125,'a10'); # 10 $h->{patient_id} = destruct(\$his_s,135,'a10'); # 10 $h->{exp_date} = destruct(\$his_s,145,'a10'); # 10 $h->{exp_time} = destruct(\$his_s,155,'a10'); # 10 $h->{hist_un0} = destruct(\$his_s,165,'a3' ); # 3 $h->{views} = destruct(\$his_s,168,'i' ); # 4 $h->{vols_added} = destruct(\$his_s,172,'i' ); # 4 $h->{start_field} = destruct(\$his_s,176,'i' ); # 4 $h->{field_skip} = destruct(\$his_s,180,'i' ); # 4 $h->{omax} = destruct(\$his_s,184,'i' ); # 4 $h->{omin} = destruct(\$his_s,188,'i' ); # 4 $h->{smax} = destruct(\$his_s,192,'i' ); # 4 $h->{smin} = destruct(\$his_s,196,'i' ); # 4 #200 bytes return $h; } # return a ASCII dump of an ANALYZE 7.5 header sub dump_analyze_header{ my($h) = shift; return "HDR:\n". " sizeof_hdr <$h->{sizeof_hdr}>\n". " data_type <$h->{data_type}>\n". " db_name <$h->{db_name}>\n". " extents <$h->{extents}>\n". " session_error <$h->{session_error}>\n". " regular <$h->{regular}>\n". " hkey_un0 <$h->{hkey_un0}>\n". "DIMENSION:\n". " dim: <" . join(' ', @{$h->{dim}}) . ">\n". " vox_units <$h->{vox_units}>\n". " cal_units <$h->{cal_units}>\n". " unused1 <$h->{unused1}>\n". " datatype <$h->{datatype}>\n". " bitpix <$h->{bitpix}>\n". " dim_un0 <$h->{dim_un0}>\n". " pixdim <" . join(' ', @{$h->{pixdim}}) . ">\n". " vox_offset <$h->{vox_offset}>\n". " scale_factor <$h->{scale_factor}>\n". " funused1 <$h->{funused1}>\n". " funused2 <$h->{funused2}>\n". " cal_max <$h->{cal_max}>\n". " cal_min <$h->{cal_min}>\n". " compressed <$h->{compressed}>\n". " verified <$h->{verified}>\n". " glmax <$h->{glmax}>\n". " glmin <$h->{glmin}>\n". "HISTORY:\n". " descrip <$h->{descrip}>\n". " aux_file <$h->{aux_file}>\n". " orient <$h->{orient}>\n". " originator <" . join(' ', @{$h->{originator}}) . ">\n". " generated <$h->{generated}>\n". " scannum <$h->{scannum}>\n". " patient_id <$h->{patient_id}>\n". " exp_date <$h->{exp_date}>\n". " exp_time <$h->{exp_time}>\n". " hist_un0 <$h->{hist_un0}>\n". " views <$h->{views}>\n". " vols_added <$h->{vols_added}>\n". " start_field <$h->{start_field}>\n". " field_skip <$h->{field_skip}>\n". " omax <$h->{omax}>\n". " omin <$h->{omin}>\n". " smax <$h->{smax}>\n". " smin <$h->{smin}>\n"; } minc-tools-2.3.00+dfsg/conversion/ana2mnc/ana2mnc_xfm_reduce.pl0000755000175000000620000010334212574624760023411 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - a.janke@gmail.com # Center for Magnetic Resonance # The University of Queensland # # MINC is a free file format from the MNI: www.bic.mni.mcgill.ca # ANALYZE is a commercial file format from the mayo clinic: www.mayo.edu/bir # SPM is a free fMRI toolkit using a hybrid ANALYZE format: www.fil.ion.bpmf.ac.uk/spm # # Inspired by Peter Neelins' (neelin@bic.mni.mcgill.ca) ana2mnc for ANALYZE 4.x. # Since then it has grown into a veritable swiss-army chainsaw of # SPM, ANALYZE 7.5 and MINC conversion. # # The reading and transformation of SPM .mat files to minc .xfm files is # Still somewhat experimental: The following is from spm_realign.m # x1 = M(1,1)*x0 + M(1,2)*y0 + M(1,3)*z0 + M(1,4) # y1 = M(2,1)*x0 + M(2,2)*y0 + M(2,3)*z0 + M(2,4) # z1 = M(3,1)*x0 + M(3,2)*y0 + M(3,3)*z0 + M(3,4) # This is equivalent to the MINC version (if you dig enough in the MINC # distribution you will find a file volume_io/Geometry/transforms.c) # in the function: homogenous_transform_point # The actual matrix decomposing is analagous to Louis Collins's # extract_parameters_from_matrix in minctracc/Numerical/make_rots.c # in mni_autoreg package # # It can be invoked as: # ana2mnc - convert ANALYZE 7.5 to MINC # mnc2ana - convert MINC to ANALYZE 7.5 # ana_show - Show the contents of an ANALYZE 7.5 header # ana_write - write an ANALYZE header (requires gobs of additional CL arguments) # # Wed Oct 18 09:29:44 EST 2000 - updated to Analyze 7.5 format # Thu Oct 19 16:57:30 EST 2000 - Added support for writing out ANALYZE 7.5 headers # Wed Oct 25 10:29:54 EST 2000 - Added mnc2ana, ana_show and ana_write functionality # Tue Nov 28 15:58:57 EST 2000 - Completed transformation decomposition added xfm_reduce require 5.0; use strict; use warnings "all"; use Math::Trig; use Math::MatrixReal; use MNI::Startup qw(nocputimes); use MNI::MincUtilities qw(:range :geometry); use Getopt::Tabular; my($clobber) = ''; my($verbose) = 0; my($spm) = 0; my($args_h) = create_analyze_header(); my(@opt_table) = ( ["-verbose", "boolean", 0, \$verbose, "Be Verbose"], ["-clobber", "const", "-clobber", \$clobber, "clobber existing files"], ["-spm", "boolean", 0, \$spm, "Read in the SPM .mat as well"], $h->{data_type} = 0; $h->{db_name} = ''; $h->{regular} = 'r'; $h->{hkey_un0} = 0; $h->{dim} = [0,0,0,0,0,0,0,0]; $h->{vox_units} = ''; $h->{cal_units} = ''; $h->{unused1} = 0; $h->{datatype} = 0; $h->{bitpix} = 0; $h->{dim_un0} = 0; $h->{pixdim} = [0,0,0,0,0,0,0,0]; $h->{vox_offset} = 0; $h->{scale_factor} = 1.0; $h->{funused1} = 0; $h->{funused2} = 0; $h->{cal_max} = 0; $h->{cal_min} = 0; $h->{compressed} = 0; $h->{verified} = 0; $h->{glmax} = 0; $h->{glmin} = 0; $h->{descrip} = ''; $h->{aux_file} = ''; $h->{orient} = 0; $h->{originator} = [0,0,0,0,0]; $h->{generated} = ''; $h->{scannum} = ''; $h->{patient_id} = ''; $h->{exp_date} = ''; $h->{exp_time} = ''; $h->{hist_un0} = ''; $h->{views} = 0; $h->{vols_added} = 0; $h->{start_field} = 0; $h->{field_skip} = 0; $h->{omax} = 0; $h->{omin} = 0; $h->{smax} = 0; $h->{smin} = 0; } ); chomp(my($prog) = `basename $0`); my $files; my $nfiles; if ($prog eq "ana2mnc"){ $files = " "; $nfiles = 2; } elsif ($prog eq "mnc2ana"){ $files = " "; $nfiles = 2; } elsif ($prog eq "ana_show"){ $files = ""; $nfiles = 1; } elsif ($prog eq "ana_write"){ $files = ""; $nfiles = 1; } elsif ($prog eq "spm_show"){ $files = ""; $nfiles = 1; } elsif ($prog eq "spm_conv"){ $files = ""; $nfiles = 1; } elsif ($prog eq "xfm_reduce"){ $files = ""; $nfiles = 1; } my($Usage) = "Usage: $prog [options] $files\n". "$prog -help to list options\n"; my($Help) = <{bitpix} == 8) ? '-unsigned ' : '-signed '; $args .= "$mncfile > $anafile.img\n"; if ($verbose){ print STDOUT $args; } system($args) == 0 or die "Died during mincextract command\n"; } elsif ($prog eq "ana_show"){ $anafile = $ARGV[0]; $anafile =~ s/\....$//; if (!-e "$anafile.hdr"){ die "$prog: Couldn't find $anafile.hdr\n"; } print STDOUT dump_analyze_header(read_analyze_header("$anafile.hdr")); } elsif ($prog eq "ana_write"){ $anafile = $ARGV[0]; $anafile =~ s/\....$//; if (-e "$anafile.hdr" && ($clobber ne "-clobber")){ die "$prog: $anafile.hdr exists! use -clobber to overwrite\n"; } my($h) = create_analyze_header(); write_analyze_header($h, "$anafile.hdr"); } elsif ($prog eq "spm_show"){ $anafile = $ARGV[0]; $anafile =~ s/\....$//; if (!-e "$anafile.mat"){ die "$prog: Couldn't find $anafile.mat\n"; } print STDOUT `mat1dump $anafile.mat`; } elsif ($prog eq "spm_conv"){ $anafile = $ARGV[0]; $anafile =~ s/\....$//; chomp(my $date = `date`); my($M, @Trans, @Scale, @Shear, @Rotn); if (!-e "$anafile.mat"){ die "$prog: Couldn't find $anafile.mat\n"; } # perl line noise to read in a text file Matrix into MatrixReal my @matdump = `mat1dump $anafile.mat`; $M = Math::MatrixReal->new_from_string("[ " . join(" ]\n[ ", @matdump[1..4]) . " ]\n"); if ($verbose){ print STDERR "M:\n$M"; } decompose_transformation_matrix($M, \@Trans, \@Scale, \@Shear, \@Rotn); print_transformation_matrix($M, "M"); my (@resliceR, $sign); my $c = 0; foreach (@Rotn){ $resliceR[$c] = 0; $sign = $_/abs($_); if (abs($_) > pi()){ die "$prog: Bugger! this angle is too big!: $_\n"; } if (abs($_) > pi()/2){ # if 90 deg $Rotn[$c] -= $sign * pi()/2; $resliceR[$c] += $sign * pi()/2; } if (abs($_) > pi()/4){ # if 45 deg reverse the rotation (subtract 90deg). $Rotn[$c] -= $sign * pi()/2; $resliceR[$c] += $sign * pi()/2; } $c ++; } my $RESLICE = create_transformation_matrix(undef, undef, undef, \@resliceR); print_transformation_matrix($RESLICE, "RESLICE"); my $MINCxfm = create_transformation_matrix(\@Trans, \@Scale, \@Shear, \@Rotn); $MINCxfm = $M * $RESLICE->decompose_LR()->invert_LR(); print_transformation_matrix($MINCxfm, "MINCxfm"); print_transformation_matrix($MINCxfm * $RESLICE, "CHECK"); open(SPM_XFM, ">$anafile.spm_xfm"); print SPM_XFM "SPM Transform File\n". "%$date>>> Created by $prog from $anafile.mat\n\n". "@matdump[1..3]"; close(SPM_XFM); } elsif ($prog eq "xfm_reduce"){ $xfmfile = $ARGV[0]; my($Mtext, $M, @Trans, @Scale, @Shear, @Rotn); if (!-e "$xfmfile"){ die "$prog: Couldn't find $xfmfile\n"; } # perl line noise to read in a text file Matrix into MatrixReal my @MNIdump = `cat $xfmfile`; my $line; do{ $line = shift(@MNIdump) or die "$prog: Couldn't find a MNI Transform in: $xfmfile\n";} until $line =~ m/Linear_Transform/; foreach (@MNIdump[0..2]){ chomp; s/\;//; $Mtext .= "[ $_ ]\n"; } $Mtext .= "[ 0 0 0 1 ]\n"; $M = Math::MatrixReal->new_from_string($Mtext); decompose_transformation_matrix($M, \@Trans, \@Scale, \@Shear, \@Rotn); my $c = 0; foreach (@Rotn){ $Rotn[$c] = rad2deg($_); $c ++; } printf("Translation: %10.5f %10.5f %10.5f\n", @Trans); printf("Rotation: %10.5f %10.5f %10.5f\n", @Rotn); printf("Scale: %10.5f %10.5f %10.5f\n", @Scale); printf("Shear: %10.5f %10.5f %10.5f\n", @Shear); } # Subroutines ####################################################### # Unpack a value from a string (passed by reference) sub destruct{ my($stringref, $offset, $type) = @_; return unpack("x$offset $type", $$stringref); } # Read an analyze 7.5 header file and return a hash sub read_analyze_header{ open(HDR, $_[0]) or die "Error opening file: $_[0]\n"; read(HDR, my($hdr_s), 40); read(HDR, my($dim_s), 108); read(HDR, my($his_s), 200); close(HDR); my($h) = {}; $h->{sizeof_hdr} = destruct(\$hdr_s, 0,'i' ); # 4 - the byte size of the header file $h->{data_type} = destruct(\$hdr_s, 4,'a10'); # 10 - $h->{db_name} = destruct(\$hdr_s, 14,'a18'); # 18 - $h->{extents} = destruct(\$hdr_s, 32,'i' ); # 4 - should be 16384 $h->{session_error} = destruct(\$hdr_s, 36,'s' ); # 2 - $h->{regular} = destruct(\$hdr_s, 38,'a' ); # 1 - 'r' indicates volumes are the same size $h->{hkey_un0} = destruct(\$hdr_s, 39,'c' ); # 1 - # 40 bytes $h->{dim} =[destruct(\$dim_s, 0,'s8' )]; # 16 - array of the image dimensions # dim[0] # of dimensions; usually 4 # dim[1] X dim - pixels in an image row # dim[2] Y dim - pixel rows in slice # dim[3] Z dim - slices in a volume # dim[4] Time dim - volumes in database $h->{vox_units} = destruct(\$dim_s, 16,'a4' ); # 4 - spatial units of measure for a voxel $h->{cal_units} = destruct(\$dim_s, 20,'a8' ); # 8 - name of the calibration unit $h->{unused1} = destruct(\$dim_s, 28,'s' ); # 2 $h->{datatype} = destruct(\$dim_s, 30,'s' ); # 2 - datatype for this image set $h->{bitpix} = destruct(\$dim_s, 32,'s' ); # 2 - # of bits per voxel 1, 8, 16, 32, or 64. $h->{dim_un0} = destruct(\$dim_s, 34,'s' ); # 2 - $h->{pixdim} =[destruct(\$dim_s, 36,'f8' )]; # 32 - pixdim[] specifies the voxel dimensions: # pixdim[1] - voxel width # pixdim[2] - voxel height # pixdim[3] - interslice distance # ..etc $h->{vox_offset} = destruct(\$dim_s, 68,'f' ); # 4 - byte offset in .img at which voxels start. # This value can be negative to specify # that the absolute value is applied for # every image $h->{scale_factor} = destruct(\$dim_s, 72,'f' ); # 4 = funused1; scale factor used by SPM $h->{funused1} = destruct(\$dim_s, 76,'f' ); # 4 $h->{funused2} = destruct(\$dim_s, 80,'f' ); # 4 $h->{cal_max} = destruct(\$dim_s, 84,'f' ); # 4 - calibrated max & min: $h->{cal_min} = destruct(\$dim_s, 88,'f' ); # 4 www.mailbase.ac.uk/lists/spm/2000-09/0099.html $h->{compressed} = destruct(\$dim_s, 92,'f' ); # 4 $h->{verified} = destruct(\$dim_s, 96,'f' ); # 4 $h->{glmax} = destruct(\$dim_s,100,'i' ); # 4 - global max | pixel values $h->{glmin} = destruct(\$dim_s,104,'i' ); # 4 - global min | (entire database) #108 bytes $h->{descrip} = destruct(\$his_s, 0,'a80'); # 80 $h->{aux_file} = destruct(\$his_s, 80,'a24'); # 24 $h->{orient} = destruct(\$his_s,104,'c' ); # 1 - slice orientation | disregarded # 0 - transverse unflipped | by SPM # 1 - coronal unflipped | instead a # 2 - sagittal unflipped | .mat file is # 3 - transverse flipped | written out # 4 - coronal flipped | with this # 5 - sagittal flipped | information $h->{originator} =[destruct(\$his_s,105,'s5' )]; # 10 - origin # originator[0] x-origin | non standard # originator[1] y-origin | SPM use only # originator[2] z-origin | $h->{generated} = destruct(\$his_s,115,'a10'); # 10 $h->{scannum} = destruct(\$his_s,125,'a10'); # 10 $h->{patient_id} = destruct(\$his_s,135,'a10'); # 10 $h->{exp_date} = destruct(\$his_s,145,'a10'); # 10 $h->{exp_time} = destruct(\$his_s,155,'a10'); # 10 $h->{hist_un0} = destruct(\$his_s,165,'a3' ); # 3 $h->{views} = destruct(\$his_s,168,'i' ); # 4 $h->{vols_added} = destruct(\$his_s,172,'i' ); # 4 $h->{start_field} = destruct(\$his_s,176,'i' ); # 4 $h->{field_skip} = destruct(\$his_s,180,'i' ); # 4 $h->{omax} = destruct(\$his_s,184,'i' ); # 4 $h->{omin} = destruct(\$his_s,188,'i' ); # 4 $h->{smax} = destruct(\$his_s,192,'i' ); # 4 $h->{smin} = destruct(\$his_s,196,'i' ); # 4 #200 bytes return $h; } # return a ASCII dump of an ANALYZE 7.5 header sub dump_analyze_header{ my($h) = shift; return "HDR:\n". " sizeof_hdr <$h->{sizeof_hdr}>\n". " data_type <$h->{data_type}>\n". " db_name <$h->{db_name}>\n". " extents <$h->{extents}>\n". " session_error <$h->{session_error}>\n". " regular <$h->{regular}>\n". " hkey_un0 <$h->{hkey_un0}>\n". "DIMENSION:\n". " dim: <" . join(' ', @{$h->{dim}}) . ">\n". " vox_units <$h->{vox_units}>\n". " cal_units <$h->{cal_units}>\n". " unused1 <$h->{unused1}>\n". " datatype <$h->{datatype}>\n". " bitpix <$h->{bitpix}>\n". " dim_un0 <$h->{dim_un0}>\n". " pixdim <" . join(' ', @{$h->{pixdim}}) . ">\n". " vox_offset <$h->{vox_offset}>\n". " scale_factor <$h->{scale_factor}>\n". " funused1 <$h->{funused1}>\n". " funused2 <$h->{funused2}>\n". " cal_max <$h->{cal_max}>\n". " cal_min <$h->{cal_min}>\n". " compressed <$h->{compressed}>\n". " verified <$h->{verified}>\n". " glmax <$h->{glmax}>\n". " glmin <$h->{glmin}>\n". "HISTORY:\n". " descrip <$h->{descrip}>\n". " aux_file <$h->{aux_file}>\n". " orient <$h->{orient}>\n". " originator <" . join(' ', @{$h->{originator}}) . ">\n". " generated <$h->{generated}>\n". " scannum <$h->{scannum}>\n". " patient_id <$h->{patient_id}>\n". " exp_date <$h->{exp_date}>\n". " exp_time <$h->{exp_time}>\n". " hist_un0 <$h->{hist_un0}>\n". " views <$h->{views}>\n". " vols_added <$h->{vols_added}>\n". " start_field <$h->{start_field}>\n". " field_skip <$h->{field_skip}>\n". " omax <$h->{omax}>\n". " omin <$h->{omin}>\n". " smax <$h->{smax}>\n". " smin <$h->{smin}>\n"; } # Write an ANALYZE 7.5 header to a file sub write_analyze_header{ my($h, $hdrfile) = @_; my $hdr_s = pack("i", $h->{sizeof_hdr} ); $hdr_s .= pack("a10", $h->{data_type} ); $hdr_s .= pack("a18", $h->{db_name} ); $hdr_s .= pack("i", $h->{extents} ); $hdr_s .= pack("s", $h->{session_error} ); $hdr_s .= pack("a", $h->{regular} ); $hdr_s .= pack("c", $h->{hkey_un0} ); my $dim_s = pack("s8", @{$h->{dim}}[0..7] ); $dim_s .= pack("a4", $h->{vox_units} ); $dim_s .= pack("a8", $h->{cal_units} ); $dim_s .= pack("s", $h->{unused1} ); $dim_s .= pack("s", $h->{datatype} ); $dim_s .= pack("s", $h->{bitpix} ); $dim_s .= pack("s", $h->{dim_un0} ); $dim_s .= pack("f8", @{$h->{pixdim}}[0..7] ); $dim_s .= pack("f", $h->{vox_offset} ); $dim_s .= pack("f", $h->{scale_factor} ); $dim_s .= pack("f", $h->{funused1} ); $dim_s .= pack("f", $h->{funused2} ); $dim_s .= pack("f", $h->{cal_max} ); $dim_s .= pack("f", $h->{cal_min} ); $dim_s .= pack("f", $h->{compressed} ); $dim_s .= pack("f", $h->{verified} ); $dim_s .= pack("i", $h->{glmax} ); $dim_s .= pack("i", $h->{glmin} ); my $his_s = pack("a80",$h->{descrip} ); $his_s .= pack("a24", $h->{aux_file} ); $his_s .= pack("c", $h->{orient} ); $his_s .= pack("s5", @{$h->{originator}}[0..4] ); $his_s .= pack("a10", $h->{generated} ); $his_s .= pack("a10", $h->{scannum} ); $his_s .= pack("a10", $h->{patient_id} ); $his_s .= pack("a10", $h->{exp_date} ); $his_s .= pack("a10", $h->{exp_time} ); $his_s .= pack("a3", $h->{hist_un0} ); $his_s .= pack("i", $h->{views} ); $his_s .= pack("i", $h->{vols_added} ); $his_s .= pack("i", $h->{start_field} ); $his_s .= pack("i", $h->{field_skip} ); $his_s .= pack("i", $h->{omax} ); $his_s .= pack("i", $h->{omin} ); $his_s .= pack("i", $h->{smax} ); $his_s .= pack("i", $h->{smin} ); open(HDR, ">$hdrfile") or die "Error opening file: $hdrfile\n"; syswrite(HDR, $hdr_s, 40); syswrite(HDR, $dim_s, 108); syswrite(HDR, $his_s, 200); close(HDR); } # Create and return an ANALYZE 7.5 as a hash sub create_analyze_header{ my($h) = {}; $h->{sizeof_hdr} = 348; $h->{data_type} = 0; $h->{db_name} = ''; $h->{extents} = 16384; $h->{session_error} = 0; $h->{regular} = 'r'; $h->{hkey_un0} = 0; $h->{dim} = [0,0,0,0,0,0,0,0]; $h->{vox_units} = ''; $h->{cal_units} = ''; $h->{unused1} = 0; $h->{datatype} = 0; $h->{bitpix} = 0; $h->{dim_un0} = 0; $h->{pixdim} = [0,0,0,0,0,0,0,0]; $h->{vox_offset} = 0; $h->{scale_factor} = 1.0; $h->{funused1} = 0; $h->{funused2} = 0; $h->{cal_max} = 0; $h->{cal_min} = 0; $h->{compressed} = 0; $h->{verified} = 0; $h->{glmax} = 0; $h->{glmin} = 0; $h->{descrip} = ''; $h->{aux_file} = ''; $h->{orient} = 0; $h->{originator} = [0,0,0,0,0]; $h->{generated} = ''; $h->{scannum} = ''; $h->{patient_id} = ''; $h->{exp_date} = ''; $h->{exp_time} = ''; $h->{hist_un0} = ''; $h->{views} = 0; $h->{vols_added} = 0; $h->{start_field} = 0; $h->{field_skip} = 0; $h->{omax} = 0; $h->{omin} = 0; $h->{smax} = 0; $h->{smin} = 0; return $h; } # Write out a MINC file based upon a given ANALYZE 7.5 header and a raw data file sub write_minc{ my($h, $mncfile, $imgfile) = @_; my(%dtypes) = ( 0 => ['-short', '-unsigned'], 2 => ['-byte', '-unsigned'], 4 => ['-short', '-signed'], 8 => ['-long', '-signed'], 16 => ['-float'], 64 => ['-double'], 128 => ['-byte', '-unsigned', '-vector', '3'] ); # Check the data type if (!defined($dtypes{$h->{datatype}})){ die "Unknown data type: $h->{datatype}\n"; } # Gethistory string chomp(my($history) = `date`); $history .= '>>>> ' . join(' ', $prog, @ARGV); # Get step info my(@world_order); if (($h->{orient} == 0) || ($h->{orient} == 3)){ # Transverse $h->{orientation} = '-transverse'; @world_order = (1, 2, 3); } elsif (($h->{orient} == 1) || ($h->{orient} == 4)){ # Coronal $h->{orientation} = '-coronal'; @world_order = (1, 3, 2); } elsif (($h->{orient} == 2) || ($h->{orient} == 5)){ # Sagittal $h->{orientation} = '-sagittal'; @world_order = (3, 1, 2); } else{ # Unknown warn "Unknown data orientation: assuming transverse\n"; $h->{orientation} = '-transverse'; @world_order = (1, 2, 3); } my(@steps) = @{$h->{pixdim}}[@world_order]; # set up the steps in mm co-ordinates (as opposed to ANALYZE voxel co-ordinates) my($c) = 0; foreach (@steps){ print "$c: $_ @{$h->{originator}}[$c]\n"; @{$h->{originator}}[$c] = (@{$h->{originator}}[$c])*$_; print "$c: $_ @{$h->{originator}}[$c]\n"; $c ++; } # Set up rawtominc command my($args) = "rawtominc $clobber @{$dtypes{$h->{datatype}}} ". "-range $h->{glmin} $h->{glmax} "; $args .= ($h->{cal_min} < $h->{cal_max}) ? "-real_range $h->{cal_min} $h->{cal_max} " : "-scan_range "; $args .= "$h->{orientation} ". "-xstep $steps[0] -ystep $steps[1] -zstep $steps[2] ". "-origin @{$h->{originator}}[0, 1, 2] ". "-sattribute :history='$history' ". "-input $imgfile $mncfile "; $args .= (@{$h->{dim}}[4] > 1) ? "@{$h->{dim}}[4] " : ''; $args .= "@{$h->{dim}}[3, 2, 1]\n"; # Check for compressed input if (!-e $imgfile && -e "$imgfile.gz"){ system("gunzip $imgfile.gz\n") == 0 or die "Died during gunzip\n"; } if ($verbose){ print STDOUT $args; } system($args) == 0 or die "Died during rawtominc system command\n"; } # Read a minc file header and return an appropriate ANALYZE 7.5 header sub read_minc{ my($mncfile) = shift; my(@start, @step, @length, @dir_cosines, @dims, $permutation); my($h) = create_analyze_header(); # ($h->{cal_min}, $h->{cal_max}) = volume_minmax($mncfile); $h->{glmin} = 0; $h->{glmax} = 65535; volume_params($mncfile, \@start, \@step, \@length, \@dir_cosines, \@dims); $permutation = (&get_dimension_order($mncfile))[1]; my $c = 0; $h->{dim}[$c] = $h->{pixdim}[$c] = $#{$permutation} + 2; foreach (@{$permutation}){ $h->{dim}[$c+1] = $length[$c]; $h->{pixdim}[$c+1] = abs($step[$c]); $c ++; } # contains: 0 corresponding ANALYZE datatype # 1 corresponding ANALYZE bitpix # 2 min # 3 max my(%dtypes) = ( 'byte' => [ 2, 8, 0, 256], # unsigned 'short' => [ 4, 16, -32767, 32767], # signed 'long' => [ 8, 32, ], # signed 'float' => [ 16, 32, ], 'double' => [ 64, 64, ], 'vector' => [128, 24, ], ); chomp(my $dt = `mincinfo -vartype image $mncfile`); $h->{datatype} = $dtypes{$dt}[0]; $h->{bitpix} = $dtypes{$dt}[1]; return $h; } # Takes a 4x4 transformation matrix $M (using the Math:MatrixReal module) # Returns the translation, scale, shear and rotations encoded in the input # matrix. # # Andrew Janke - a.janke@gmail.com # Losely based on Louis Collins' make_rots.c from the mni_autoreg package # With substantial help from Mark Griffin - mark.griffin@cmr.uq.edu.au sub decompose_transformation_matrix{ my($M, $Trans, $Scale, $Shear, $Rotn) = @_; my ($Sx, $Sy, $Sz, $SHa, $SHb, $SHc, $Rx, $Ry, $Rz); # TRANSLATIONS - [M] = [T][S][SH][R] # As of yet I am assuming the center of rotation is (0,0,0) as # I am not exactly sure as to what SPM does here. @$Trans[0] = $M->element(1, 4); @$Trans[1] = $M->element(2, 4); @$Trans[2] = $M->element(3, 4); my $T = $M->shadow(); $T->one(); # Create and zero the Translation Matrix $T->assign(1, 4, @$Trans[0]); $T->assign(2, 4, @$Trans[1]); $T->assign(3, 4, @$Trans[2]); # SCALES - [M] = inv[T][T][S][SH][R] = [S][SH][R] # Here we use an identical method to Louis Collins's in mni_autoreg # Namely multiply a unit vector in each direction and measure the length # after the transformation. $M = $T->decompose_LR()->invert_LR() * $M; my $SSHRinv = $M->decompose_LR()->invert_LR(); my $Unit = Math::MatrixReal->new(4, 1); $Unit->zero(); $Unit->assign(1, 1, 1); $Sx = ($SSHRinv * $Unit)->length(); $Unit->zero(); $Unit->assign(2, 1, 1); $Sy = ($SSHRinv * $Unit)->length(); $Unit->zero(); $Unit->assign(3, 1, 1); $Sz = ($SSHRinv * $Unit)->length(); my $Sinv = $M->shadow(); $Sinv->zero(); # Create and zero the inverse Scaling Matrix $Sinv->assign(1, 1, $Sx); $Sx = 1/$Sx; $Sinv->assign(2, 2, $Sy); $Sy = 1/$Sy; $Sinv->assign(3, 3, $Sz); $Sz = 1/$Sz; # SHEARS - [M] = inv[T][T]inv[S][S][SH][R] = [SH][R] # We assume the shear matrix: SH [ 1 0 0 0 ] # where x' = x [ a 1 0 0 ] # y' = ax + y [ b c 1 0 ] # z' = bx + cy + z [ 0 0 0 1 ] # # However as M at this point is in fact [SH][R] # we can extract a, b and c as such: # # let [ M1 ] # [ M2 ] = [SH][R] # [ M3 ] # # thus: # # a = (M2 . M1) / |M1|^2 # b = (M3 . M1) / |M1|^2 # c = (M3 . T) / |T|^2 where T = M2 - (a . M1) # # We could also use the determinant to determine whether we have an # Orthogonal matrix and thus don;t have shears, but we don't do this yet.... $M = $Sinv * $M; # check determinant for "sheariness" if det != 0 shears exist. # my $det = $M->decompose_LR()->det_LR(); my $M1 = ~$M->row(1); my $M2 = ~$M->row(2); my $M3 = ~$M->row(3); $SHa = $M2->scalar_product($M1) / $M1->scalar_product($M1); $SHb = $M3->scalar_product($M1) / $M1->scalar_product($M1); my $TMP = $M2 - ($SHa * $M1); $SHc = $M3->scalar_product($TMP) / $TMP->scalar_product($TMP); my $SH = $M->shadow(); $SH->one(); # Create and zero the Shear Matrix $SH->assign(2, 1, $SHa); $SH->assign(3, 1, $SHb); $SH->assign(3, 2, $SHc); # ROTATIONS - [M] = inv[T][T]inv[S][S]inv[SH][SH][R] = [R] # We assume cy is positive to ensure we get one of the 2 possible solutions # where rotations are between -pi and pi. # # Here we deduce Rx, Ry and Rz by virtue or the fact that the rotation # matrix is as follows. # # R = [ cos(Ry)*cos(Rz) 0 ] # [ cos(Ry)*sin(Rz) 0 ] # [ sin(Ry) sin(Rx)*cos(Ry) cos(Rx)*cos(Ry) 0 ] # [ 0 0 0 0 ] # # Then the quadrant of the angle must be deduced by the sign of # cos and sin for the particular rotation. $M = $SH->decompose_LR()->invert_LR() * $M; # Get Y Rotation and check that we aren't up a creek without a paddle my $sy = $M->element(3, 1); if (abs($sy) == 1) { die "cos X = 0. I haven't solved this yet...\n"; } $Ry = asin($sy); # Get X Rotation my $cy = cos($Ry); my $sx = $M->element(3, 2) / $cy; my $cx = $M->element(3, 3) / $cy; $Rx = asin($sx); if ($cx < 0){ if ($sx > 0){ $Rx = pi() - $Rx; } # quadrant 2 else { $Rx = -pi() - $Rx; } # quadrant 3 } # Get Z Rotation my $cz = $M->element(1, 1) / $cy; my $sz = $M->element(2, 1) / $cy; $Rz = asin($sz); if ($cz < 0){ if ($sz > 0){ $Rz = pi() - $Rz; } # quadrant 2 else { $Rz = -pi() - $Rz; } # quadrant 3 } # If verbose do a bit of checking and output the remainder which should # be the identity matrix or close to it if ($verbose){ my $RX = $M->shadow(); $RX->one(); # Create and zero the X Rotation Matrix $RX->assign(2, 2, cos($Rx)); $RX->assign(2, 3, -sin($Rx)); $RX->assign(3, 2, sin($Rx)); $RX->assign(3, 3, cos($Rx)); my $RY = $M->shadow(); $RY->one(); # Create and zero the Y Rotation Matrix $RY->assign(1, 1, cos($Ry)); $RY->assign(1, 3, -sin($Ry)); $RY->assign(3, 1, sin($Ry)); $RY->assign(3, 3, cos($Ry)); my $RZ = $M->shadow(); $RZ->one(); # Create and zero the Z Rotation Matrix $RZ->assign(1, 1, cos($Rz)); $RZ->assign(1, 2, -sin($Rz)); $RZ->assign(2, 1, sin($Rz)); $RZ->assign(2, 2, cos($Rz)); my $RZYXinv = ($RZ * $RY * $RX)->decompose_LR()->invert_LR(); print "Remainder:\n" . ($RZYXinv * $M); } @$Scale[0] = $Sx; @$Scale[1] = $Sy; @$Scale[2] = $Sz; @$Shear[0] = $SHa; @$Shear[1] = $SHb; @$Shear[2] = $SHc; @$Rotn[0] = $Rx; @$Rotn[1] = $Ry; @$Rotn[2] = $Rz; } # Creates a 4x4 transformation matrix $M using the input # Translations, scales, shears and rotations (or not) sub create_transformation_matrix{ my($Trans, $Scale, $Shear, $Rotn) = @_; # set a few defaults my $c; for ($c = 0; $c < 3; $c ++){ if (!defined @$Trans[$c]){ @$Trans[$c] = 0; } if (!defined @$Scale[$c]){ @$Scale[$c] = 1; } if (!defined @$Shear[$c]){ @$Shear[$c] = 0; } if (!defined @$Rotn[$c] ){ @$Rotn[$c] = 0; } } my $M = Math::MatrixReal->new(4, 4); $M->one(); $M->assign(1, 4, @$Trans[0]); $M->assign(2, 4, @$Trans[1]); $M->assign(3, 4, @$Trans[2]); $M->assign(1, 1, @$Scale[0]); $M->assign(2, 2, @$Scale[1]); $M->assign(3, 3, @$Scale[2]); my $SH = $M->shadow(); $SH->one(); # Create and zero the Shear Matrix $SH->assign(2, 1, @$Shear[0]); $SH->assign(3, 1, @$Shear[1]); $SH->assign(3, 2, @$Shear[2]); my $RX = $M->shadow(); $RX->one(); # Create and zero the X Rotation Matrix $RX->assign(2, 2, cos(@$Rotn[0])); $RX->assign(2, 3, -sin(@$Rotn[0])); $RX->assign(3, 2, sin(@$Rotn[0])); $RX->assign(3, 3, cos(@$Rotn[0])); my $RY = $M->shadow(); $RY->one(); # Create and zero the Y Rotation Matrix $RY->assign(1, 1, cos(@$Rotn[1])); $RY->assign(1, 3, -sin(@$Rotn[1])); $RY->assign(3, 1, sin(@$Rotn[1])); $RY->assign(3, 3, cos(@$Rotn[1])); my $RZ = $M->shadow(); $RZ->one(); # Create and zero the Z Rotation Matrix $RZ->assign(1, 1, cos(@$Rotn[2])); $RZ->assign(1, 2, -sin(@$Rotn[2])); $RZ->assign(2, 1, sin(@$Rotn[2])); $RZ->assign(2, 2, cos(@$Rotn[2])); return $M * $SH * $RZ * $RY * $RX; } sub print_transformation_matrix{ my($M, $name) = @_; my (@Trans, @Scale, @Shear, @Rotn, @Rotnd); print "$name:\n$M"; decompose_transformation_matrix($M, \@Trans, \@Scale, \@Shear, \@Rotn); foreach (@Rotn){ push(@Rotnd, rad2deg($_)); } printf("Translation: %10.5f %10.5f %10.5f\n", @Trans); printf("Rotation: %10.5f %10.5f %10.5f\n", @Rotnd); printf("Scale: %10.5f %10.5f %10.5f\n", @Scale); printf("Shear: %10.5f %10.5f %10.5f\n", @Shear); } minc-tools-2.3.00+dfsg/conversion/ana2mnc/README0000644000175000000620000000152512574624760020211 0ustar stevestaff ana2mnc - Analyze to MINC conversion (and SPM) ---------------------------------------------- For more info use the inbuilt help: ana2mnc -help Installation ana2mnc is a self contained perl script, all you need to do is change the first line of the script to reflect where your version of perl 5 resides. ana2mnc uses the GetOpt::Tabular module for argument parsing. If you do not have this installed already, it can be obtained from www.cpan.org To "make" the other utilities (mnc2ana, ana_show, spm2mnc, spm2xfm, spm_show) Run the shell script 'make_links'. The resulting files are merely symlinks to ana2mnc. The different names are required in order that ana2mnc knows what it is doing. This mechanism may well be replaced in the future.. :) Andrew Janke - a.janke@gmail.com minc-tools-2.3.00+dfsg/conversion/ecattominc/0002755000175000000620000000000012574624760020137 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/ecattominc/machine_indep.c0000644000175000000620000001760612574624760023076 0ustar stevestaff#include #include #include #ifdef _WIN32 unsigned short ntohs(unsigned short us) { unsigned char *p = (unsigned char*)&us; return ((unsigned short)p[1] + (p[0] >> 8)); } unsigned long ntohl(unsigned long ul) { unsigned char *p = (unsigned char*)&ul; return ((unsigned long)p[3] + (p[2] >> 8) + (p[1] >> 16) + (p[0] >> 24)); } #else #include #endif void get_short_value(const void *from, void *to) { short int short_value; (void) memcpy(&short_value, from, sizeof(short int)); *(short int *)to = ntohs(short_value); } void get_long_value(const void *from, void *to) { long int long_value; (void) memcpy(&long_value, from, sizeof(long int)); *(long int *)to = ntohl(long_value); } /* we need this, as the ecat directory block uses 32-bit ints */ void get_int32_value(const void *from, void *to) { int32_t int32_value; (void) memcpy(&int32_value, from, sizeof(int32_t)); *(int32_t *)to = ntohl(int32_value); } /* from vax_conversion.c -Copyright 1993 Peter Neelin */ /* ----------------------------- MNI Header ----------------------------------- @NAME : vax_conversions.c @DESCRIPTION: File containing routines to convert machine values to vax format values. @METHOD : @CREATED : December 10, 1992 (Peter Neelin) @MODIFIED : * $Log: machine_indep.c,v $ * Revision 6.6 2011-01-21 01:06:58 nikelski * * Fixed seg fault in ecattominc on 64-bit systems * * Corrected by modifying ecat_file.c and machine_indep.c as follows: * - added include providing access to the int32_t type * - changed the "dirblock" buffer from long to int32_t, making explicit * that this buffer needs to hold 32-bit ints (reflecting the ecat file) * - added function get_int32_value to return 32-bit ints from the * dirblock buffer * * Revision 6.5 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.4 2008/01/17 02:33:01 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 2005/05/19 20:58:37 bert * Fixes for Windows builds ported from 1.X branch * * Revision 6.1.2.2 2005/05/04 20:19:30 bert * Modify for Windows builds * * Revision 6.1.2.1 2005/02/15 19:59:54 bert * Initial checkin on 1.X branch * * Revision 6.1 2005/01/19 19:46:28 bert * Changes from Anthonin Reilhac * * Revision 6.2 1999/10/19 15:57:18 neelin * Fixed log message containing log substitution * * 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.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:32 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:34:18 neelin * Pre-release * * Revision 1.4 93/08/04 13:04:01 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. ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vax_short @INPUT : nvals - number of values to convert vax_value - pointer to array of shorts in vax format @OUTPUT : mach_value - pointer to array of shorts in current machine format @RETURNS : (nothing) @DESCRIPTION: Converts vax short integers to short integers in the format of the current machine. @METHOD : @GLOBALS : (none) @CALLS : memcpy @CREATED : December 10, 1992. @MODIFIED : ---------------------------------------------------------------------------- */ void get_vax_short(int nvals, void *vax_value, short *mach_value) { int i; char *ptr1, *ptr2, v0, v1; #ifdef vax memcpy((void *) mach_value, vax_value, nvals*sizeof(short)); #else ptr1 = (char *) vax_value; ptr2 = (char *) mach_value; for (i=0; i #include #include #define MIbloodroot "blood_analysis" /* ----------------------------- MNI Header ----------------------------------- @NAME : CreateBloodStructures @INPUT : mincHandle -> a handle for an open MINC file. This file should be open for writing, but not in redefinition mode. bloodHandle -> a handle for an open BNC file. This file should be open for reading. @OUTPUT : none @RETURNS : void @DESCRIPTION: Copies all variable definitions (with attributes) from the BNC file to the MINC file. The appropriate dimensions are also copied. @METHOD : none. Just muddled through. @GLOBALS : none @CALLS : micopy_all_var_defs (MINC library) miadd_child (MINC library) @CREATED : May 30, 1994 by MW @MODIFIED : ---------------------------------------------------------------------------- */ void CreateBloodStructures (int mincHandle, int bloodHandle) { int mincRoot; int bloodRoot; /* * Copy all the variables with their attributes. */ (void) micopy_all_var_defs (bloodHandle, mincHandle, 0, NULL); /* * Make the blood analysis root variable a child of * the MINC root variable. */ mincRoot = ncvarid (mincHandle, MIrootvariable); bloodRoot = ncvarid (mincHandle, MIbloodroot); (void) miadd_child (mincHandle, mincRoot, bloodRoot); } /* ----------------------------- MNI Header ----------------------------------- @NAME : FillBloodStructures @INPUT : mincHandle -> a handle for an open MINC file. This file should be open for writing, but not in redefinition mode. bloodHandle -> a handle for an open BNC file. This file should be open for reading. @OUTPUT : none @RETURNS : void @DESCRIPTION: Copies all variable values from the BNC file to the MINC file. The variable themselves should already exist in the MINC file (see CreateBloodStructures). @METHOD : none. Just muddled through. @GLOBALS : none @CALLS : micopy_all_var_values (MINC library) @CREATED : May 30, 1994 by MW @MODIFIED : ---------------------------------------------------------------------------- */ void FillBloodStructures (int mincHandle, int bloodHandle) { (void) micopy_all_var_values (bloodHandle, mincHandle, 0, NULL); } minc-tools-2.3.00+dfsg/conversion/ecattominc/machine_indep.h0000644000175000000620000000441012574624760023070 0ustar stevestaff#ifndef _ECAT_MACHINE_INDEP_H #define _ECAT_MACHINE_INDEP_H 1 void get_short_value(const void *from, void *to); void get_long_value(const void *from, void *to); /* from vax_conversions.h -Copyright 1993 Peter Neelin */ /* ----------------------------- MNI Header ----------------------------------- @NAME : vax_conversions.h @DESCRIPTION: Header file for vax type conversion routines @GLOBALS : @CALLS : @CREATED : January 8, 1993 @MODIFIED : * $Log: machine_indep.h,v $ * Revision 6.1 2005-01-19 19:46:28 bert * Changes from Anthonin Reilhac * * Revision 6.2 1999/10/19 15:57:19 neelin * Fixed log message containing log substitution * * 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.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:32 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:34:21 neelin * Pre-release * * Revision 1.3 93/08/04 13:04:03 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. ---------------------------------------------------------------------------- */ void get_vax_short(int nvals, void *vax_value, short *mach_value); void get_vax_long(int nvals, void *vax_value, long *mach_value); void get_vax_float(int nvals, void *vax_value, float *mach_value); void get_int32_value(const void *from, void *to); #endif /*_ECAT_MACHINE_INDEP_H*/ minc-tools-2.3.00+dfsg/conversion/ecattominc/ecattominc.man10000644000175000000620000000461412574624760023046 0ustar stevestaff.TH "ecattominc" "1" "" "" "" .SH "NAME" ecattominc \- convert an ecat format file (version 6.x or 7.x) to a minc format file .SH "SYNOPSIS" \fBecattominc [] \fR \fBecattominc [\-help]\fR .SH "DESCRIPTION" \fIecattominc\fR will convert an ecat data file (version 6.x or 7.x) to a minc file format Unless the \fB\-small_header\fR option is specified, \fBecattominc\fR will conserve the maximum informations from the ecat header and subheader fields which will be respectively stored as attributes of the ecat\-main and ecat\-subhdr minc variables. By default the whole 3D or 4D volume is converted, however, \fBecattominc\fR allows for the selection of slice and frame ranges to be copied. The voxel values of the generated minc file are decay corrected, unless the \fBnodecay_correct\fR flag is specified. Finally, blood data file can be inserted within the minc file with the \fB\-bloodfile\fR option. .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH "General options" .TP \fI\-byte:\fR Write out data as bytes (default). .TP \fI\-short:\fR Write out data as short integers. .TP \fI\-clobber:\fR Overwrite existing file. .TP \fI\-noclobber:\fR Don't overwrite existing file (default). .TP \fI\-verbose:\fR List files as they are converted (default) .TP \fI\-quiet:\fR Do not list files as they are converted. .SH "Command specific options" \fI\-decay_correct:\fR Do decay correction on images (default). .TP \fI\-nodecay_correct:\fR Don't do decay correction. .TP \fI\-slices:\fRRange of slices to copy (counting from 0). Default value: \-2147483648 \-2147483648 .TP \fI\-frames:\fR Range of frames to copy (counting from 0). Default value: \-2147483648 \-2147483648 .TP \fI\-frame:\fR Single frame to copy (counting from 0). Default value: \-2147483648 .TP \fI\-small_header:\fR Copy only basic header information. .TP \fI\-all_header:\fR Copy all header information (default). .TP \fI\-bloodfile:\fR Insert blood data from this file. .SH "Generic options for all commands" .TP \fI\-help:\fR Print summary of command\-line options and abort .SH "KNOWN BUGS" No bug listed so far :=) .SH "SEE ALSO" minctoecat(1), rawtominc(1), minctoraw(1), dicomtominc(1) .SH "AUTHOR" Peter Neelin and Anthonin Reilhac (anthonin.reilhac@cermep.fr) .SH "COPYRIGHTS" Copyrights 2005 by Peter Neelin minc-tools-2.3.00+dfsg/conversion/ecattominc/Makefile0000644000175000000620000000074712574624760021605 0ustar stevestaff# -------------------------------------------------------------------- # # MINC Makefile # #ROOT = ../../minc #include $(ROOT)/Make_machine_specific #include $(ROOT)/Make_configuration # Executable names PROGS = ecattominc dump_ecat_header EXTRA_OBJS = ecat_file.o insertblood.o HEADERS = ecat_file.h ecat_header_definition.h CDEFINES = -DDEBUG # cpp defines LDOPT = $(PROG_LDOPT) MANSECT = 1 #MANPAGES = $(PROGS).$(MANSECT) #include $(ROOT)/progs/Make_progs minc-tools-2.3.00+dfsg/conversion/ecattominc/dump_ecat_header.c0000644000175000000620000001237212574624760023557 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dump_ecat_header @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Dump ECAT file header information @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1996 (Peter Neelin) @MODIFIED : * $Log: dump_ecat_header.c,v $ * Revision 6.4 2008-01-17 02:33:01 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 2005/01/19 19:46:00 bert * Changes from Anthonin Reilhac * * Revision 6.1 1999/10/29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:22 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:21 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:04 neelin * Release of minc version 0.4 * * Revision 1.1 1996/01/18 14:52:14 neelin * Initial revision * @COPYRIGHT : Copyright 1996 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 #include "ecat_file.h" /* Main program */ int main(int argc, char *argv[]) { Ecat_file *fp; char *pname, *filename, *fieldname; int index, volume, slice, iheader, ifield; Ecat_field_name field; char *description; int length; int error; char svalue[ECAT_MAX_STRING_LENGTH]; /* Check the arguments */ pname = argv[0]; if ((argc < 2) || (argc > 6)) { (void) fprintf(stderr, "Usage: %s [ [ [ ]]]\n", pname); return EXIT_FAILURE; } filename = argv[1]; fieldname = ((argc > 2) ? argv[2] : (char *) NULL); index = ((argc > 3) ? atoi(argv[3]) : 0); volume = ((argc > 4) ? atoi(argv[4]) : 0); slice = ((argc > 5) ? atoi(argv[5]) : 0); if ((fieldname != NULL) && (strlen(fieldname) == 0)) fieldname = NULL; /* Open the file */ fp = ecat_open(filename); if (fp == NULL) { (void) fprintf(stderr, "%s: Error opening file \"%s\"", pname, filename); if (errno != 0) { (void) fprintf(stderr, " - "); perror(NULL); } else { (void) fprintf(stderr, "\n"); } exit(EXIT_FAILURE); } /* Loop over main header then appropriate subheader */ for (iheader = 0; iheader < 2; iheader++) { /* Print out header type */ if (fieldname == NULL) { if (iheader == 0) { (void) printf("Main header:\n"); } else { (void) printf("Subheader (volume %d, slice %d):\n", volume, slice); } } /* Loop over fields */ ifield = -1; do { ifield++; /* Get information on the next field */ if (iheader == 0) { field = ecat_list_main(fp, ifield); description = ecat_get_main_field_description(fp, field); length = ecat_get_main_field_length(fp, field); error = ecat_get_main_value(fp, field, index, NULL, NULL, svalue); } else { field = ecat_list_subhdr(fp, ifield); description = ecat_get_subhdr_field_description(fp, field); length = ecat_get_subhdr_field_length(fp, field); error = ecat_get_subhdr_value(fp, volume, slice, field, index, NULL, NULL, svalue); } /* Check for the end of list */ if (field == ECAT_No_Field) continue; /* If we are looking for a specific field, check to see if we've found it */ if ((fieldname != NULL) && (description != NULL) && (strcmp(fieldname, description) != 0)) { continue; } /* Look for an error in reading the value */ if (error) { (void) fprintf(stderr, " Error retrieving field \"%s\".\n", (description == NULL ? "" : description)); continue; } /* Print out appropriate results */ if ((fieldname == NULL) || (strcmp(fieldname, description) == 0)) { (void) printf(" %s", description); if (length > 1) (void) printf("[%d/%d]", index, length); (void) printf(" = %s\n", svalue); } } while (field != ECAT_No_Field); } /* End of loop over headers */ /* Close the file */ ecat_close(fp); return EXIT_SUCCESS; } minc-tools-2.3.00+dfsg/conversion/ecattominc/ecat_file.h0000644000175000000620000001440212574624760022222 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_file.h @DESCRIPTION: Header file for routines that read ECAT image files @GLOBALS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : * $Log: ecat_file.h,v $ * Revision 6.2 2005-01-19 19:46:01 bert * Changes from Anthonin Reilhac * * Revision 6.1 1999/10/29 17:52:01 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:22 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:21 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:04 neelin * Release of minc version 0.4 * * Revision 1.2 1996/03/26 15:58:18 neelin * Added some more header values. * * Revision 1.1 1996/01/18 14:52:14 neelin * Initial revision * @COPYRIGHT : Copyright 1996 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. ---------------------------------------------------------------------------- */ #ifndef public #define public #endif #ifndef private #define private static #endif #define ECAT_MAX_STRING_LENGTH 64 /*memory allocation*/ #define MALLOC(size) ((void *) malloc_check(size)) #define FREE(ptr) free(ptr) #define REALLOC(ptr, size) ((void *) realloc_check(ptr, size)) #define CALLOC(nelem, elsize) ((void *) calloc(nelem, elsize)) public void *malloc_check(size_t size); public void *realloc_check(void *ptr, size_t size); typedef enum { ECAT_No_Field, ECAT_Magic_Number, ECAT_Original_Filename, ECAT_Sw_Version, ECAT_System_Type, ECAT_File_Type, ECAT_Serial_Number, ECAT_Scan_Start_Time, ECAT_Isotope_Name, ECAT_Isotope_Halflife, ECAT_Radiopharmaceutical, ECAT_Gantry_Tilt, ECAT_Gantry_Rotation, ECAT_Bed_Elevation, ECAT_Intrinsic_Tilt, ECAT_Wobble_Speed, ECAT_Transm_Source_Type, ECAT_Distance_Scanned, ECAT_Transaxial_Fov, ECAT_Angular_Compression, ECAT_Coin_Samp_Mode, ECAT_Axial_Samp_Mode, ECAT_Calibration_Factor, ECAT_Calibration_Units, ECAT_Calibration_Units_Label, ECAT_Compression_Code, ECAT_Study_Type, ECAT_Patient_Id, ECAT_Patient_Name, ECAT_Patient_Sex, ECAT_Patient_Dexterity, ECAT_Patient_Age, ECAT_Patient_Height, ECAT_Patient_Weight, ECAT_Patient_Birth_Date, ECAT_Physician_Name, ECAT_Operator_Name, ECAT_Study_Description, ECAT_Acquision_Type, ECAT_Patient_Orientation, ECAT_Facility_Name, ECAT_Num_Planes, ECAT_Num_Frames, ECAT_Num_Gates, ECAT_Num_Bed_Pos, ECAT_Init_Bed_Position, ECAT_Bed_Position, ECAT_Plane_Separation, ECAT_Lwr_Sctr_Thres, ECAT_Lwr_True_Thres, ECAT_Upr_True_Thres, ECAT_User_Process_Code, ECAT_Acquisition_Mode, ECAT_Bin_Size, ECAT_Branching_Fraction, ECAT_Dose_Start_Time, ECAT_Dosage, ECAT_Well_Counter_Corr_Factor, ECAT_Data_Units, ECAT_Septa_State, ECAT_Data_Type, ECAT_Num_Dimensions, ECAT_X_Dimension, ECAT_Y_Dimension, ECAT_Z_Dimension, ECAT_X_Offset, ECAT_Y_Offset, ECAT_Z_Offset, ECAT_Recon_Zoom, ECAT_Scale_Factor, ECAT_Image_Min, ECAT_Image_Max, ECAT_X_Pixel_Size, ECAT_Y_Pixel_Size, ECAT_Z_Pixel_Size, ECAT_Frame_Duration, ECAT_Frame_Start_Time, ECAT_Filter_Code, ECAT_X_Resolution, ECAT_Y_Resolution, ECAT_Z_Resolution, ECAT_X_Rotation_Angle, ECAT_Y_Rotation_Angle, ECAT_Z_Rotation_Angle, ECAT_Decay_Corr_Fctr, ECAT_Corrections_Applied, ECAT_Gate_Duration, ECAT_R_Wave_Offset, ECAT_Num_Accepted_Beats, ECAT_Filter_Cutoff_Frequency, ECAT_Filter_Dc_Component, ECAT_Filter_Ramp_Slope, ECAT_Filter_Order, ECAT_Filter_Scatter_Fraction, ECAT_Filter_Scatter_Slope, ECAT_Annotation, ECAT_Da_X_Rotation_Angle, ECAT_Da_Y_Rotation_Angle, ECAT_Da_Z_Rotation_Angle, ECAT_Da_X_Translation, ECAT_Da_Y_Translation, ECAT_Da_Z_Translation, ECAT_Da_X_Scale_Factor, ECAT_Da_Y_Scale_Factor, ECAT_Da_Z_Scale_Factor, ECAT_Rfilter_Cutoff, ECAT_Rfilter_Resolution, ECAT_Rfilter_Code, ECAT_Rfilter_Order, ECAT_Zfilter_Cutoff, ECAT_Zfilter_Resolution, ECAT_Zfilter_Code, ECAT_Zfilter_Order, ECAT_Scan_Start_Day, ECAT_Scan_Start_Month, ECAT_Scan_Start_Year, ECAT_Scan_Start_Hour, ECAT_Scan_Start_Minute, ECAT_Scan_Start_Second, ECAT_Rot_Source_Speed } Ecat_field_name; typedef struct Ecat_file Ecat_file; /* Routine declarations */ public Ecat_file *ecat_open(char *filename); public void ecat_close(Ecat_file *file); public int ecat_get_num_planes(Ecat_file *file); public int ecat_get_num_frames(Ecat_file *file); public int ecat_get_num_bed_positions(Ecat_file *file); public int ecat_get_num_gates(Ecat_file *file); public Ecat_field_name ecat_list_main(Ecat_file *file, int index); public Ecat_field_name ecat_list_subhdr(Ecat_file *file, int index); public int ecat_get_main_field_length(Ecat_file *file, Ecat_field_name field); public int ecat_get_subhdr_field_length(Ecat_file *file, Ecat_field_name field); public char *ecat_get_main_field_description(Ecat_file *file, Ecat_field_name field); public char *ecat_get_subhdr_field_description(Ecat_file *file, Ecat_field_name field); public int ecat_get_main_value(Ecat_file *file, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue); public int ecat_get_subhdr_value(Ecat_file *file, int volume, int slice, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue); public int ecat_get_image(Ecat_file *file, int volume, int slice, short *image); minc-tools-2.3.00+dfsg/conversion/ecattominc/ecat_file.c0000644000175000000620000012025112574624760022215 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_file.c @DESCRIPTION: File containing routines to read ECAT image files @GLOBALS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : * $Log: ecat_file.c,v $ * Revision 6.5 2011-01-21 01:06:58 nikelski * * Fixed seg fault in ecattominc on 64-bit systems * * Corrected by modifying ecat_file.c and machine_indep.c as follows: * - added include providing access to the int32_t type * - changed the "dirblock" buffer from long to int32_t, making explicit * that this buffer needs to hold 32-bit ints (reflecting the ecat file) * - added function get_int32_value to return 32-bit ints from the * dirblock buffer * * Revision 6.4 2008/01/17 02:33:01 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 2005/01/19 19:46:00 bert * Changes from Anthonin Reilhac * * Revision 6.1 1999/10/29 17:52:01 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:22 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:21 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:04 neelin * Release of minc version 0.4 * * Revision 1.1 1996/01/18 14:52:14 neelin * Initial revision * @COPYRIGHT : Copyright 1996 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 #include #include "ecat_file.h" #include "machine_indep.h" /*#include */ /* Set some standard macros */ #ifndef SEEK_SET # define SEEK_SET 0 #endif #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif /* Constants */ #define BLOCK_SIZE 512 #define MAIN_HEADER_SIZE BLOCK_SIZE #define SUBHEADER_SIZE BLOCK_SIZE #define DIRBLOCK_SIZE BLOCK_SIZE #define MAGIC_STRING "MATRIX" #define FIRST_DIRBLOCK 2 #define DRBLK_NEXT 1 #define DRBLK_NUMUSED 3 #define DRBLK_SKIP 4 #define DRBLK_WIDTH 4 #define DRBLK_PTR 1 /* Header definition types */ typedef enum { ecat_byte, ecat_short, ecat_long, ecat_float, ecat_char } Ecat_type; typedef struct { Ecat_field_name name; int offset; int length; Ecat_type type; char *description; } Ecat_field_description_type; typedef struct { int initialized; int num_entries; Ecat_field_description_type *fields; Ecat_field_name *file_order; } Ecat_header_table_type; typedef struct { Ecat_header_table_type *main_header; Ecat_header_table_type *subheader; } Ecat_header_description_type; /* Header definition */ #include "ecat_header_definition.h" /* ECAT file Type */ struct Ecat_file { FILE *file_pointer; Ecat_header_description_type *header_description; int vax_byte_order; int num_planes; int num_frames; int num_bed_positions; int num_gates; int num_volumes; unsigned char *main_header; long cur_subhdr_offset; unsigned char *subheader; int num_subhdrs; long *subhdr_offsets; }; typedef enum { ECAT_MAIN_HEADER, ECAT_SUBHEADER } Ecat_which_header; /* Private functions */ static Ecat_field_name ecat_list_fields(Ecat_file *file, Ecat_which_header which_header, int index); static int ecat_get_field_length(Ecat_file *file, Ecat_which_header which_header, Ecat_field_name field); static char *ecat_get_field_description(Ecat_file *file, Ecat_which_header which_header, Ecat_field_name field); static int ecat_get_value(Ecat_file *file, Ecat_which_header which_header, int volume_number, int slice_number, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue); static int ecat_read_subhdr(Ecat_file *file, int volume, int slice); static int ecat_lookup_field(Ecat_header_table_type *table, Ecat_field_name field, int *offset, int *length, Ecat_type *type, char **description); static void ecat_initialize_table(Ecat_header_table_type *table); static int ecat_table_entry_compare(const void *v1, const void *v2); static int ecat_table_offset_compare(const void *v1, const void *v2); static int ecat_read_directory(Ecat_file *file); static long get_dirblock(Ecat_file *file, int32_t *dirblock, int offset); static int ecat_get_subhdr_offset(Ecat_file *file, int volume, int slice, long *offset); /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_open @INPUT : filename - name of file to open @OUTPUT : (none) @RETURNS : Pointer to ECAT file descriptor or NULL if an error occurs. @DESCRIPTION: Routine to open an ECAT file (for reading only), given its pathname. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Ecat_file *ecat_open(char *filename) { Ecat_file *file; /* Allocate space for an ecat file structure */ file = (void *) MALLOC(sizeof(*file)); file->main_header = (void *) MALLOC(MAIN_HEADER_SIZE); file->subheader = (void *) MALLOC(SUBHEADER_SIZE); file->cur_subhdr_offset = -1; file->subhdr_offsets = NULL; /* Open the file */ if ((file->file_pointer=fopen(filename, "rb")) == NULL) { ecat_close(file); return NULL; } /* Read in the main header */ if (fread(file->main_header, sizeof(char), (size_t) MAIN_HEADER_SIZE, file->file_pointer) != MAIN_HEADER_SIZE) { ecat_close(file); return NULL; } /* Figure out which type of file we are using */ if (strncmp((char *)file->main_header, MAGIC_STRING, strlen(MAGIC_STRING)) == 0) { file->header_description = ECAT_VER_7; file->vax_byte_order = FALSE; } else { file->header_description = ECAT_VER_PRE7; file->vax_byte_order = TRUE; } /* Get the number of frames, slices, bed positions and gates */ if (ecat_get_main_value(file, ECAT_Num_Planes, 0, &file->num_planes, NULL, NULL) || ecat_get_main_value(file, ECAT_Num_Frames, 0, &file->num_frames, NULL, NULL) || ecat_get_main_value(file, ECAT_Num_Bed_Pos, 0, &file->num_bed_positions, NULL, NULL) || ecat_get_main_value(file, ECAT_Num_Gates, 0, &file->num_gates, NULL, NULL)) { ecat_close(file); return NULL; } file->num_volumes = file->num_frames; if (file->num_volumes < file->num_bed_positions) file->num_volumes = file->num_bed_positions; if (file->num_volumes < file->num_gates) file->num_volumes = file->num_gates; /* Read the directory structure */ if (ecat_read_directory(file)) { ecat_close(file); return NULL; } /* Return the file pointer */ return file; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_close @INPUT : file - ecat file pointer @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close an ECAT file and free the associated structures @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void ecat_close(Ecat_file *file) { if (file==NULL) return; if (file->file_pointer != NULL) { (void) fclose(file->file_pointer); } if (file->subhdr_offsets != NULL) { FREE(file->subhdr_offsets); } if (file->main_header != NULL) FREE(file->main_header); if (file->subheader != NULL) FREE(file->subheader); FREE(file); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_num_{planes,frames,bed_positions,gates} @INPUT : file - ecat file pointer @OUTPUT : (none) @RETURNS : Number of * for an ECAT file. @DESCRIPTION: Routine to get the number of planes (slices), frames, bed_positions or gates in an ECAT file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_num_planes(Ecat_file *file) { return file->num_planes; } int ecat_get_num_frames(Ecat_file *file) { return file->num_frames; } int ecat_get_num_bed_positions(Ecat_file *file) { return file->num_bed_positions; } int ecat_get_num_gates(Ecat_file *file) { return file->num_gates; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_list_main @INPUT : file - ecat file pointer index - index into field list @OUTPUT : (none) @RETURNS : Next Ecat_field_name value, ECAT_No_Field if index is too large. @DESCRIPTION: Routine to list the fields in an ECAT main header. Should be called repeatedly with increasing values of index (starting from 0) until field ECAT_No_Field is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Ecat_field_name ecat_list_main(Ecat_file *file, int index) { return ecat_list_fields(file, ECAT_MAIN_HEADER, index); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_list_subhdr @INPUT : file - ecat file pointer index - index into field list @OUTPUT : (none) @RETURNS : Next Ecat_field_name value, ECAT_No_Field if index is too large. @DESCRIPTION: Routine to list the fields in an ECAT subheader. Should be called repeatedly with increasing values of index (starting from 0) until field ECAT_No_Field is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ Ecat_field_name ecat_list_subhdr(Ecat_file *file, int index) { return ecat_list_fields(file, ECAT_SUBHEADER, index); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_list_fields @INPUT : file - ecat file pointer which_header - header in which to look index - index into field list @OUTPUT : (none) @RETURNS : Next Ecat_field_name value, ECAT_No_Field if index is too large. @DESCRIPTION: Routine to list the fields in an ECAT subheader. Should be called repeatedly with increasing values of index (starting from 0) until field ECAT_No_Field is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static Ecat_field_name ecat_list_fields(Ecat_file *file, Ecat_which_header which_header, int index) { Ecat_header_table_type *table; /* Get the appropriate header */ switch (which_header) { case ECAT_MAIN_HEADER: table = file->header_description->main_header; break; case ECAT_SUBHEADER: table = file->header_description->subheader; break; default: return ECAT_No_Field; } /* Initialize the table, if needed */ if (!table->initialized) { ecat_initialize_table(table); } /* Check the index */ if ((index < 0) || (index >= table->num_entries)) { return ECAT_No_Field; } /* Return the fields in file order */ return table->file_order[index]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_main_field_length @INPUT : file - ecat file pointer field - ecat field name @OUTPUT : (none) @RETURNS : Length of field. Returns -1 if the field is not found. @DESCRIPTION: Routine to get the length of a field from the ECAT main header. Length is the number of elements, not the number of bytes. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_main_field_length(Ecat_file *file, Ecat_field_name field) { return ecat_get_field_length(file, ECAT_MAIN_HEADER, field); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_subhdr_field_length @INPUT : file - ecat file pointer field - ecat field name @OUTPUT : (none) @RETURNS : Length of field. Returns -1 if the field is not found. @DESCRIPTION: Routine to get the length of a field from an ECAT subheader. Length is the number of elements, not the number of bytes. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_subhdr_field_length(Ecat_file *file, Ecat_field_name field) { return ecat_get_field_length(file, ECAT_SUBHEADER, field); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_field_length @INPUT : file - ecat file pointer which_header - header in which to look field - ecat field name @OUTPUT : (none) @RETURNS : Length of field. Returns -1 if the field is not found. @DESCRIPTION: Routine to get the length of a field from an ECAT header. Length is the number of elements, not the number of bytes. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_get_field_length(Ecat_file *file, Ecat_which_header which_header, Ecat_field_name field) { Ecat_header_table_type *table; Ecat_type type; int length; /* Get the appropriate header */ switch (which_header) { case ECAT_MAIN_HEADER: table = file->header_description->main_header; break; case ECAT_SUBHEADER: table = file->header_description->subheader; break; default: return -1; } /* Look for the field description */ if (ecat_lookup_field(table, field, NULL, &length, &type, NULL)) { return -1; } /* Return the length. If we have a string, then return 1. */ if (type == ecat_char) return 1; else return length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_main_field_description @INPUT : file - ecat file pointer field - ecat field name @OUTPUT : (none) @RETURNS : Description of field. Returns NULL if the field is not found. @DESCRIPTION: Routine to get the description of a field from the ECAT main header. The string returned should not be modified. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *ecat_get_main_field_description(Ecat_file *file, Ecat_field_name field) { return ecat_get_field_description(file, ECAT_MAIN_HEADER, field); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_subhdr_field_description @INPUT : file - ecat file pointer field - ecat field name @OUTPUT : (none) @RETURNS : Description of field. Returns NULL if the field is not found. @DESCRIPTION: Routine to get the description of a field from an ECAT subheader. The string returned should not be modified. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char *ecat_get_subhdr_field_description(Ecat_file *file, Ecat_field_name field) { return ecat_get_field_description(file, ECAT_SUBHEADER, field); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_field_description @INPUT : file - ecat file pointer which_header - header in which to look field - ecat field name @OUTPUT : (none) @RETURNS : Description of field. Returns NULL if the field is not found. @DESCRIPTION: Routine to get the descrition of a field from an ECAT header. The string returned should not be modified. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static char *ecat_get_field_description(Ecat_file *file, Ecat_which_header which_header, Ecat_field_name field) { Ecat_header_table_type *table; char *description; /* Get the appropriate header */ switch (which_header) { case ECAT_MAIN_HEADER: table = file->header_description->main_header; break; case ECAT_SUBHEADER: table = file->header_description->subheader; break; default: return NULL; } /* Look for the field description */ if (ecat_lookup_field(table, field, NULL, NULL, NULL, &description)) { return NULL; } /* Return the description */ return description; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_main_value @INPUT : file - ecat file pointer field - ecat field name index - index for multi-valued fields (counting from zero) @OUTPUT : ivalue - integer value fvalue - floating-point value svalue - string value @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to get a field value from the ECAT main header. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_main_value(Ecat_file *file, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue) { return ecat_get_value(file, ECAT_MAIN_HEADER, 0, 0, field, index, ivalue, fvalue, svalue); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_subhdr_value @INPUT : file - ecat file pointer volume - frame or bed position for subheader (from 0) slice - slice number (counting from 0) field - ecat field name index - index for multi-valued fields (counting from zero) @OUTPUT : ivalue - integer value fvalue - floating-point value svalue - string value @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to get a field value from the ECAT main header. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_subhdr_value(Ecat_file *file, int volume, int slice, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue) { return ecat_get_value(file, ECAT_SUBHEADER, volume, slice, field, index, ivalue, fvalue, svalue); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_value @INPUT : file - ECAT file pointer which_header - header in which to look volume_number - frame or bed position for subheader (counting from 0) slice_number - number of slice for subheader (counting from 0) field - field to look for index - index for multi-valued fields (counting from zero) @OUTPUT : ivalue - integer value fvalue - floating-point value svalue - string value @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to look up a field value in a header table. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_get_value(Ecat_file *file, Ecat_which_header which_header, int volume_number, int slice_number, Ecat_field_name field, int index, int *ivalue, double *fvalue, char *svalue) { /* Sizes of ECAT types */ static int ecat_type_sizes[] = {1, 2, 4, 4, 1}; /* Variables */ unsigned char *header; Ecat_header_table_type *table; int offset, length; Ecat_type type; unsigned char byte_value; short short_value; long long_value; float float_value; char string[ECAT_MAX_STRING_LENGTH]; /* Get the appropriate header */ switch (which_header) { case ECAT_MAIN_HEADER: header = file->main_header; table = file->header_description->main_header; break; case ECAT_SUBHEADER: if (ecat_read_subhdr(file, volume_number, slice_number)) { return TRUE; } header = file->subheader; table = file->header_description->subheader; break; default: return TRUE; } /* Look for the field description */ if (ecat_lookup_field(table, field, &offset, &length, &type, NULL)) { return TRUE; } /* Check the index */ if ((index < 0) || (index >= length) || ((type == ecat_char) && (index > 0))) { return TRUE; } offset += index * ecat_type_sizes[type]; /* Get the value and convert it */ switch (type) { case ecat_byte: byte_value = header[offset]; if (ivalue != NULL) *ivalue = byte_value; if (fvalue != NULL) *fvalue = byte_value; if (svalue != NULL) (void) sprintf(svalue, "%d", (int) byte_value); break; case ecat_short: if (file->vax_byte_order) { get_vax_short(1, &header[offset], &short_value); } else { get_short_value(& header[offset], &short_value); } if (ivalue != NULL) *ivalue = short_value; if (fvalue != NULL) *fvalue = short_value; if (svalue != NULL) (void) sprintf(svalue, "%d", (int) short_value); break; case ecat_long: if (file->vax_byte_order) { get_vax_long(1, &header[offset], &long_value); } else { get_long_value(& header[offset], &long_value); } if (ivalue != NULL) *ivalue = long_value; if (fvalue != NULL) *fvalue = long_value; if (svalue != NULL) (void) sprintf(svalue, "%d", (int) long_value); break; case ecat_float: offset += index * sizeof(float); if (file->vax_byte_order) { get_vax_float(1, &header[offset], &float_value); } else { get_long_value(& header[offset], &float_value); } if (ivalue != NULL) *ivalue = float_value; if (fvalue != NULL) *fvalue = float_value; if (svalue != NULL) (void) sprintf(svalue, "%.7g", (double) float_value); break; case ecat_char: (void) memcpy(string, &header[offset], length); string[length] = '\0'; if (ivalue != NULL) *ivalue = atoi(svalue); if (fvalue != NULL) *fvalue = atof(svalue); if (svalue != NULL) (void) strcpy(svalue, string); break; default: return TRUE; } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_read_subhdr @INPUT : file - ecat file pointer volume - frame or bed position for subheader (from 0) slice - slice number (counting from 0) @OUTPUT : file - file structure is modified to include new subheader @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to read in the appropriate subheader, if needed. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_read_subhdr(Ecat_file *file, int volume, int slice) { long offset; /* Get subheader offset */ if (ecat_get_subhdr_offset(file, volume, slice, &offset)) { return TRUE; } /* Do we need to read in the header? */ if (offset == file->cur_subhdr_offset) { return FALSE; } /* If so, do it */ if (fseek(file->file_pointer, offset, SEEK_SET) || (fread(file->subheader, sizeof(char), (size_t) SUBHEADER_SIZE, file->file_pointer) != SUBHEADER_SIZE)) { return TRUE; } file->cur_subhdr_offset = offset; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_lookup_field @INPUT : table - table containing header fields field - field to look for @OUTPUT : offset - offset into header length - length of field (number of values, not bytes) type - type of values description - string describing field (ptr is passed back by reference) @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to look up a field value in a header table. All output arguments can be NULL to indicate that the corresponding value should not be returned. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_lookup_field(Ecat_header_table_type *table, Ecat_field_name field, int *offset, int *length, Ecat_type *type, char **description) { Ecat_field_description_type field_description, *result; /* Initialize the table, if needed */ if (!table->initialized) { ecat_initialize_table(table); } /* Set up a dummy entry */ field_description.name = field; /* Search for the field */ result = bsearch(&field_description, table->fields, table->num_entries, sizeof(table->fields[0]), ecat_table_entry_compare); if (result == NULL) { return TRUE; } /* Store the results */ if (offset != NULL) *offset = result->offset; if (length != NULL) *length = result->length; if (type != NULL) *type = result->type; if (description != NULL) *description = result->description; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_initialize_table @INPUT : table - table containing header fields @OUTPUT : table - modified appropriately @RETURNS : (nothing) @DESCRIPTION: Routine to initialize the header table. @METHOD : Initialized should be set to TRUE, num_entries should already be set and fields should point to a list. The fields list will be sorted and the file_order field will be set. @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static void ecat_initialize_table(Ecat_header_table_type *table) { Ecat_field_description_type *file_order_list; int iel; /* Check that table is not already initialized */ if (table->initialized) return; /* Sort the field list by field name for binary searching */ qsort(table->fields, table->num_entries, sizeof(table->fields[0]), ecat_table_entry_compare); /* Get a list of field names in file order by copying the field list and sorting it by offset */ file_order_list = MALLOC(table->num_entries * sizeof(*file_order_list)); for (iel=0; iel < table->num_entries; iel++) { file_order_list[iel] = table->fields[iel]; } qsort(file_order_list, table->num_entries, sizeof(file_order_list[0]), ecat_table_offset_compare); table->file_order = MALLOC(table->num_entries * sizeof(table->file_order[0])); for (iel=0; iel < table->num_entries; iel++) { table->file_order[iel] = file_order_list[iel].name; } FREE(file_order_list); /* Mark the table as initialized */ table->initialized = TRUE; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_table_entry_compare @INPUT : v1 - first value @OUTPUT : v2 - second value @RETURNS : (nothing) @DESCRIPTION: Routine to compare field list elements for sorting by name. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_table_entry_compare(const void *v1, const void *v2) { const Ecat_field_description_type *first, *second; /* Get pointers */ first = (const Ecat_field_description_type *) v1; second = (const Ecat_field_description_type *) v2; /* Compare field names */ return ((int) first->name - (int) second->name); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_table_offset_compare @INPUT : v1 - first value @OUTPUT : v2 - second value @RETURNS : (nothing) @DESCRIPTION: Routine to compare field list elements for sorting by offset. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_table_offset_compare(const void *v1, const void *v2) { const Ecat_field_description_type *first, *second; /* Get pointers */ first = (const Ecat_field_description_type *) v1; second = (const Ecat_field_description_type *) v2; /* Compare field names */ return ((int) first->offset - (int) second->offset); } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_read_directory @INPUT : file - ecat file pointer @OUTPUT : (nothing) @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to read in the ECAT subheader directory @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static int ecat_read_directory(Ecat_file *file) { int ientry, start_entry, num_alloc; long nextblock, num_used; int32_t dirblock[DIRBLOCK_SIZE / sizeof(int32_t)]; /* Allocate space for the subheader offset array */ num_alloc = file->num_volumes; if (file->header_description == ECAT_VER_PRE7) num_alloc *= file->num_planes; file->num_subhdrs = 0; file->subhdr_offsets = MALLOC(num_alloc * sizeof(file->subhdr_offsets[0])); /* Reading directory blocks until done */ nextblock = FIRST_DIRBLOCK; do { /* Read in the block */ if (fseek(file->file_pointer, (nextblock - 1) * BLOCK_SIZE, SEEK_SET) || (fread(dirblock, sizeof(char), sizeof(dirblock), file->file_pointer) != sizeof(dirblock))) { return TRUE; } /* Get a pointer to the next block and the number of entries used */ nextblock = get_dirblock(file, dirblock, DRBLK_NEXT); num_used = get_dirblock(file, dirblock, DRBLK_NUMUSED); /* Increment the number of subheaders */ start_entry = file->num_subhdrs; file->num_subhdrs += num_used; if (num_alloc < file->num_subhdrs) { num_alloc = file->num_subhdrs; REALLOC(file->subhdr_offsets, num_alloc * sizeof(file->subhdr_offsets[0])); } /* Save the offsets */ for (ientry=0; ientry < num_used; ientry++) { file->subhdr_offsets[start_entry + ientry] = BLOCK_SIZE * (get_dirblock(file, dirblock, DRBLK_SKIP + ientry * DRBLK_WIDTH + DRBLK_PTR) - 1); } } while (nextblock > FIRST_DIRBLOCK); return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dirblock @INPUT : file - ecat file pointer dirblock - directory block offset - offset (in longwords) into the block @OUTPUT : (nothing) @RETURNS : directory block value @DESCRIPTION: Routine to get a value from an ECAT directory block. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ static long get_dirblock(Ecat_file *file, int32_t *dirblock, int offset){ long value; if (file->header_description == ECAT_VER_PRE7) { /*get_vax_long(1, &dirblock[offset], &value);*/ } else if (file->header_description == ECAT_VER_7) { /* value = dirblock[offset];*/ get_int32_value(&dirblock[offset], &value); } else { return 0; } return value; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ecat_get_image @INPUT : file - ecat file pointer volume - frame or bed position (from 0) slice - slice number (counting from 0) @OUTPUT : image @RETURNS : FALSE if successful, TRUE otherwise @DESCRIPTION: Routine to get an image from an ECAT file @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int ecat_get_image(Ecat_file *file, int volume, int slice, short *image) { long file_offset; int xsize, ysize, zsize, data_type, bytes_per_pixel; unsigned long image_npix, image_size, array_offset, ipix; unsigned char *bimage; /* Get the image size and type */ if (ecat_get_subhdr_value(file, volume, slice, ECAT_X_Dimension, 0, &xsize, NULL, NULL) || ecat_get_subhdr_value(file, volume, slice, ECAT_Y_Dimension, 0, &ysize, NULL, NULL) || ecat_get_subhdr_value(file, volume, slice, ECAT_Data_Type, 0, &data_type, NULL, NULL) || (xsize <= 0) || (ysize <= 0)) { return TRUE; } /* Figure out the image size */ switch (data_type) { case 1: bytes_per_pixel = 1; break; case 2: case 6: bytes_per_pixel = 2; break; default: return TRUE; } image_npix = xsize * ysize; image_size = image_npix * bytes_per_pixel; /* Look for a z size */ zsize = 0; (void) ecat_get_subhdr_value(file, volume, slice, ECAT_Z_Dimension, 0, &zsize, NULL, NULL); /* Check the that the slice is in range */ if ((slice < 0) || ((zsize > 0) && (slice > zsize))) { return TRUE; } /* Find the appropriate subheader */ if (ecat_get_subhdr_offset(file, volume, slice, &file_offset)) { return TRUE; } /* Adjust the offset appropriately */ file_offset += BLOCK_SIZE; if (zsize > 0) { file_offset += image_size * slice; } /* Calculate image size and offsets */ array_offset = image_npix * (sizeof(short) - bytes_per_pixel); bimage = (unsigned char *) image; /* Read in the image */ if (fseek(file->file_pointer, file_offset, SEEK_SET) || (fread(&bimage[array_offset], (size_t) bytes_per_pixel, (size_t) image_npix, file->file_pointer) != image_npix)) { return TRUE; } /* Transform the image to the right type */ switch (bytes_per_pixel) { case 1: for (ipix=0; ipixheader_description == ECAT_VER_PRE7) { /*get_vax_short(image_npix, image, image);*/ } else { for (ipix=0; ipix= file->num_volumes) || (slice < 0) || (slice >= file->num_planes)) { return TRUE; } /* Calculate subheader number */ if (file->header_description == ECAT_VER_7) { subheader_number = volume; } else if (file->header_description == ECAT_VER_PRE7) { subheader_number = volume * file->num_planes + slice; } else { return TRUE; } /* Check the subheader number */ if ((subheader_number < 0) || (subheader_number >= file->num_subhdrs)) { return TRUE; } /* Get the offset for this subheader */ *offset = file->subhdr_offsets[subheader_number]; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : malloc_check @INPUT : size - number of bytes to allocate @OUTPUT : (nothing) @RETURNS : pointer to memory @DESCRIPTION: Routine to allocate memory. It will never return NULL - the program will exit first. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *malloc_check(size_t size) { void *ptr; ptr = malloc(size); if (ptr == NULL) { (void) fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } return ptr; } /* ----------------------------- MNI Header ----------------------------------- @NAME : realloc_check @INPUT : ptr - old pointer size - number of bytes to allocate @OUTPUT : (nothing) @RETURNS : pointer to memory @DESCRIPTION: Routine to re-allocate memory. It will never return NULL - the program will exit first. @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void *realloc_check(void *ptr, size_t size) { ptr = realloc(ptr, size); if (ptr == NULL) { (void) fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } return ptr; } minc-tools-2.3.00+dfsg/conversion/ecattominc/ecattominc.c0000644000175000000620000017243712574624760022445 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ecattominc @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts a CTI ECAT file to a minc format file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 3, 1996 (Peter Neelin) @MODIFIED : * $Log: ecattominc.c,v $ * Revision 6.7 2008-04-11 05:16:02 rotor * * added config.h to ecattominc * * removed minc_globdef.h from Makefile.am * * Revision 6.6 2008/01/17 02:33:01 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 2005/01/19 19:46:01 bert * Changes from Anthonin Reilhac * * * Revision 7.0 2004/08/13 Anthonin Reilhac * Portage of the code under linux environment * - little / big Indian conversion * - the vax conversion routines are now included within the distribution * - MALLOC, REALLOC and FREE macros definined within the ecat_file.h body * MALLOC and REALLOC use the malloc_check and realloc_check in ecat_file.c * which are not static(static) anymore * mni_def.h is not used anymore and * Fixed x and y flipping when y size is odd. * Did not show up before since normal x and y size are even * * Revision 6.3 2000/09/08 18:17:15 neelin * Fixed swapping of x and y sizes when getting dimensions sizes from ecat file. * This has not previously shown up since normal ECAT images are square. * * Revision 6.2 1999/11/09 13:44:56 neelin * Year 2000 fixes for scan date in minc file. * * Revision 6.1 1999/10/29 17:52:01 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:22 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:21 neelin * Release of minc version 0.5 * * Revision 4.1 1997/05/16 18:21:37 neelin * Changed calculation of z filter width to use BinSize rather than * PlaneSeparation. * * Revision 4.0 1997/05/07 20:06:04 neelin * Release of minc version 0.4 * * Revision 1.2 1996/03/26 15:58:18 neelin * Various changes including changed coordinates in x and y and computing * FWHM from cutoff frequency. * * Revision 1.1 1996/01/18 14:52:14 neelin * Initial revision * @COPYRIGHT : Copyright 1996 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. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include /*#include modif anthonin */ #include "ecat_file.h" /* Type declarations */ typedef struct { char *name; char *values; } ecat_header_data_type; typedef struct { int nslices; int low_slice; int high_slice; double scan_time; double time_width; double half_life; double zstart; double zstep; double decay_correction; char isotope[16]; int image_xsize; int image_ysize; int ordered_frame; int *ordered_slices; char image_type[16]; } frame_info_type; typedef struct { int low_frame; int high_frame; int num_slices; int max_nslices; int max_xsize; int max_ysize; double xstart; double ystart; double xstep; double ystep; double xwidth; double ywidth; double zwidth; int decay_corrected; char img_units[16]; char patient_name[40]; char patient_sex[8]; long patient_age; char patient_birthdate[40]; char study_id[40]; char start_time[40]; long start_year; long start_month; long start_day; long start_hour; long start_minute; double start_seconds; char tracer[40]; char injection_time[40]; long injection_hour; long injection_minute; double injection_seconds; double injection_dose; int septa_retracted; ecat_header_data_type *main_field_list; int num_main_fields; ecat_header_data_type *subhdr_field_list; int num_subhdr_fields; } general_info_type; typedef struct { double sort_key; void *sort_value; } sort_type; /* Function declarations */ void usage_error(char *progname); int get_frame_info(Ecat_file *ecat_fp, int slice_range[2], int num_frames, frame_info_type *frame_info, general_info_type *general_info); void sort_slices(int sort_over_time, int num_frames, frame_info_type *frame_info, general_info_type *general_info); int sortcmp(const void *val1, const void *val2); int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_frames, frame_info_type *frame_info, general_info_type *general_info, char *blood_file); int get_slice(Ecat_file *ecat_fp, int frame_num, int slice_num, long *pixel_max, double *image_max, short *image, frame_info_type *frame_info, general_info_type *general_info); int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims,long start[], long count[], short *image, int image_xsize, int image_ysize, long pixel_max, double image_max, double scan_time, double time_width, double zpos); double decay_correction(double scan_time, double measure_time, double start_time, double half_life); void CreateBloodStructures (int mincHandle, int bloodHandle); void FillBloodStructures (int mincHandle, int bloodHandle); /* Constants */ #define TRUE 1 #define FALSE 0 #define MAX_DIMS 4 #define ECAT_ACTIVITY "ACTIVITY" #define ECAT_CALIB_UNITS_UNKNOWN 0 #define ECAT_CALIB_UNITS_BECQUEREL 1 #define ECAT_CALIB_UNITS_CPS 3 #define MM_PER_CM 10.0 #define BECQUEREL_PER_NCURIE 37 #define BECQUEREL_PER_MCURIE (BECQUEREL_PER_NCURIE * 1e6) #define NCURIE_PER_CC_STRING "nCi/cc" #define SECONDS_PER_HOUR 3600 #define DEFAULT_RANGE INT_MIN #define MINIMUM_HALFLIFE 0.1 #define FWHM_SCALE_FOR_HANN 1.082 /* we don't need mni_def anymore*/ /*#define MALLOC(size) ((void *) malloc(size)) #define FREE(ptr) free( (void *) ptr) #define REALLOC(ptr, size) ((void *) realloc(ptr, size))*/ /* Main program */ int main(int argc, char *argv[]) { /* Variables for arguments */ static int write_byte_data = TRUE; static int clobber = FALSE; static int verbose = TRUE; static int decay_correct = TRUE; static int slice_range[2] = {DEFAULT_RANGE, DEFAULT_RANGE}; static int copy_all_header = TRUE; static char *blood_file = NULL; static int frame_range[2] = {DEFAULT_RANGE, DEFAULT_RANGE}; /* Argument option table */ static ArgvInfo argTable[] = { {"-byte", ARGV_CONSTANT, (char *) TRUE, (char *) &write_byte_data, "Write out data as bytes (default)."}, {"-short", ARGV_CONSTANT, (char *) FALSE, (char *) &write_byte_data, "Write out data as short integers."}, {"-decay_correct", ARGV_CONSTANT, (char *) TRUE, (char *) &decay_correct, "Do decay correction on images (default)."}, {"-nodecay_correct", ARGV_CONSTANT, (char *) FALSE, (char *) &decay_correct, "Don't do decay correction."}, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "List files as they are converted (default)"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not list files as they are converted."}, {"-slices", ARGV_INT, (char *) 2, (char *) slice_range, "Range of slices to copy (counting from 0)."}, {"-frames", ARGV_INT, (char *) 2, (char *) frame_range, "Range of frames to copy (counting from 0)."}, {"-frame", ARGV_INT, (char *) 1, (char *) frame_range, "Single frame to copy (counting from 0)."}, {"-small_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Copy only basic header information."}, {"-all_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all header information (default)."}, {"-bloodfile", ARGV_STRING, (char *) 1, (char *) &blood_file, "Insert blood data from this file."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Other variables */ char *pname; /*name of the present command ->argv[0]*/ char *mincfile; /*name of the minc file = output*/ char *ecat_filename; /*name of the ecat7 file = input*/ int num_frames; int num_bed_positions; int sort_over_time; frame_info_type *frame_info; general_info_type *general_info; long count[MAX_DIMS], start[MAX_DIMS]; int ndims; int mincid, icvid, varid; int islice, iframe, i, slice_num, ifield, frame_num, low_frame, high_frame; long pixel_max; double image_max; double scale; short *image; Ecat_file *ecat_fp; int status; char *tm_stamp; /******** pk for minchistory*/ double first_z, last_z, zstep; /* Get time stamp */ tm_stamp = time_stamp(argc, argv); /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc != 3)) { usage_error(pname); } /* Check the slice range */ if (slice_range[0] == DEFAULT_RANGE) { slice_range[0] = 0; slice_range[1] = INT_MAX; } else if (slice_range[1] == DEFAULT_RANGE) { slice_range[1] = slice_range[0]; } if ((slice_range[0] < 0) || (slice_range[1] < 0) || (slice_range[1] < slice_range[0])) { (void) fprintf(stderr, "%s: Error in slice range: %d to %d.\n", pname, slice_range[0], slice_range[1]); exit(EXIT_FAILURE); } /* Check the frame range */ if (frame_range[0] == DEFAULT_RANGE) { frame_range[0] = 0; frame_range[1] = INT_MAX; } else if (frame_range[1] == DEFAULT_RANGE) { frame_range[1] = frame_range[0]; } if ((frame_range[0] < 0) || (frame_range[1] < 0) || (frame_range[1] < frame_range[0])) { (void) fprintf(stderr, "%s: Error in frame range: %d to %d.\n", pname, frame_range[0], frame_range[1]); exit(EXIT_FAILURE); } /* Get file names */ ecat_filename = argv[1]; mincfile = argv[2]; /* Open the ECAT file */ if ((ecat_fp=ecat_open(ecat_filename))==NULL) { (void) fprintf(stderr, "%s: Error opening file %s.\n", pname, ecat_filename); exit(EXIT_FAILURE); } /* Get number of frames and bed positions to see if we are varying over time or interleaving slices */ num_frames = ecat_get_num_frames(ecat_fp); num_bed_positions = ecat_get_num_bed_positions(ecat_fp); sort_over_time = TRUE; if ((num_frames > 1) && (num_bed_positions > 1)) { (void) fprintf(stderr, "%s: Cannot handle multiple frames and bed positions.\n", pname); exit(EXIT_FAILURE); } if (num_bed_positions > 1) { num_frames = num_bed_positions; sort_over_time = FALSE; } /* Print log message */ if (verbose) { (void) fprintf(stderr, "Reading headers.\n"); } /* Get frame range */ low_frame = 0; high_frame = num_frames - 1; if (frame_range[0] > low_frame) low_frame = frame_range[0]; if (frame_range[1] < high_frame) high_frame = frame_range[1]; if (low_frame > high_frame) low_frame = high_frame; num_frames = high_frame - low_frame + 1; /* Read the files to get basic information */ general_info=MALLOC(sizeof(*general_info)); frame_info = MALLOC(num_frames * sizeof(*frame_info)); general_info->low_frame = low_frame; general_info->high_frame = high_frame; status=get_frame_info(ecat_fp, slice_range, num_frames, frame_info, general_info); if (status >= 0) { (void) fprintf(stderr, "%s: Error reading ECAT file %s on frame %d.\n", pname, ecat_filename, status); exit(EXIT_FAILURE); } /* Allocate space for ordered slice list if sorting over z position */ if (!sort_over_time) { for (iframe=0; iframe1)) { ndims = 4; count[0]=num_frames; count[1]=general_info->max_nslices; } else { ndims=3; count[0]=general_info->num_slices; } count[ndims-1] = general_info->max_xsize; count[ndims-2] = general_info->max_ysize; mincid = micreate(mincfile, (clobber ? NC_CLOBBER : NC_NOCLOBBER)); (void) miattputstr(mincid, NC_GLOBAL, MIhistory, tm_stamp); icvid=setup_minc_file(mincid, write_byte_data, copy_all_header, ndims, count, num_frames, frame_info, general_info, blood_file); if (icvid==MI_ERROR) { (void) fprintf(stderr, "%s: Error setting up minc file %s.\n", pname, mincfile); exit(EXIT_FAILURE); } /* Initialize minc start and count vectors */ for (i=0; imax_xsize * general_info->max_ysize * sizeof(short)); /* Print log message */ if (verbose) { (void) fprintf(stderr, "Copying frames:"); (void) fflush(stderr); } /* Loop through files */ for (iframe=0; iframedecay_corrected && (strcmp(frame_info[iframe].image_type, ECAT_ACTIVITY) == 0)) { scale = decay_correction(frame_info[iframe].scan_time, frame_info[iframe].time_width, 0.0, frame_info[iframe].half_life); } else if (!decay_correct && general_info->decay_corrected) { scale = 1.0 / frame_info[iframe].decay_correction; } else { scale = 1.0; } /* Loop through slices */ for (islice = 0; islice < frame_info[iframe].nslices; islice++) { /* Set the minc coordinates */ if (sort_over_time && (num_frames>1)) { start[0]=frame_info[iframe].ordered_frame; start[1]=islice; } else if (sort_over_time) start[0]=islice; else start[0]=frame_info[iframe].ordered_slices[islice]; count[ndims-1] = frame_info[iframe].image_xsize; count[ndims-2] = frame_info[iframe].image_ysize; /* Copy the slice */ slice_num = islice + frame_info[iframe].low_slice; frame_num = iframe + general_info->low_frame; if (get_slice(ecat_fp, frame_num, slice_num, &pixel_max, &image_max, image, &frame_info[iframe], general_info) || write_minc_slice(scale, write_byte_data, mincid, icvid, ndims, start, count, image, frame_info[iframe].image_xsize, frame_info[iframe].image_ysize, pixel_max, image_max, frame_info[iframe].scan_time, frame_info[iframe].time_width, frame_info[iframe].zstep * (double) slice_num + frame_info[iframe].zstart)) { (void) fprintf(stderr, "%s: Error copying slice %d from frame %d.\n", pname, slice_num, frame_num); exit(EXIT_FAILURE); } } /* End slice loop */ } /* End frame loop */ /* Write out average z step and start for irregularly spaced slices */ if ((ndims!=MAX_DIMS) && (num_frames>1)) { start[0] = 0; varid = ncvarid(mincid, MIzspace); (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &first_z); start[0] = general_info->num_slices - 1; (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &last_z); if (start[0] > 0) zstep = (last_z - first_z) / ((double) start[0]); else zstep = 1.0; (void) miattputdbl(mincid, varid, MIstep, zstep); (void) miattputdbl(mincid, varid, MIstart, first_z); } /* Close minc file */ (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); (void) miclose(mincid); /* Write out log message */ if (verbose) { (void) fprintf(stderr, "Done\n"); (void) fflush(stderr); } FREE(image); if (!sort_over_time) { for (iframe=0; iframenum_main_fields; ifield++) { FREE(general_info->main_field_list[ifield].name); FREE(general_info->main_field_list[ifield].values); } for (ifield=0; ifield < general_info->num_subhdr_fields; ifield++) { FREE(general_info->subhdr_field_list[ifield].name); FREE(general_info->subhdr_field_list[ifield].values); } FREE(general_info->main_field_list); FREE(general_info->subhdr_field_list); FREE(general_info); exit(EXIT_SUCCESS); } /* ----------------------------- MNI Header ----------------------------------- @NAME : usage_error @INPUT : progname - program name @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Prints a usage error message and exits. @METHOD : @GLOBALS : @CALLS : @CREATED : January 3, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void usage_error(char *progname) { (void) fprintf(stderr, "\nUsage: %s [] \n", progname); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(EXIT_FAILURE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_frame_info @INPUT : ecat_fp - file pointer for ecat file slice_range - 2-component array giving range of slices num_frames - number of frames @OUTPUT : frame_info - array of structures containing information about each frame. general_info - general information about the file. @RETURNS : (-1) if no error occurs, otherwise, the index of the first frame that could not be read. @DESCRIPTION: Reads information for frame in the ECAT file @METHOD : @GLOBALS : @CALLS : @CREATED : January 4, 1996 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_frame_info(Ecat_file *ecat_fp, int slice_range[2], int num_frames, frame_info_type *frame_info, general_info_type *general_info) { static char *the_months[]= {NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int *num_slices, *max_nslices, *max_xsize, *max_ysize; int start_day, start_month, start_year; int start_hour, start_minute, start_seconds; frame_info_type *fip; int lvalue; double fvalue; char svalue[ECAT_MAX_STRING_LENGTH]; int iframe, ifield, imult, num_fields, iheader, curframe; int length, newlength, multiplicity; struct tm *tm_ptr; ecat_header_data_type *field_list; Ecat_field_name field; char *description; time_t the_time; char *ptr; int isotope_name_okay; int septa_state; double cutoff, binsize; /* Initialize number of slices */ num_slices = &(general_info->num_slices); max_nslices = &(general_info->max_nslices); max_xsize = &(general_info->max_xsize); max_ysize = &(general_info->max_ysize); *num_slices = 0; *max_nslices = 0; *max_xsize = *max_ysize = 0; general_info->decay_corrected = FALSE; /* Loop through files, reading information */ for (iframe=0; iframelow_frame; /* Get number of slices */ fip->low_slice = 0; fip->high_slice = ecat_get_num_planes(ecat_fp) - 1; if (slice_range[0] > fip->low_slice) fip->low_slice = slice_range[0]; if (slice_range[1] < fip->high_slice) fip->high_slice = slice_range[1]; if (fip->low_slice > fip->high_slice) fip->low_slice = fip->high_slice; fip->nslices = fip->high_slice - fip->low_slice + 1; *num_slices += fip->nslices; if (fip->nslices > *max_nslices) *max_nslices = fip->nslices; /* Get image width */ if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_X_Dimension, 0, &lvalue, NULL, NULL)) return curframe; fip->image_xsize = lvalue; if (lvalue > *max_xsize) *max_xsize = lvalue; if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Y_Dimension, 0, &lvalue, NULL, NULL)) return curframe; fip->image_ysize = lvalue; if (lvalue > *max_ysize) *max_ysize = lvalue; /* Get frame start time (in seconds) */ if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Frame_Start_Time, 0, &lvalue, NULL, NULL)) return curframe; fip->scan_time = (double) lvalue / 1000.0; /* Get length of frame (in seconds) */ if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Frame_Duration, 0, &lvalue, NULL, NULL)) return curframe; fip->time_width = (double) lvalue / 1000.0; /* Get scan type */ if (ecat_get_main_value(ecat_fp, ECAT_Calibration_Units, 0, &lvalue, NULL, NULL)) return curframe; if ((lvalue == ECAT_CALIB_UNITS_BECQUEREL) || (lvalue == ECAT_CALIB_UNITS_UNKNOWN) || (lvalue == ECAT_CALIB_UNITS_CPS)) { (void) strcpy(fip->image_type, ECAT_ACTIVITY); } else { (void) strcpy(fip->image_type, ""); } /* Get isotope and half-life */ if (ecat_get_main_value(ecat_fp, ECAT_Isotope_Name, 0, NULL, NULL, fip->isotope)) return curframe; if (ecat_get_main_value(ecat_fp, ECAT_Isotope_Halflife, 0, NULL, &fip->half_life, NULL)) return curframe; /* Check that they are reasonable */ isotope_name_okay = TRUE; for (ptr=fip->isotope; (*ptr != '\0') && (ptr < &fip->isotope[sizeof(fip->isotope)]); ptr++) { if (!isprint((int) *ptr)) { isotope_name_okay = FALSE; } } if (ptr == fip->isotope) isotope_name_okay = FALSE; if (!isotope_name_okay || (fip->half_life < MINIMUM_HALFLIFE)) { (void) fprintf(stderr, "Ignoring bad isotope name or half-life.\n"); fip->isotope[0] = '\0'; fip->half_life = 0.0; } /* Get z start and step (correct start for non-zero first slice */ if (ecat_get_main_value(ecat_fp, ECAT_Plane_Separation, 0, NULL, &fip->zstep, NULL)) return curframe; fip->zstep *= -MM_PER_CM; if (ecat_get_num_bed_positions(ecat_fp) <= 1) { if (ecat_get_main_value(ecat_fp, ECAT_Init_Bed_Position, 0, NULL, &fip->zstart, NULL)) return curframe; } else { if (ecat_get_main_value(ecat_fp, ECAT_Bed_Position, curframe, NULL, &fip->zstart, NULL)) return curframe; } fip->zstart *= -MM_PER_CM; fip->zstart += fip->low_slice * fip->zstep; /* Check to see if file has been decay corrected */ fvalue = 1.0; (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Decay_Corr_Fctr, 0, NULL, &fvalue, NULL); if (fvalue <= 0.0) fvalue = 1.0; fip->decay_correction = fvalue; if (fip->decay_correction != 1.0) { general_info->decay_corrected = TRUE; } /* Get general information from first frame */ if (iframe==0) { /* Get pixel sizes */ if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_X_Pixel_Size, 0, NULL, &general_info->xstep, NULL)) return curframe; if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Y_Pixel_Size, 0, NULL, &general_info->ystep, NULL)) return curframe; general_info->xstep *= MM_PER_CM; general_info->ystep *= MM_PER_CM; /* Get location of first voxel */ if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_X_Offset, 0, NULL, &general_info->xstart, NULL)) return curframe; if (ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Y_Offset, 0, NULL, &general_info->ystart, NULL)) return curframe; general_info->xstart *= -MM_PER_CM; general_info->ystart *= MM_PER_CM; general_info->xstart -= general_info->xstep * ((double) fip->image_xsize - 1.0) / 2.0; general_info->ystart -= general_info->ystep * ((double) fip->image_ysize - 1.0) / 2.0; /* Get resolution in each direction (or zero if not found) */ general_info->xwidth = general_info->ywidth = general_info->zwidth = -1.0; (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_X_Resolution, 0, NULL, &general_info->xwidth, NULL); (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Y_Resolution, 0, NULL, &general_info->ywidth, NULL); (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Z_Resolution, 0, NULL, &general_info->zwidth, NULL); general_info->xwidth *= MM_PER_CM; general_info->ywidth *= MM_PER_CM; general_info->zwidth *= MM_PER_CM; /* If resolution is not found, then use cutoff frequency and assume FWHM for Hann filter */ cutoff = -1.0; binsize = -1.0; (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Rfilter_Cutoff, 0, NULL, &cutoff, NULL); if (cutoff <= 0) { (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Filter_Cutoff_Frequency, 0, NULL, &cutoff, NULL); } (void) ecat_get_main_value(ecat_fp, ECAT_Bin_Size, 0, NULL, &binsize, NULL); binsize *= MM_PER_CM; if ((general_info->xwidth <= 0.0) && (cutoff > 0.0) && (binsize > 0.0)) { general_info->xwidth = FWHM_SCALE_FOR_HANN * binsize / cutoff; } if ((general_info->ywidth <= 0.0) && (cutoff > 0.0) && (binsize > 0.0)) { general_info->ywidth = FWHM_SCALE_FOR_HANN * binsize / cutoff; } if ((general_info->zwidth <= 0.0) && !ecat_get_subhdr_value(ecat_fp, curframe, 0, ECAT_Zfilter_Cutoff, 0, NULL, &cutoff, NULL)) { general_info->zwidth = FWHM_SCALE_FOR_HANN * binsize / cutoff; } /* Get image range and units */ if (ecat_get_main_value(ecat_fp, ECAT_Calibration_Units, 0, &lvalue, NULL, NULL)) return curframe; if (lvalue == ECAT_CALIB_UNITS_BECQUEREL) { (void) strcpy(general_info->img_units, NCURIE_PER_CC_STRING); } else { (void) strcpy(general_info->img_units, ""); } /* Get patient information */ if (ecat_get_main_value(ecat_fp, ECAT_Patient_Name, 0, NULL, NULL, general_info->patient_name)) return curframe; if (ecat_get_main_value(ecat_fp, ECAT_Patient_Sex, 0, NULL, NULL, general_info->patient_sex)) return curframe; switch (general_info->patient_sex[0]) { case 1: case 'M': (void) strcpy(general_info->patient_sex, MI_MALE); break; case 2: case 'F': (void) strcpy(general_info->patient_sex, MI_FEMALE); break; default: (void) strcpy(general_info->patient_sex, MI_OTHER); break; } if (ecat_get_main_value(ecat_fp, ECAT_Patient_Age, 0, &lvalue, NULL, NULL)) general_info->patient_age = -1; else general_info->patient_age = lvalue; if (!ecat_get_main_value(ecat_fp, ECAT_Patient_Birth_Date, 0, &lvalue, NULL, NULL)) { /* Try to get the right birthday by adding half a day, using UTC and rounding down. This works because field stores birthday at 0:00 converted using the local scanner timezone. */ the_time = (time_t) (lvalue + 12 * SECONDS_PER_HOUR); tm_ptr = gmtime(&the_time); (void) sprintf(general_info->patient_birthdate, "%d-%s-%d", tm_ptr->tm_mday, the_months[tm_ptr->tm_mon+1], tm_ptr->tm_year+1900); } else { general_info->patient_birthdate[0] = '\0'; } /* Get study information */ if (ecat_get_main_value(ecat_fp, ECAT_Study_Type, 0, NULL, NULL, general_info->study_id)) return curframe; if (!ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Time, 0, &lvalue, NULL, NULL)) { the_time = (time_t) lvalue; tm_ptr = localtime(&the_time); general_info->start_day = tm_ptr->tm_mday; general_info->start_month = tm_ptr->tm_mon + 1; general_info->start_year = tm_ptr->tm_year + 1900; general_info->start_hour = tm_ptr->tm_hour; general_info->start_minute = tm_ptr->tm_min; general_info->start_seconds = tm_ptr->tm_sec; } else { if (ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Day, 0, &start_day, NULL, NULL) || ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Month, 0, &start_month, NULL, NULL) || ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Year, 0, &start_year, NULL, NULL) || ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Hour, 0, &start_hour, NULL, NULL) || ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Minute, 0, &start_minute, NULL, NULL) || ecat_get_main_value(ecat_fp, ECAT_Scan_Start_Second, 0, &start_seconds, NULL, NULL)) { return curframe; } start_year += 1900; if (start_year < 1950) start_year += 100; general_info->start_day = start_day; general_info->start_month = start_month; general_info->start_year = start_year; general_info->start_hour = start_hour; general_info->start_minute = start_minute; general_info->start_seconds = start_seconds; } (void) sprintf(general_info->start_time, "%d-%s-%d %d:%d:%d", (int) general_info->start_day, the_months[general_info->start_month], (int) general_info->start_year, (int) general_info->start_hour, (int) general_info->start_minute, (int) general_info->start_seconds); if ((int) strlen(fip->isotope) > 0) { if (ecat_get_main_value(ecat_fp, ECAT_Radiopharmaceutical, 0, NULL, NULL, general_info->tracer)) return curframe; } else { general_info->tracer[0] = '\0'; } if (!ecat_get_main_value(ecat_fp, ECAT_Dose_Start_Time, 0, &lvalue, NULL, NULL) && (lvalue != 0)) { the_time = (time_t) lvalue; (void) strcpy(general_info->injection_time, ctime(&the_time)); tm_ptr = localtime(&the_time); general_info->injection_hour = tm_ptr->tm_hour; general_info->injection_minute = tm_ptr->tm_min; general_info->injection_seconds = tm_ptr->tm_sec; } else { general_info->injection_time[0] = '\0'; } if (!ecat_get_main_value(ecat_fp, ECAT_Dosage, 0, NULL, &general_info->injection_dose, NULL)) { general_info->injection_dose /= BECQUEREL_PER_MCURIE; } else { general_info->injection_dose = -1.0; } /* Get septa state */ septa_state = 0; if (!ecat_get_main_value(ecat_fp, ECAT_Septa_State, 0, &septa_state, NULL, NULL)) { general_info->septa_retracted = (septa_state == 1); } /* Get list of header values */ for (iheader=0; iheader < 2; iheader++) { /* Get space for first field */ field_list = MALLOC(sizeof(*field_list)); /* Loop through fields */ ifield = 0; num_fields = 0; do { /* Get next field */ if (iheader == 0) { field = ecat_list_main(ecat_fp, ifield); description = ecat_get_main_field_description(ecat_fp, field); multiplicity = ecat_get_main_field_length(ecat_fp, field); } else { field = ecat_list_subhdr(ecat_fp, ifield); description = ecat_get_subhdr_field_description(ecat_fp, field); multiplicity = ecat_get_subhdr_field_length(ecat_fp, field); } ifield++; /* Check for end of list */ if ((field == ECAT_No_Field) || (description == NULL) || (multiplicity <= 0)) continue; /* Save the description */ field_list[num_fields].name = strdup(description); /* Get space for the values */ field_list[num_fields].values = NULL; length = 0; /* Loop through multiplicity */ for (imult=0; imult < multiplicity; imult++) { /* Get value */ svalue[0] = '\0'; if (iheader == 0) { (void) ecat_get_main_value(ecat_fp, field, imult, NULL, NULL, svalue); } else { (void) ecat_get_subhdr_value(ecat_fp, curframe, 0, field, imult, NULL, NULL, svalue); } /* Save it */ if (field_list[num_fields].values == NULL) { field_list[num_fields].values = strdup(svalue); length = strlen(svalue); } else { newlength = length + strlen(svalue) + 1; field_list[num_fields].values = REALLOC(field_list[num_fields].values, (size_t) newlength + 1); field_list[num_fields].values[length] = '\n'; (void) strcpy(&field_list[num_fields].values[length+1], svalue); length = newlength; } } /* End of loop over multiplicity */ /* Get space for next field */ field_list = REALLOC(field_list, (num_fields+2) * sizeof(field_list[0])); /* Increment counter */ num_fields++; } while (field != ECAT_No_Field); /* Save the list */ if (iheader == 0) { general_info->main_field_list=field_list; general_info->num_main_fields=num_fields; } else { general_info->subhdr_field_list=field_list; general_info->num_subhdr_fields=num_fields; } } /* Loop over headers */ } /* If first file */ } /* Loop over files */ return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_slices @INPUT : sort_over_time - boolean indicating whether sort should be over time or z position. num_frames - number of frames frame_info - array of frame information. general_info - general information about files. @OUTPUT : frame_info - modified to give slice ordering information. @RETURNS : (nothing) @DESCRIPTION: Sorts the slices for the output file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void sort_slices(int sort_over_time, int num_frames, frame_info_type *frame_info, general_info_type *general_info) { int iframe, islice, isort, num_sort, slice_num; /* Variables for sorting */ sort_type *sort_array; struct { int file; int slice; } *slice_ptr; frame_info_type *file_ptr; /* Allocate array for sorting */ num_sort = (sort_over_time ? num_frames : general_info->num_slices); sort_array = MALLOC (num_sort * sizeof(*sort_array)); /* Are we sorting over time or z position */ if (sort_over_time) { /* Go through the files */ for (iframe=0; iframefile = iframe; slice_ptr->slice = islice; sort_array[isort].sort_value = slice_ptr; isort++; } } } /* Sort the slices */ qsort(sort_array, num_sort, sizeof(*sort_array), sortcmp); /* Loop through sorted list */ for (isort=0; isortordered_frame = isort; } else { slice_ptr = sort_array[isort].sort_value; iframe = slice_ptr->file; islice = slice_ptr->slice; frame_info[iframe].ordered_slices[islice] = isort; FREE(slice_ptr); } } /* Free the sorting array */ FREE(sort_array); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sortcmp @INPUT : val1 - first value val2 - second value @OUTPUT : (none) @RETURNS : 0 if values are the same, -1 if val1->sort_key < val2->sort_key and +1 if val1->sort_key > val2->sort_key. @DESCRIPTION: Compares two double precision values. If they are the same, then return 0. If val1 < val2, return -1. If val1 > val2, return +1. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int sortcmp(const void *val1, const void *val2) { if (((const sort_type *)val1)->sort_key < ((const sort_type *)val2)->sort_key) return -1; else if (((const sort_type *)val1)->sort_key > ((const sort_type *)val2)->sort_key) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_file @INPUT : mincid - id of minc file write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). copy_all_header - boolean indicating whether all of the header information should be copied or not. ndims - number of dimensions for minc file count - lengths of dimensions minc file num_frames - number of frames. frame_info - array of information about frames. general_info - general information about file. blood_file - name of blood file containing data to include. @OUTPUT : (nothing) @RETURNS : Image conversion variable id or MI_ERROR if an error occurs. @DESCRIPTION: Initializes the header of the minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_frames, frame_info_type *frame_info, general_info_type *general_info, char *blood_file) { static char *dim_names_array[]={MItime, MIzspace, MIyspace, MIxspace}; char **dim_names; static char *dimwidth_names_array[]={ MItime_width, MIzspace_width, MIyspace_width, MIxspace_width}; char **dimwidth_names; int dim[MAX_DIMS]; int img, imgmax, imgmin, dimvarid, widvarid, icv, varid, ecat_var; int bloodid; int idim, ifield, num_fields, iheader, iframe; double vrange[2]; char varname[MAX_NC_NAME]; ecat_header_data_type *field_list; double dimwidths[MAX_DIMS]; double *frame_times; double *frame_lengths; /* Set up dimension arrays for looping */ dim_names = dim_names_array + MAX_DIMS - ndims; dimwidth_names = dimwidth_names_array + MAX_DIMS - ndims; for (idim=0; idim < ndims; idim++) { switch (idim + MAX_DIMS - ndims) { case 0: dimwidths[idim] = 1.0; break; case 1: dimwidths[idim] = general_info->zwidth; break; case 2: dimwidths[idim] = general_info->ywidth; break; case 3: dimwidths[idim] = general_info->xwidth; break; } } /* Create the dimensions */ for (idim=0; idim1)) ? 1 : 0), &dim[idim]); if (dimwidths[idim] > 0) { widvarid=micreate_std_variable(mincid, dimwidth_names[idim], NC_DOUBLE, ((strcmp(dim_names[idim], MItime)==0) ? 1 : 0), &dim[idim]); } else { widvarid = MI_ERROR; } /* Add attributes to the dimension variables */ if (strcmp(dim_names[idim], MIzspace)==0) { /* Write out step and start. We will rewrite this for irregularly spaced files */ (void) miattputdbl(mincid, dimvarid, MIstep, frame_info[0].zstep); (void) miattputdbl(mincid, dimvarid, MIstart, frame_info[0].zstart); (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); if (widvarid != MI_ERROR) { (void) miattputdbl(mincid, widvarid, MIwidth, (double) general_info->zwidth); (void) miattputstr(mincid, widvarid, MIunits, "mm"); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } } else if (strcmp(dim_names[idim], MIyspace)==0) { (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputdbl(mincid, dimvarid, MIstart, (double) general_info->ystart); (void) miattputdbl(mincid, dimvarid, MIstep, (double) general_info->ystep); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); if (widvarid != MI_ERROR) { (void) miattputdbl(mincid, widvarid, MIwidth, (double) general_info->ywidth); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } } else if (strcmp(dim_names[idim], MIxspace)==0) { (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputdbl(mincid, dimvarid, MIstart, (double) general_info->xstart); (void) miattputdbl(mincid, dimvarid, MIstep, (double) general_info->xstep); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); if (widvarid != MI_ERROR) { (void) miattputdbl(mincid, widvarid, MIwidth, (double) general_info->xwidth); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } } else if (strcmp(dim_names[idim], MItime)==0) { (void) miattputstr(mincid, dimvarid, MIunits, "seconds"); (void) miattputstr(mincid, widvarid, MIunits, "seconds"); } } /* Create the image variable */ if (write_byte_data) { img=micreate_std_variable(mincid, MIimage, NC_BYTE, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_UNSIGNED); vrange[0]=0; vrange[1]=255; } else { img=micreate_std_variable(mincid, MIimage, NC_SHORT, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_SIGNED); vrange[0] = -32000; vrange[1] = 32000; } (void) ncattput(mincid, img, MIvalid_range, NC_DOUBLE, 2, vrange); (void) miattputstr(mincid, img, MIcomplete, MI_FALSE); /* Create the image max and min variables */ imgmax=micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); imgmin=micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); (void) miattputstr(mincid, imgmax, MIunits, general_info->img_units); (void) miattputstr(mincid, imgmin, MIunits, general_info->img_units); /* Create the image conversion variable */ icv=miicv_create(); (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT); /* Save patient info */ varid = micreate_group_variable(mincid, MIpatient); (void) miattputstr(mincid, varid, MIfull_name, general_info->patient_name); (void) miattputstr(mincid, varid, MIsex, general_info->patient_sex); if (general_info->patient_age > 0) (void) ncattput(mincid, varid, MIage, NC_LONG, 1, &general_info->patient_age); if ((int) strlen(general_info->patient_birthdate) > 0) (void) miattputstr(mincid, varid, MIbirthdate, general_info->patient_birthdate); /* Save study info */ varid = micreate_group_variable(mincid, MIstudy); (void) miattputstr(mincid, varid, MImodality, MI_PET); (void) miattputstr(mincid, varid, MImanufacturer, "CTI"); (void) miattputstr(mincid, varid, MIstudy_id, general_info->study_id); (void) miattputstr(mincid, varid, MIstart_time, general_info->start_time); (void) ncattput(mincid, varid, MIstart_year, NC_LONG, 1, &general_info->start_year); (void) ncattput(mincid, varid, MIstart_month, NC_LONG, 1, &general_info->start_month); (void) ncattput(mincid, varid, MIstart_day, NC_LONG, 1, &general_info->start_day); (void) ncattput(mincid, varid, MIstart_hour, NC_LONG, 1, &general_info->start_hour); (void) ncattput(mincid, varid, MIstart_minute, NC_LONG, 1, &general_info->start_minute); (void) ncattput(mincid, varid, MIstart_seconds, NC_DOUBLE, 1, &general_info->start_seconds); /* Save acquisition info */ varid = micreate_group_variable(mincid, MIacquisition); if ((int) strlen(frame_info[0].isotope) > 0) { (void) miattputstr(mincid, varid, MIradionuclide, frame_info[0].isotope); } if (frame_info[0].half_life > 0.0) { (void) miattputdbl(mincid, varid, MIradionuclide_halflife, (double) frame_info[0].half_life); } if ((int) strlen(general_info->tracer) > 0) { (void) miattputstr(mincid, varid, MItracer, general_info->tracer); } if ((int) strlen(general_info->injection_time) > 0) { (void) miattputstr(mincid, varid, MIinjection_time, general_info->injection_time); (void) ncattput(mincid, varid, MIinjection_hour, NC_LONG, 1, &general_info->injection_hour); (void) ncattput(mincid, varid, MIinjection_minute, NC_LONG, 1, &general_info->injection_minute); (void) ncattput(mincid, varid, MIinjection_seconds, NC_DOUBLE, 1, &general_info->injection_seconds); } if (general_info->injection_dose > 0.0) { (void) ncattput(mincid, varid, MIinjection_dose, NC_DOUBLE, 1, &general_info->injection_dose); (void) miattputstr(mincid, varid, MIdose_units, "mCurie"); } /* Save the septa state in a special ECAT variable, along with frame starts and lengths if we are not creating a time dimension. */ varid = ncvardef(mincid, "ecat_acquisition", NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "ECAT-specific acquisition information"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); (void) miattputstr(mincid, varid, "septa_retracted", (general_info->septa_retracted ? MI_TRUE : MI_FALSE)); if (ndims < MAX_DIMS) { frame_times = MALLOC(sizeof(*frame_times) * num_frames); frame_lengths = MALLOC(sizeof(*frame_lengths) * num_frames); for (iframe=0; iframe < num_frames; iframe++) { frame_times[iframe] = frame_info[iframe].scan_time; frame_lengths[iframe] = frame_info[iframe].time_width; } (void) ncattput(mincid, varid, "frame_times", NC_DOUBLE, num_frames, frame_times); (void) ncattput(mincid, varid, "frame_lengths", NC_DOUBLE, num_frames, frame_lengths); } /* If we want all of the values from the header, get them */ if (copy_all_header) { for (iheader=0; iheader < 2; iheader++) { /* Set up values for either header */ if (iheader == 0) { (void) strcpy(varname, "ecat-main"); field_list = general_info->main_field_list; num_fields = general_info->num_main_fields; } else { (void) strcpy(varname, "ecat-subhdr"); field_list = general_info->subhdr_field_list; num_fields = general_info->num_subhdr_fields; } /* Create a variable for ECAT fields */ ecat_var = ncvardef(mincid, varname, NC_LONG, 0, NULL); (void) miattputstr(mincid, ecat_var, MIvartype, MI_GROUP); (void) miattputstr(mincid, ecat_var, MIvarid, "MNI ECAT variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), ecat_var); /* Loop through fields */ for (ifield=0; ifield < num_fields; ifield++){ (void) miattputstr(mincid, ecat_var, field_list[ifield].name, field_list[ifield].values); } } /* Loop over headers */ } /* If copy_all_header */ /* Open the blood file and create the variables if needed */ if (blood_file != NULL) { bloodid = ncopen(blood_file, NC_NOWRITE); CreateBloodStructures(mincid, bloodid); } /* Attach the icv */ (void) ncsetfill(mincid, NC_NOFILL); (void) ncendef(mincid); (void) miicv_attach(icv, mincid, img); /* Copy the blood data */ if (blood_file != NULL) { FillBloodStructures(mincid, bloodid); ncclose(bloodid); } return icv; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_slice @INPUT : ecat_fp - file pointer for file frame_num - number of frame slice_num - slice to copy frame_info - information on frame general_info - general file information @OUTPUT : pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds image - the image @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Gets an image from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_slice(Ecat_file *ecat_fp, int frame_num, int slice_num, long *pixel_max, double *image_max, short *image, frame_info_type *frame_info, general_info_type *general_info) /* ARGSUSED */ { long npix, in, off; int pmax; int lvalue; short temp; double scale, global_scale; /* Get the image from the file */ if (ecat_get_image(ecat_fp, frame_num, slice_num, image)) return TRUE; /* Flip the image to give positive x & y axes */ npix = frame_info->image_xsize * frame_info->image_ysize; for(in = 0; in < npix/2; in++) { off = npix - in - 1; temp = image[off]; image[off] = image[in]; image[in] = temp; } /* Get image and pixel max */ if (ecat_get_subhdr_value(ecat_fp, frame_num, slice_num, ECAT_Image_Max, 0, &pmax, NULL, NULL) || ecat_get_subhdr_value(ecat_fp, frame_num, slice_num, ECAT_Scale_Factor, 0, NULL, &scale, NULL)) return TRUE; if (!ecat_get_main_value(ecat_fp, ECAT_Calibration_Factor, 0, NULL, &global_scale, NULL) && (global_scale > 0.0)) { if (!ecat_get_main_value(ecat_fp, ECAT_Calibration_Units, 0, &lvalue, NULL, NULL) && (lvalue == ECAT_CALIB_UNITS_BECQUEREL)) { global_scale /= BECQUEREL_PER_NCURIE; } } else if (ecat_get_subhdr_value(ecat_fp, frame_num, slice_num, ECAT_Calibration_Factor, 0, NULL, &global_scale, NULL)) { global_scale = 1.0; } *pixel_max = pmax; *image_max = (double) pmax * scale * global_scale; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_minc_slice @INPUT : scale - scale for decay correcting image write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). mincid - id of minc file icvid - id of image conversion variable start - coordinate of slice in minc file count - edge lengths of image to write in minc file image - pointer to image buffer image_xsize, image_ysize - dimensions of image pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds scan_time - time of slice time_width - time width of slice zpos - z position of slice frame_info - information on frame general_info - general file information @OUTPUT : (nothing) @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Writes out the image to the minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims, long start[], long count[], short *image, int image_xsize, int image_ysize, long pixel_max, double image_max, double scan_time, double time_width, double zpos) /*ARGSUSED*/ { double pixmin, pixmax; long ipix, npix; double maximum, minimum; /* Search for pixel max and min */ npix = image_xsize * image_ysize; pixmin = pixmax = image[0]; for (ipix=1; ipixpixmax) pixmax = image[ipix]; if (image[ipix] #include #include #include #include #include #include #ifndef SEEK_SET # define SEEK_SET 0 #endif /* Private functions */ private void scx_get_value(unsigned char *header, long position, scx_mnem_types type, int length, long *lvalue, float *fvalue, char *svalue); /* Constants */ #define TRUE 1 #define FALSE 0 #define HEADER_SIZE 2048 #define FILE_TYPE_POSITION 0 #define FILE_TYPE_TYPE scx_word #define SCX_NCS "NCS" #define SCX_SPOS "SPOS" #define SCX_DPR "DPR" #define SCX_FHS "FHS" #define SCX_REC "REC" #define SCX_DTYP "DTYP" #define SCX_IMFM "IMFM" #define SCX_SPOS "SPOS" #define MAX_PIX_SIZE 2 /* ----------------------------- MNI Header ----------------------------------- @NAME : scx_open @INPUT : filename - name of file to open @OUTPUT : (none) @RETURNS : Pointer to scanditronix file descriptor or NULL if an error occurs. @DESCRIPTION: Routine to open a scanditronix file (for reading only), given its pathname. @METHOD : @GLOBALS : @CALLS : @CREATED : January 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public scx_file *scx_open(char *filename) { scx_file *file; long file_type, nslice, spos; int i; /* Allocate space for a scx file structure */ file = (void *) malloc(sizeof(scx_file)); /* Open the file */ if ((file->file_pointer=fopen(filename, "rb")) == NULL) { free(file); return NULL; } /* Read in the header */ file->header = (void *) malloc(HEADER_SIZE); if (fread(file->header, sizeof(char), (size_t) HEADER_SIZE, file->file_pointer) != HEADER_SIZE) { free(file->header); free(file); return NULL; } /* Get the file type from the header */ scx_get_value(file->header, FILE_TYPE_POSITION, FILE_TYPE_TYPE, 1, &file_type, NULL, NULL); /* Search for the file type */ for (i=0; (scx_file_types[i].file_type!=0) && (file_type!=scx_file_types[i].file_type); i++) {} if (scx_file_types[i].file_type==0) { free(file->header); free(file); return NULL; } file->blocks = scx_file_types[i].block_list; file->mnemonics = scx_file_types[i].mnemonic_list; /* Get the number of mnemonics in the header */ for (i=0; file->mnemonics[i].name!=NULL; i++) {} file->num_mnemonics = i; /* Check for a consistent header by looking at the slice positions */ if (scx_get_mnem(file, SCX_NCS, 0, &nslice, NULL, NULL) || (nslice<=0)) { free(file->header); free(file); return NULL; } for (i=0; inslice)) { free(file->header); free(file); return NULL; } } return file; } /* ----------------------------- MNI Header ----------------------------------- @NAME : scx_close @INPUT : file - scanditronix file descriptor to close @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close a scanditronix file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void scx_close(scx_file *file) { if (file==NULL) return; (void) fclose(file->file_pointer); free(file->header); free(file); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : scx_get_mnem @INPUT : file - scanditronix file descriptor mnem - mnemonic to find multiplicity - index of value to get @OUTPUT : lvalue - long integer value to return fvalue - floating point value to return svalue - string value to return @RETURNS : TRUE if an error occurs, FALSE otherwise @DESCRIPTION: Routine to get a mnemonic's value from a scanditronix file header. @METHOD : @GLOBALS : @CALLS : @CREATED : January 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int scx_get_mnem(scx_file *file, char *mnem, int multiplicity, long *lvalue, float *fvalue, char *svalue) { scx_block_type *block_ptr; scx_mnemonic_type *mnem_ptr; long position; /* Check file pointer */ if (file==NULL) return TRUE; /* Check for negative multiplicity */ if (multiplicity < 0) return TRUE; /* Search for the mnemonic */ for (mnem_ptr=file->mnemonics; (mnem_ptr->name!=NULL) && (strcmp(mnem_ptr->name, mnem)!=0); mnem_ptr++) {} if (mnem_ptr->name==NULL) return TRUE; /* Check for a mnemonic not in the file (a constant). We only handle integer constants */ if (!mnem_ptr->in_file) { switch (mnem_ptr->type) { case scx_byte: case scx_word: case scx_long: if (lvalue!=NULL) *lvalue = mnem_ptr->mdefault; if (fvalue!=NULL) *fvalue = mnem_ptr->mdefault; if (svalue!=NULL) (void) sprintf(svalue, "%d", (int) mnem_ptr->mdefault); break; default: return TRUE; } } /* Mnemonic is in file header */ else { /* Calculate the position of the mnemonic by looping through the list of parent blocks */ position = mnem_ptr->start; for (block_ptr = mnem_ptr->block; (multiplicity>0) && (block_ptr!=NULL); block_ptr = block_ptr->parent) { position += block_ptr->length * (multiplicity % block_ptr->multiplicity); multiplicity /= block_ptr->multiplicity; } if (multiplicity > 0) return TRUE; /* Get the value */ scx_get_value(file->header, position, mnem_ptr->type, mnem_ptr->length, lvalue, fvalue, svalue); } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : scx_get_value @INPUT : header - pointer to header data position - postion of value to read type - type of value to get length - number of values to get @OUTPUT : lvalue - long integer value to return fvalue - floating point value to return svalue - string value to return @RETURNS : (nothing) @DESCRIPTION: Routine to get a mnemonic's value from a scanditronix file header, given the position and type. @METHOD : @GLOBALS : @CALLS : @CREATED : January 8, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private void scx_get_value(unsigned char *header, long position, scx_mnem_types type, int length, long *lvalue, float *fvalue, char *svalue) { void *data; unsigned char *bdata; short *idata; long *ldata; float *fdata; /* Check length */ if (length<=0) return; /* Get the values */ data = (void *) malloc(length * scx_mnem_type_size[type]); bdata = data; idata = data; ldata = data; fdata = data; switch (type) { case scx_byte: case scx_string: case scx_time: case scx_date: (void) memcpy(bdata, &header[position], length); break; case scx_word: case scx_short_float: get_vax_short(length, &header[position], idata); break; case scx_long: get_vax_long(length, &header[position], ldata); break; case scx_float: get_vax_float(length, &header[position], fdata); } /* Convert the values */ switch (type) { case scx_byte: if (lvalue!=NULL) *lvalue = bdata[0]; if (fvalue!=NULL) *fvalue = bdata[0]; if (svalue!=NULL) (void) sprintf(svalue, "%d", (int) bdata[0]); break; case scx_string: if (lvalue!=NULL) *lvalue = 0; if (fvalue!=NULL) *fvalue = 0; if (svalue!=NULL) { (void) memcpy(svalue, bdata, length); svalue[length]='\0'; } break; case scx_time: if (lvalue!=NULL) *lvalue = 0; if (fvalue!=NULL) *fvalue = 0; if (svalue!=NULL) { if (length==3) (void) sprintf(svalue, "%02d:%02d:%02d", (int) bdata[0], (int) bdata[1], (int) bdata[2]); else if (length==4) (void) sprintf(svalue, "%02d:%02d:%02d.%02d", (int) bdata[0], (int) bdata[1], (int) bdata[2], (int) bdata[3]); else svalue[0]='\0'; } break; case scx_date: if (lvalue!=NULL) *lvalue = 0; if (fvalue!=NULL) *fvalue = 0; if (svalue!=NULL) { if (length==3) (void) sprintf(svalue, "%02d-%02d-%02d", (int) bdata[0], (int) bdata[1], (int) bdata[2]); else svalue[0]='\0'; } break; case scx_word: if (lvalue!=NULL) *lvalue = idata[0]; if (fvalue!=NULL) *fvalue = idata[0]; if (svalue!=NULL) (void) sprintf(svalue, "%d", (int) idata[0]); break; case scx_short_float: if (lvalue!=NULL) *lvalue = idata[0]/100.0; if (fvalue!=NULL) *fvalue = idata[0]/100.0; if (svalue!=NULL) (void) sprintf(svalue, "%8g", (double) idata[0]/100.0); break; case scx_long: if (lvalue!=NULL) *lvalue = ldata[0]; if (fvalue!=NULL) *fvalue = ldata[0]; if (svalue!=NULL) (void) sprintf(svalue, "%d", (int) ldata[0]); break; case scx_float: if (lvalue!=NULL) *lvalue = fdata[0]; if (fvalue!=NULL) *fvalue = fdata[0]; if (svalue!=NULL) (void) sprintf(svalue, "%8g", (double) fdata[0]); break; } free(data); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : scx_get_image @INPUT : file - scanditronix file pointer image_num - image to get (first image is zero) @OUTPUT : image - pointer to array into which image should be written. @RETURNS : TRUE if an error occurs, false otherwise. @DESCRIPTION: Routine to get an image from a scanditronix image file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 11, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int scx_get_image(scx_file *file, int image_num, short *image) { long header_size, record_size, data_type; long image_width, image_pos, image_npix, image_size; long file_offset, array_offset, ipix; int pix_size; unsigned char *bimage; /* Check file pointer */ if (file==NULL) return TRUE; /* Parameters for calculating image position */ if (scx_get_mnem(file, SCX_FHS, 0, &header_size, NULL, NULL) || scx_get_mnem(file, SCX_REC, 0, &record_size, NULL, NULL) || scx_get_mnem(file, SCX_DTYP, 0, &data_type, NULL, NULL) || scx_get_mnem(file, SCX_IMFM, 0, &image_width, NULL, NULL) || scx_get_mnem(file, SCX_SPOS, image_num, &image_pos, NULL, NULL) || (header_size<0) || (record_size<0) || (image_width<0) || (image_pos<0)) { return TRUE; } /* Check that image is in file */ if (image_pos<1) return TRUE; /* Check data type */ switch (data_type) { case 0: pix_size=2; break; case 1: pix_size=1; break; case 2: pix_size=2; break; default: return TRUE; } /* Calculate image size and offsets */ image_npix = image_width * image_width; image_size = image_npix * pix_size; file_offset = header_size * record_size + (image_pos-1) * image_size; array_offset = image_npix * (MAX_PIX_SIZE - pix_size); bimage = (unsigned char *) image; /* Read in the image */ if (fseek(file->file_pointer, file_offset, SEEK_SET) || (fread(&bimage[array_offset], (size_t) pix_size, (size_t) image_npix, file->file_pointer) != image_npix)) { return TRUE; } /* Transform the image to the right type */ switch (pix_size) { case 1: for (ipix=0; ipix= file->num_mnemonics)) return NULL; /* Copy the name */ (void) strcpy(mname, file->mnemonics[index].name); /* Get the multiplicity */ *mmult = 1; for (block_ptr = file->mnemonics[index].block; block_ptr!=NULL; block_ptr = block_ptr->parent) { *mmult *= block_ptr->multiplicity; } /* Get the mnemonic type */ switch (file->mnemonics[index].type) { case scx_byte: *mtype = scx_long; break; case scx_string: *mtype = scx_string; break; case scx_time: *mtype = scx_string; break; case scx_date: *mtype = scx_string; break; case scx_word: *mtype = scx_long; break; case scx_short_float: *mtype = scx_float; break; case scx_long: *mtype = scx_long; break; case scx_float: *mtype = scx_float; break; } return mname; } minc-tools-2.3.00+dfsg/conversion/scxtominc/insertblood.c0000644000175000000620000000473212574624760022514 0ustar stevestaff#include #include #include #define MIbloodroot "blood_analysis" /* ----------------------------- MNI Header ----------------------------------- @NAME : CreateBloodStructures @INPUT : mincHandle -> a handle for an open MINC file. This file should be open for writing, but not in redefinition mode. bloodHandle -> a handle for an open BNC file. This file should be open for reading. @OUTPUT : none @RETURNS : void @DESCRIPTION: Copies all variable definitions (with attributes) from the BNC file to the MINC file. The appropriate dimensions are also copied. @METHOD : none. Just muddled through. @GLOBALS : none @CALLS : micopy_all_var_defs (MINC library) miadd_child (MINC library) @CREATED : May 30, 1994 by MW @MODIFIED : ---------------------------------------------------------------------------- */ void CreateBloodStructures (int mincHandle, int bloodHandle) { int mincRoot; int bloodRoot; /* * Copy all the variables with their attributes. */ (void) micopy_all_var_defs (bloodHandle, mincHandle, 0, NULL); /* * Make the blood analysis root variable a child of * the MINC root variable. */ mincRoot = ncvarid (mincHandle, MIrootvariable); bloodRoot = ncvarid (mincHandle, MIbloodroot); (void) miadd_child (mincHandle, mincRoot, bloodRoot); } /* ----------------------------- MNI Header ----------------------------------- @NAME : FillBloodStructures @INPUT : mincHandle -> a handle for an open MINC file. This file should be open for writing, but not in redefinition mode. bloodHandle -> a handle for an open BNC file. This file should be open for reading. @OUTPUT : none @RETURNS : void @DESCRIPTION: Copies all variable values from the BNC file to the MINC file. The variable themselves should already exist in the MINC file (see CreateBloodStructures). @METHOD : none. Just muddled through. @GLOBALS : none @CALLS : micopy_all_var_values (MINC library) @CREATED : May 30, 1994 by MW @MODIFIED : ---------------------------------------------------------------------------- */ void FillBloodStructures (int mincHandle, int bloodHandle) { (void) micopy_all_var_values (bloodHandle, mincHandle, 0, NULL); } minc-tools-2.3.00+dfsg/conversion/scxtominc/scxmnem.c0000644000175000000620000000637312574624760021645 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : scxmnem @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Prints out scanditronix format file mnemonics @METHOD : @GLOBALS : @CALLS : @CREATED : November 3, 1993 (Peter Neelin) @MODIFIED : * $Log: scxmnem.c,v $ * Revision 6.3 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.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.1 1999/10/29 17:52:07 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:31 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:32 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:13 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:05 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:33:49 neelin * Release of minc version 0.2 * * Revision 1.2 94/09/28 10:33:36 neelin * Pre-release * * Revision 1.1 93/11/03 16:51:41 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 #include #include #include int main(int argc, char *argv[]) { char *pname, *filename, *mnemonic; int multiplicity; scx_file *file; long lvalue; float fvalue; char svalue[256]; /* Check arguments */ pname = argv[0]; if (argc == 3) { filename = argv[1]; mnemonic = argv[2]; multiplicity = 0; } else if (argc == 4) { filename = argv[1]; mnemonic = argv[2]; multiplicity = atoi(argv[3]); } else { (void) fprintf(stderr, "Usage: %s []\n", pname); exit(EXIT_FAILURE); } /* Open the file */ if ((file=scx_open(filename)) == NULL) { (void) fprintf(stderr, "%s: Error opening file %s\n", pname, filename); exit(EXIT_FAILURE); } if (scx_get_mnem(file, mnemonic, multiplicity, &lvalue, &fvalue, svalue)) { (void) fprintf(stderr, "%s: Error reading mnemnonic %s(%d)\n", pname, mnemonic, multiplicity); exit(EXIT_FAILURE); } /* Print out the result */ (void) printf("%s(%d): lvalue = %d, fvalue = %g, svalue = %s\n", mnemonic, multiplicity, (int) lvalue, fvalue, svalue); /* Close the file */ scx_close(file); exit(EXIT_SUCCESS); } minc-tools-2.3.00+dfsg/conversion/scxtominc/define_scx_header.perl0000755000175000000620000002275312574624760024335 0ustar stevestaff#! /usr/local/bin/perl # Script to read in Scanditronix .DRS files (defining scx files) # and convert them to a .h file containing mnemonics and offsets. # # Usage: # define_scx_header.perl [ ...] # # 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. # # Constants %types = ( "B", "scx_byte", "W", "scx_word", "L", "scx_long", "F", "scx_float", "FS", "scx_short_float", "S", "scx_string", "B-", "scx_date", "B:", "scx_time" ); %lengths = ( "scx_byte", 1, "scx_word", 2, "scx_long", 4, "scx_float", 4, "scx_short_float", 2, "scx_string", 1, "scx_date", 1, "scx_time", 1 ); @mnem_fields = ("name", "type", "length", "in_file", "mdefault", "start", "block"); @block_fields = ("length", "multiplicity", "parent", "start"); # Check arguments if ($#ARGV<1) { die "Usage : define_scx_header.perl ". "[ ...]\n"; } $header_file = shift; # Open the header file and write initial stuff @drs_list = (); open(hfile, ">$header_file") || die "$0 : Cannot open file $header_file\n"; @types = values(%types); @lengths = (); foreach $types (@types) {push(@lengths, $lengths{$types});} print hfile ' /* Header file for scanditronix file definition */ #ifndef SCX_FILE_HEADER_DEFINITION_H #define SCX_FILE_HEADER_DEFINITION_H /* Mnemonic types */ typedef enum scx_mnem_types_enum {' . join(', ', @types) . '} scx_mnem_types; static int scx_mnem_type_size[] = {' . join(', ', @lengths) . '}; /* Block type */ typedef struct scx_block_struct scx_block_type; struct scx_block_struct { int length; int multiplicity; scx_block_type *parent; }; /* Mnemonic type */ typedef struct scx_mnemonic_struct scx_mnemonic_type; struct scx_mnemonic_struct { char *name; scx_mnem_types type; int length; int in_file; long mdefault; long start; scx_block_type *block; }; '; # Loop through drs files while ($drs_file = shift) { # Initialize the variables needed undef(%mnemonics); undef(%blocks); $nmnems = 0; $nblocks = 0; $blk= -1; @block_list = ($blk); $position = $next_position = 0; undef $file_type; undef $rev; undef $ext; # Open the drs file if (!open(dfile, $drs_file)) { print STDERR "Unable to open drs file $drs_file\n"; next; } # Loop through the lines of the drs file while () { # Look for leading lines if (! (defined($file_type) && defined($rev) && defined($ext))) { if (/^\s*TYPE\S*\s+\D*(\d+)/) { $file_type = $1; if ($file_type == 0) {die "Illegal file type : $file_type\n";} } elsif (/^\s*REV\.\S*\s+\D*(\d+)/) { $rev = $1; } elsif (/^\s*EXT\.\S*\s+(.*)/) { $ext = $1; } } # Look for mnemonic lines elsif (/^\s*[EDM]\s/) { # Parse the line ($code, $mnem, $ftype, $mdefault, $prio, @rest) = split(' '); next if ($prio eq ""); $nmnems++; # Get the mnemonic name $mnemonics{$nmnems-1, "name"} = $mnem; # Is the mnemonic stored in the file $mnemonics{$nmnems-1, "in_file"} = ($code ne "E"); # Get data type and length (if there is a - or : on the end, and # the type is B, then we have a date or time) if ( $ftype !~ /^(\d*)(B|W|L|F|FS|S|B-|B:)$/ ) {next;} if ($1 ne "") {$mnemonics{$nmnems-1, "length"} = $1;} else {$mnemonics{$nmnems-1, "length"} = 1;} $mnemonics{$nmnems-1, "type"} = $types{$2}; # Get pre-defined values if ($mdefault !~ /^=(.*)$/) {next;} $mnemonics{$nmnems-1, "mdefault"} = $1; # Get block $mnemonics{$nmnems-1, "block"} = $blk; # Increment position counters if ($code ne "M") {$position = $next_position;} if ($code ne "E") { $next_position = $position + $mnemonics{$nmnems-1, "length"} * $lengths{$mnemonics{$nmnems-1, "type"}}; } # Get position of first value if ($mnemonics{$nmnems-1, "in_file"}) {$mnemonics{$nmnems-1, "start"} = $position;} else {$mnemonics{$nmnems-1, "start"} = -1;} # Check for file type matching first word of file if (($position == 0) && ($code eq 'D') && ($file_type != $mnemonics{$nmnems-1, "mdefault"})) { die "Type $file_type does not match first word of file (", $mnemonics{$nmnems-1, "mdefault"},")\n"; } } # Look for reserved space lines elsif (/^\s*R\s/) { if (! /^\s*R\s+(\d+)\s+.*$/) {next;} $next_position += $1; } # Look for block definitions elsif (/^\s*([\(\)])-/) { # Look for block open or close $open = ($1 eq "("); if ($open) { # Parse block open line if (!/^\s*\(-+LEN=([^-]+)-+MULT=([^-]+)-+/) {die "Error parsing block open\n";} $length = $1; $mult = $2; if (($mult !~ /^\d+$/) || ($mult < 1)) {die "Error in block multiplicity\n";} if ($length !~ /^\d+$/) {$length = -1;} if (($length < 0) && ($mult>1)) {die "Error in block length\n";} # Add new block $nblocks++; $blk = $nblocks-1; $blocks{$blk, "length"} = $length; $blocks{$blk, "multiplicity"} = $mult; $blocks{$blk, "parent"} = $block_list[$#block_list]; $blocks{$blk, "start"} = $next_position; push(@block_list, $blk); } # Otherwise, we have a close block else { # Check for an open block if (($nblocks<=0) || ($#block_list<=0)) {die "Unbalanced block bounds\n";} # Check the block length $block_length = $next_position - $blocks{$blk, "start"}; if ($blocks{$blk,"length"} >= 0) { if ($blocks{$blk,"length"} != $block_length) { die "Actual block length not equal to given block length\n"; } } else { $blocks{$blk,"length"} = $block_length; } # Move up the pointer (only changes if multiplicity is > 1) $next_position += $blocks{$blk, "length"} * ($blocks{$blk, "multiplicity"} - 1); # Pop the block pop(@block_list); $blk = $block_list[$#block_list]; } } # End of block } # End of read loop # Add drs file to list of files ($drs_id = "pc_".$file_type."_r_".$rev) =~ tr/\W/_/; if (grep(/$drs_id/, @drs_list)) { die "Two files with the same type and revision : $drs_id\n"; } push(@drs_list, $drs_id); $block_array = 'drs_block_'.$drs_id; $mnem_array = 'drs_mnems_'.$drs_id; $drs_file_types{$drs_id} = $file_type; $drs_blocks{$drs_id} = $block_array; $drs_mnemonics{$drs_id} = $mnem_array; # Write out block table for file print hfile ' /* Blocks for drs file with id '.$drs_id.' */ static scx_block_type '.$block_array.'[] = { '; foreach $blk (0..$nblocks-1) { $the_block = $blocks{$blk, "parent"}; if ($the_block < 0) {$the_block = 'NULL';} else {$the_block = '&('.$block_array.'['.$the_block.'])';} print hfile " " . $blocks{$blk, "length"} . ', ' . $blocks{$blk, "multiplicity"} . ', ' . $the_block . ",\n"; } print hfile " 0, 0, NULL\n};\n\n"; # Write out mnemonic table for file print hfile ' /* Mnemonics for drs file with id '.$drs_id.' */ static scx_mnemonic_type '.$mnem_array.'[] = { '; foreach $mnem (0..$nmnems-1) { $the_block = $mnemonics{$mnem, "block"}; if ($the_block < 0) {$the_block = 'NULL';} else {$the_block = '&('.$block_array.'['.$the_block.'])';} # For now, we don't handle anything other than integer defaults $the_default = $mnemonics{$mnem, "mdefault"}; if (($the_default eq "") || ($mnemonics{$mnem, "type"} !~ /^(scx_byte|scx_word|scx_long)$/)) {$the_default = "0";} print hfile ' "' . $mnemonics{$mnem, "name"} . '", ' . $mnemonics{$mnem, "type"} . ', ' . $mnemonics{$mnem, "length"} . ', ' . $mnemonics{$mnem, "in_file"} . ', ' . $the_default . ', ' . $mnemonics{$mnem, "start"} . ', ' . $the_block . ",\n"; } print hfile " NULL, scx_byte, 0, 0, 0, NULL\n};\n\n"; } # End of loop over files # Write out list of file types print hfile ' /* List of drs files and types */ static struct { int file_type; scx_block_type *block_list; scx_mnemonic_type *mnemonic_list; } scx_file_types[] = { '; foreach $drs_id (@drs_list) { print hfile " ${drs_file_types{$drs_id}}, " . "${drs_blocks{$drs_id}}, " . "${drs_mnemonics{$drs_id}},\n"; } print hfile ' 0, NULL, NULL }; #endif /* SCX_FILE_HEADER_DEFINITION_H */ '; minc-tools-2.3.00+dfsg/conversion/scxtominc/pc6r1.drs0000644000175000000620000002135612574624760021472 0ustar stevestaff TYPE: PC-6 REV.: 1 EXT.: .DAT Definition of the file header structure for the PC4096 positron camera data file (with interleaved scan) (---LEN=FHS--MULT=1----------------------------------------------------- E FHS B =8 P0F ; File header size (Records) E REC W =512 P0F ; Record size D TYP W =6 P0F ; Type of equipment D NCS B = P0F ; Number of slices D SITE 4S = P0A ; Installation site D REV W =1 P0F ; Revision number D NDET W = P0F ; Number of detectors per ring D NPRO W = P0F ; Number of projections per slice D NMEM W = P0F ; Number of members in stat. proj. D NWOB B = P0F ; Number of wobble bins per member D CDI F = P0A ; Cross-section distance (mm) D CSZ F = P0A ; Cross-section thickness (p) (mm) D CSX F = P0A ; Cross-section thickness (x) (mm) D RID 10S = P0A ; Run id (user def) D RIN L = P02 ; Identification number D ETN W = P02 ; ET-number (user def) D PNM 24S = P02 ; Patient name D BRN L = P02 ; Birth number D ASS 22S = P02 ; Responsible technician D DOC 22S = P02 ; Responsible doctor D DAT 3B- = P0A ; Measurement date M DATY B = P0A ; Year (B) D DATM B = P0A ; Month (B) D DATD B = P0A ; Day (B) D TIM 4B: = P0A ; Measurement time M TIMH B = P0A ; Hour D TIMM B = P0A ; Minutes D TIMS B = P0A ; Seconds D TIHU B = P0A ; Hundredths of seconds D QES 12S = P02 ; Question D POR 4S = P02 ; Patient orientation D PAN W = P02 ; Patient angle D ZDI F = P0A ; Z displacement (mm) D NRV W = P02 ; Number of revolutions/min D NME W = P02 ; Number of measurements D MTM F = P02 ; Measuring time D RPRT F = P02 ; Time in s. between rates-file reports D ISO 6S = P02 ; Isotope (eg GA-68) D HALF F = P02 ; Halftime of isotope (user supplied) D CAR 8S = P02 ; Carrier (eg EDTA) D ACT F = P02 ; Injected activity D INP 4S = P02 ; Injection place D INT 4S = P02 ; Injection type D ITM 3B: = P02 ; Injection time M ITMH B = P02 ; Hour (B) D ITMM B = P02 ; Minute (B) D ITMS B = P02 ; Second (B) D COL 1S = P0A ; Collimator type (' '=standard) D SAMP B = P0A ; Sampling mode (0=stat, X=bins) D DTYP B = P0A ; Data type (0=word, 1=byte) D FOV B = P02 ; Field-of-view type D PRJL W = P0A ; Projection length in members D TILT F = P0A ; Gantry tilt angle D SLEW F = P0A ; Gantry slew angle D ELV W = P0A ; Energy level (keV) D TIW F = P0A ; Time window (ns) D COM 20S = P02 ; Comments D STYP B = P0A ; Scan type (0=em, 1=tr, 2=bl, 3=norm) D BLAN L = P02 ; Blank scan number D TRAN L = P02 ; Transmission scan number D PAR B = P0A ; Parameter file number D NORM W = P02 ; Norm file number D DPR B = P0A ; Data present in file (1=present) D TNM 8S = P02 ; Tape name (user def) D FERR L = P0A ; FIFO errors D FLOS F = P0A ; FIFO loss rate D INVA F = P0A ; Invalid count rate D TRAT F = P0A ; Total count rate D TAC W = P0A ; Type of attenuation corr. D IMFM W = P0A ; Image format (eg 128) D IMTP 10S = P02 ; Image type (' '=activity) D IMUN 10S = P02 ; Image scale unit (eg nCi/cc) D PXS F = P0A ; Pixel size (mm/pixel) D FFN 4S = P0A ; Filter function (eg 'BOHM') D FWD F = P0A ; Filter width (mm) D SLOT W = P0F ; Slot on DAP disk D CNTX 10S = P0A ; Context of study (user def) D AGE B = P0A ; Patient's age D SEX 1S = P0A ; Patient's sex D MAX W = P02 ; Repr. of highest pixel in image R 64 (---LEN=4--MULT=8------------------------------------------------------- D SING F = P0A ; Singles count rate per detector )----------------------------------------------------------------------- (---LEN=4--MULT=8------------------------------------------------------- D XRAT F = P0A ; Physiological input count rate )----------------------------------------------------------------------- (---LEN=18--MULT=5----------------------------------------------------- D USR F = P02 ; User definable reals D USI L = P02 ; User definable longwords D USC 10S = P02 ; User definable character strings )----------------------------------------------------------------------- (---LEN=37--MULT=1----------------------------------------------------- D CYCL F = P0F ; Gate cycle time low limit D CYCH F = P0F ; Gate cycle time high limit D GTLL F = P0F ; Gate low limit D GTLH F = P0F ; Gate high limit D GTNR B = P0F ; Gate number D GTYP B = P0F ; Type of gating D FRNR W = P0F ; Time frame number D DCY F = P0F ; MTM duty cycle ( in % ) D NGAT B = P0F ; Number of gating bins D NBTS W = P0F ; Number of beats (gated histo) D NBR W = P0F ; Number of beats rejected D AVC F = P0F ; Average cycle D YPOS F = P0A ; Y position of the couch )----------------------------------------------------------------------- (---LEN=103--MULT=30---------------------------------------------------- D STOT F = P0A ; Total number of counts per slice D CAL F = P02 ; Slice calibration factors D MIN W = P02 ; Repr. of lowest pixel in slice D MAG F = P02 ; Image magnification D SPOS B = P02 ; Slice image position in file D SCA F = P02 ; Scatter per slice D RAN F = P02 ; Randoms per slice D DTC F = P02 ; Dead time correction factor D CLV F = P0A ; Cross-section level (mm+OM) D XPAN F = P0A ; X reconstruction displacement D YPAN F = P0A ; Y reconstruction displacement (---LEN=2--MULT=32----------------------------------------------------- D PERX B = P02 ; Perimeter point x-coordinate D PERY B = P02 ; Perimeter point y-coordinate )-------------------------------------------------------------------- )--------------------------------------------------------------------- (---LEN=4--MULT=30---------------------------------------------------- D OFFS F = P02 ;Offset in image )----------------------------------------------------------------------- )-----------------------------------------------------------------------  minc-tools-2.3.00+dfsg/conversion/scxtominc/scxtominc.c0000644000175000000620000014206012574624760022174 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : scxtominc @INPUT : argc, argv - command line arguments @OUTPUT : (none) @RETURNS : error status @DESCRIPTION: Converts scanditronix format files to a minc format file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 11, 1993 (Peter Neelin) @MODIFIED : * $Log: scxtominc.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 1999/11/09 13:34:48 neelin * Year 2000 fix for date stored in minc file. * * Revision 6.1 1999/10/29 17:52:07 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:23:31 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:32 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:13 neelin * Release of minc version 0.4 * * Revision 3.1 1996/01/04 13:41:48 neelin * Added missing exit when user specifies a bad slice range. * * Revision 3.0 1995/05/15 19:31:05 neelin * Release of minc version 0.3 * * Revision 2.5 1995/02/09 14:11:43 neelin * Mods to make irix 5 lint happy. * * Revision 2.4 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.3 1995/01/23 09:21:13 neelin * Changed ncclose to miclose * * Revision 2.2 95/01/23 08:57:37 neelin * Changed nccreate to micreate. * * Revision 2.1 95/01/09 15:17:37 neelin * Added code to check for generic reconstructions. * * Revision 2.0 94/09/28 10:33:50 neelin * Release of minc version 0.2 * * Revision 1.12 94/09/28 10:33:30 neelin * Pre-release * * Revision 1.11 94/09/28 08:23:58 neelin * Find max and min pixel values for both bytes and shorts. * (Shorts are rescaled to full range). * * Revision 1.10 94/05/31 07:56:42 neelin * Added insertblood.c to optionally insert blood data into minc file. * * Revision 1.9 93/11/17 12:21:55 neelin * Changed default to -noclobber. * * Revision 1.8 93/08/11 15:27:34 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 #include #include #include #include #include #include #include "isotope_list.h" /* Macro definitions */ #undef MALLOC #undef REALLOC #undef FREE #define MALLOC( n_items, type ) \ ( (void *) malloc( (size_t) (n_items) * sizeof(type) ) ) #define REALLOC( ptr, n_items, type ) \ ( (void *) realloc( (void *) ptr, (size_t) (n_items) * sizeof(type) ) ) #define FREE( ptr ) \ free( (void *) ptr ) /* Type declarations */ typedef struct { char name[8]; int mult; nc_type type; void *att_vector; } scx_mnem_list_type; typedef struct { int nslices; int low_slice; int high_slice; double scan_time; double time_width; double half_life; double zstart; double zstep; char isotope[16]; int image_size; int ordered_file; int *ordered_slices; char image_type[16]; } scx_file_info_type; typedef struct { int num_scx_slices; int max_nslices; int max_size; float zwidth; float xystep; float xywidth; long vmax; char img_units[16]; char patient_name[32]; char patient_sex[8]; long patient_age; char study_id[40]; char start_time[40]; long start_year; long start_month; long start_day; long start_hour; long start_minute; float start_seconds; char tracer[10]; char injection_time[40]; long injection_hour; long injection_minute; float injection_seconds; float injection_dose; scx_mnem_list_type *mnem_list; int num_mnems; int used_MNI_generic_reconstruction; } scx_general_info_type; typedef struct { double sort_key; void *sort_value; } scx_sort_type; /* Function declarations */ void usage_error(char *progname); int get_scx_file_info(int num_scx_files, char **scx_files, int slice_range[2], scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); void sort_scx_slices(int sort_over_time, int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); int sortcmp(const void *val1, const void *val2); int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info, char *blood_file); int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims,long start[], long count[], short *image, int image_size, long pixel_max, float image_max, double scan_time, double time_width, double zpos); int get_scx_slice(scx_file *scx_fp, int slice_num, long *pixel_max, float *image_max, short *image, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info); double decay_correction(double scan_time, double measure_time, double start_time, double half_life); void CreateBloodStructures (int mincHandle, int bloodHandle); void FillBloodStructures (int mincHandle, int bloodHandle); /* Constants */ #define TRUE 1 #define FALSE 0 #define MAX_DIMS 4 #define HOURS_PER_DAY 24 #define MIN_PER_HOUR 60 #define SEC_PER_MIN 60 /* Scanditronix mnemonics used */ #define SCX_NCS "NCS" #define SCX_IMFM "IMFM" #define SCX_DATY "DATY" #define SCX_DATM "DATM" #define SCX_DATD "DATD" #define SCX_TIM "TIM" #define SCX_TIMH "TIMH" #define SCX_TIMM "TIMM" #define SCX_TIMS "TIMS" #define SCX_TIHU "TIHU" #define SCX_ITM "ITM" #define SCX_ITMH "ITMH" #define SCX_ITMM "ITMM" #define SCX_ITMS "ITMS" #define SCX_MTM "MTM" #define SCX_ISO "ISO" #define SCX_CLV "CLV" #define SCX_CDI "CDI" #define SCX_CSZ "CSZ" #define SCX_PXS "PXS" #define SCX_MAX "MAX" #define SCX_IMUN "IMUN" #define SCX_MAG "MAG" #define SCX_FWD "FWD" #define SCX_PNM "PNM" #define SCX_SEX "SEX" #define SCX_AGE "AGE" #define SCX_RIN "RIN" #define SCX_CAR "CAR" #define SCX_ACT "ACT" #define SCX_IMTP "IMTP" #define SCX_USC "USC" /* Scanditronix constants */ #define SCX_ACTIVITY "" #define SCX_MNI_GENERIC_RECONSTRUCTION_CODE "Generic 8" /* Main program */ int main(int argc, char *argv[]) { /* Variables for arguments */ static int write_byte_data=TRUE; static int sort_over_time=TRUE; static int clobber=FALSE; static int verbose=TRUE; static int decay_correct=TRUE; static int slice_range[2]={0, 9999}; static int copy_all_header=FALSE; static char *blood_file = NULL; /* Argument option table */ static ArgvInfo argTable[] = { {"-byte", ARGV_CONSTANT, (char *) TRUE, (char *) &write_byte_data, "Write out data as bytes (default)."}, {"-short", ARGV_CONSTANT, (char *) FALSE, (char *) &write_byte_data, "Write out data as short integers."}, {"-time", ARGV_CONSTANT, (char *) TRUE, (char *) &sort_over_time, "Keep time ordering of data (default)."}, {"-zposition", ARGV_CONSTANT, (char *) FALSE, (char *) &sort_over_time, "Sort data according to z position."}, {"-decay_correct", ARGV_CONSTANT, (char *) TRUE, (char *) &decay_correct, "Do decay correction on images (default)."}, {"-nodecay_correct", ARGV_CONSTANT, (char *) FALSE, (char *) &decay_correct, "Don't do decay correction."}, {"-clobber", ARGV_CONSTANT, (char *) TRUE, (char *) &clobber, "Overwrite existing file."}, {"-noclobber", ARGV_CONSTANT, (char *) FALSE, (char *) &clobber, "Don't overwrite existing file (default)."}, {"-verbose", ARGV_CONSTANT, (char *) TRUE, (char *) &verbose, "List files as they are converted (default)"}, {"-quiet", ARGV_CONSTANT, (char *) FALSE, (char *) &verbose, "Do not list files as they are converted."}, {"-small_header", ARGV_CONSTANT, (char *) FALSE, (char *) ©_all_header, "Copy only basic header information (default)."}, {"-all_header", ARGV_CONSTANT, (char *) TRUE, (char *) ©_all_header, "Copy all scanditronix header information."}, {"-slices", ARGV_INT, (char *) 2, (char *) slice_range, "Range of slices to copy."}, {"-bloodfile", ARGV_STRING, (char *) 1, (char *) &blood_file, "Insert blood data from this file."}, {NULL, ARGV_END, NULL, NULL, NULL} }; /* Other variables */ char *pname; char *mincfile; char **scx_files; int num_scx_files; scx_file_info_type *scx_file_info; scx_general_info_type *scx_general_info; long count[MAX_DIMS], start[MAX_DIMS]; int ndims; int mincid, icvid, varid; int islice, ifile, i, slice_num; long pixel_max; float image_max; double scale; short *image; scx_file *scx_fp; int status; char *tm_stamp; double first_z, last_z, zstep; /* Get time stamp */ tm_stamp = time_stamp(argc, argv); /* Check arguments */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 3)) { usage_error(pname); } /* Check the slice range */ if ((slice_range[0] < 0) || (slice_range[1] < 0) || (slice_range[1] < slice_range[0])) { (void) fprintf(stderr, "%s: Error in slice range: %d to %d.\n", pname, slice_range[0], slice_range[1]); exit(EXIT_FAILURE); } /* Get file names */ mincfile = argv[argc-1]; argv[argc-1] = NULL; /* Null-terminate scx file list */ num_scx_files = argc - 2; scx_files = &argv[1]; /* Print log message */ if (verbose) { (void) fprintf(stderr, "Reading headers.\n"); } /* Read the files to get basic information */ scx_general_info=MALLOC(1, *scx_general_info); scx_file_info = MALLOC(num_scx_files, *scx_file_info); status=get_scx_file_info(num_scx_files, scx_files, slice_range, scx_file_info, scx_general_info); if (status >= 0) { (void) fprintf(stderr, "%s: Error reading scanditronix file %s.\n", pname, scx_files[status]); exit(EXIT_FAILURE); } /* Allocate space for ordered slice list if sorting over z position */ if (!sort_over_time) { for (ifile=0; ifile1)) { ndims = 4; count[0]=num_scx_files; count[1]=scx_general_info->max_nslices; } else { ndims=3; count[0]=scx_general_info->num_scx_slices; } count[ndims-1] = count[ndims-2] = scx_general_info->max_size; mincid = micreate(mincfile, (clobber ? NC_CLOBBER : NC_NOCLOBBER)); (void) miattputstr(mincid, NC_GLOBAL, MIhistory, tm_stamp); icvid=setup_minc_file(mincid, write_byte_data, copy_all_header, ndims, count, num_scx_files, scx_file_info, scx_general_info, blood_file); if (icvid==MI_ERROR) { (void) fprintf(stderr, "%s: Error setting up minc file %s from scx file %s.\n", pname, mincfile, scx_files[0]); exit(EXIT_FAILURE); } /* Initialize minc start and count vectors */ for (i=0; imax_size * scx_general_info->max_size, short); /* Print log message */ if (verbose) { (void) fprintf(stderr, "Copying files:\n"); } /* Loop through files */ for (ifile=0; ifile1)) { start[0]=scx_file_info[ifile].ordered_file; start[1]=islice; } else if (sort_over_time) start[0]=islice; else start[0]=scx_file_info[ifile].ordered_slices[islice]; count[ndims-1] = count[ndims-2] = scx_file_info[ifile].image_size; /* Copy the slice */ slice_num = islice + scx_file_info[ifile].low_slice; if (get_scx_slice(scx_fp, slice_num, &pixel_max, &image_max, image, &scx_file_info[ifile], scx_general_info) || write_minc_slice(scale, write_byte_data, mincid, icvid, ndims, start, count, image, scx_file_info[ifile].image_size, pixel_max, image_max, scx_file_info[ifile].scan_time, scx_file_info[ifile].time_width, scx_file_info[ifile].zstep * (double) slice_num + scx_file_info[ifile].zstart)) { (void) fprintf(stderr, "%s: Error copying slice from file %s.\n", pname, scx_files[ifile]); exit(EXIT_FAILURE); } } /* End slice loop */ /* Close the scanditronix file */ scx_close(scx_fp); } /* End file loop */ /* Write out average z step and start for irregularly spaced slices */ if ((ndims!=MAX_DIMS) && (num_scx_files>1)) { start[0] = 0; varid = ncvarid(mincid, MIzspace); (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &first_z); start[0] = scx_general_info->num_scx_slices - 1; (void) mivarget1(mincid, varid, start, NC_DOUBLE, NULL, &last_z); if (start[0] > 0) zstep = (last_z - first_z) / ((double) start[0]); else zstep = 1.0; (void) miattputdbl(mincid, varid, MIstep, zstep); (void) miattputdbl(mincid, varid, MIstart, first_z); } /* Close minc file */ (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); (void) miclose(mincid); FREE(image); if (!sort_over_time) { for (ifile=0; ifile] [...] \n", progname); (void) fprintf(stderr, " %s [-help]\n\n", progname); exit(EXIT_FAILURE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_scx_file_info @INPUT : num_scx_files - number of scanditronix files. scx_files - array of scanditronix file names. slice_range - 2-component array giving range of slices @OUTPUT : scx_file_info - array of structures containing information about each file. scx_general_info - general information about the scx files. @RETURNS : (-1) if no error occurs, otherwise, the index (in scx_file) of the first file that could not be read. @DESCRIPTION: Reads information from each scanditronix file given by scx_files. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_scx_file_info(int num_scx_files, char **scx_files, int slice_range[2], scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) { static char *the_months[]= {NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int *num_scx_slices, *max_nslices, *max_size; scx_file *fp; scx_file_info_type *sfip; long lvalue, timh, timm, tims, tihu; float fvalue; char svalue[40]; double inj_time; int ifile, i, imnem, imult, length; scx_mnem_types mtype; scx_mnem_list_type *mnem_list; void *att_vector; /* Initialize number of scanditronix slices */ num_scx_slices = &(scx_general_info->num_scx_slices); max_nslices = &(scx_general_info->max_nslices); max_size = &(scx_general_info->max_size); *num_scx_slices = 0; *max_nslices = 0; *max_size = 0; /* Loop through files, reading information */ for (ifile=0; ifilelow_slice = 0; sfip->high_slice = lvalue - 1; if (slice_range[0] > sfip->low_slice) sfip->low_slice = slice_range[0]; if (slice_range[1] < sfip->high_slice) sfip->high_slice = slice_range[1]; if (sfip->low_slice > sfip->high_slice) sfip->low_slice = sfip->high_slice; sfip->nslices = sfip->high_slice - sfip->low_slice + 1; *num_scx_slices += sfip->nslices; if (sfip->nslices > *max_nslices) *max_nslices = sfip->nslices; /* Get image width */ if (scx_get_mnem(fp, SCX_IMFM, 0, &lvalue, NULL, NULL)) return ifile; sfip->image_size = lvalue; if (lvalue > *max_size) *max_size = lvalue; /* Get time (in seconds) */ if (scx_get_mnem(fp, SCX_TIMH, 0, &timh, NULL, NULL) || scx_get_mnem(fp, SCX_TIMM, 0, &timm, NULL, NULL) || scx_get_mnem(fp, SCX_TIMS, 0, &tims, NULL, NULL) || scx_get_mnem(fp, SCX_TIHU, 0, &tihu, NULL, NULL)) { return ifile; } sfip->scan_time = (timh * MIN_PER_HOUR + timm) * SEC_PER_MIN + tims + ((double) tihu)/100.0; /* Get injection time (in seconds) (use date from before and check for time before inj_time) */ if (scx_get_mnem(fp, SCX_ITMH, 0, &timh, NULL, NULL) || scx_get_mnem(fp, SCX_ITMM, 0, &timm, NULL, NULL) || scx_get_mnem(fp, SCX_ITMS, 0, &tims, NULL, NULL)) { return ifile; } inj_time = (timh * MIN_PER_HOUR + timm) * SEC_PER_MIN + tims; /* Set scan time to be from injection time. Check for scans over midnight (cannot deal with scans longer than 1 day */ sfip->scan_time -= inj_time; if (sfip->scan_time < 0.0) sfip->scan_time += HOURS_PER_DAY * MIN_PER_HOUR * SEC_PER_MIN; /* Get length of frame (in seconds) */ if (scx_get_mnem(fp, SCX_MTM, 0, NULL, &fvalue, NULL)) return ifile; sfip->time_width = fvalue; /* Get scan type */ if (scx_get_mnem(fp, SCX_IMTP, 0, NULL, NULL, sfip->image_type)) return ifile; /* Get isotope and half-life */ if (scx_get_mnem(fp, SCX_ISO, 0, NULL, NULL, sfip->isotope)) return ifile; for (i=0; isotope_list[i].name !=NULL; i++) { if (strncmp(isotope_list[i].name, sfip->isotope, strlen(isotope_list[i].name))==0) break; } sfip->half_life = isotope_list[i].half_life; /* Get z start and step (correct start for non-zero first slice */ if (scx_get_mnem(fp, SCX_CDI, 0, NULL, &fvalue, NULL)) return ifile; sfip->zstep = fvalue; if (scx_get_mnem(fp, SCX_CLV, 0, NULL, &fvalue, NULL)) return ifile; sfip->zstart = fvalue + sfip->low_slice * sfip->zstep; /* Get general information from first scx file */ if (ifile==0) { if (scx_get_mnem(fp, SCX_CSZ, 0, NULL, &scx_general_info->zwidth, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_PXS, 0, NULL, &scx_general_info->xystep, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_FWD, 0, NULL, &scx_general_info->xywidth, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_MAX, 0, &scx_general_info->vmax, NULL, NULL)) return MI_ERROR; if ((scx_general_info->vmax<=0) || (scx_general_info->vmax>32767)) scx_general_info->vmax=32000; if (scx_get_mnem(fp, SCX_IMUN, 0, NULL, NULL, scx_general_info->img_units)) return MI_ERROR; if (scx_get_mnem(fp, SCX_PNM, 0, NULL, NULL, scx_general_info->patient_name)) return MI_ERROR; if (scx_get_mnem(fp, SCX_SEX, 0, NULL, NULL, scx_general_info->patient_sex)) return MI_ERROR; if (scx_general_info->patient_sex[0]=='M') (void) strcpy(scx_general_info->patient_sex, MI_MALE); else if (scx_general_info->patient_sex[0]=='F') (void) strcpy(scx_general_info->patient_sex, MI_FEMALE); else (void) strcpy(scx_general_info->patient_sex, MI_OTHER); if (scx_get_mnem(fp, SCX_AGE, 0, &scx_general_info->patient_age, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_RIN, 0, NULL, NULL, scx_general_info->study_id)) return MI_ERROR; if (scx_get_mnem(fp, SCX_DATY, 0, &scx_general_info->start_year, NULL, NULL)) return MI_ERROR; scx_general_info->start_year += 1900; if (scx_general_info->start_year < 1950) scx_general_info->start_year += 100; if (scx_get_mnem(fp, SCX_DATM, 0, &scx_general_info->start_month, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_DATD, 0, &scx_general_info->start_day, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMH, 0, &scx_general_info->start_hour, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMM, 0, &scx_general_info->start_minute, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIMS, 0, NULL, &scx_general_info->start_seconds, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_TIHU, 0, NULL, &fvalue, NULL)) return MI_ERROR; scx_general_info->start_seconds += fvalue/100.0; if (scx_get_mnem(fp, SCX_TIM, 0, NULL, NULL, svalue)) return MI_ERROR; (void) sprintf(scx_general_info->start_time, "%d-%s-%d %s", (int) scx_general_info->start_day, the_months[scx_general_info->start_month], (int) scx_general_info->start_year, svalue); if (scx_get_mnem(fp, SCX_CAR, 0, NULL, NULL, scx_general_info->tracer)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITM, 0, NULL, NULL, scx_general_info->injection_time)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMH, 0, &scx_general_info->injection_hour, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMM, 0, &scx_general_info->injection_minute, NULL, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ITMS, 0, NULL, &scx_general_info->injection_seconds, NULL)) return MI_ERROR; if (scx_get_mnem(fp, SCX_ACT, 0, NULL, &scx_general_info->injection_dose, NULL)) return MI_ERROR; /* Find out if MNI generic reconstruction was used */ if (scx_get_mnem(fp, SCX_USC, 0, NULL, NULL, svalue)) return MI_ERROR; scx_general_info->used_MNI_generic_reconstruction = (strncmp(svalue, SCX_MNI_GENERIC_RECONSTRUCTION_CODE, strlen(SCX_MNI_GENERIC_RECONSTRUCTION_CODE)) == 0); /* Get list of header values */ /* Get space for first mnemonic */ mnem_list = MALLOC(1, *mnem_list); /* Loop through mnemonics */ for (imnem=0; scx_list_mnems(fp, imnem, mnem_list[imnem].name, &mnem_list[imnem].mult, &mtype)!=NULL; imnem++){ /* Get space for attributes (handle strings differently) */ switch (mtype) { case scx_string: mnem_list[imnem].type = NC_CHAR; length = 2; att_vector = MALLOC(length, char); *((char *) att_vector) = '\0'; break; case scx_long: mnem_list[imnem].type = NC_LONG; att_vector = MALLOC(mnem_list[imnem].mult, long); break; case scx_float: mnem_list[imnem].type = NC_FLOAT; att_vector = MALLOC(mnem_list[imnem].mult, float); break; } /* Loop through multiplicity */ for (imult=0; imult < mnem_list[imnem].mult; imult++) { if (scx_get_mnem(fp, mnem_list[imnem].name, imult, &lvalue, &fvalue, svalue)) return MI_ERROR; switch (mtype) { case scx_string: if (imult>0) { *((char *) att_vector + length - 2) = '\n'; *((char *) att_vector + length - 1) = '\0'; length += 1; } length += strlen(svalue); att_vector = REALLOC(att_vector, length, char); att_vector = strcat((char *) att_vector, svalue); break; case scx_long: *(((long *) att_vector) + imult) = lvalue; break; case scx_float: *(((float *) att_vector) + imult) = fvalue; break; } } /* Loop over multiplicity */ /* Save length of string */ if (mtype==scx_string) { mnem_list[imnem].mult = length - 1; } /* Save pointer to attributes */ mnem_list[imnem].att_vector = att_vector; /* Get space for next mnemonic */ mnem_list = REALLOC(mnem_list, imnem+2, *mnem_list); } scx_general_info->mnem_list=mnem_list; scx_general_info->num_mnems=imnem; } /* If first file */ /* Close file */ scx_close(fp); } /* Loop over files */ return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_scx_slices @INPUT : sort_over_time - boolean indicating whether sort should be over time or z position. num_scx_files - number of scanditronix files. scx_file_info - array of scanditronix file information. scx_general_info - general information about scx files. @OUTPUT : scx_file_info - modified to give slice ordering information. @RETURNS : (nothing) @DESCRIPTION: Sorts the scanditronix slices for the output file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ void sort_scx_slices(int sort_over_time, int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) { int ifile, islice, isort, num_sort, slice_num; /* Variables for sorting */ scx_sort_type *scx_sort; struct { int file; int slice; } *slice_ptr; scx_file_info_type *file_ptr; /* Allocate array for sorting */ num_sort = (sort_over_time) ? num_scx_files : scx_general_info->num_scx_slices; scx_sort = MALLOC (num_sort, *scx_sort); /* Are we sorting over time or z position */ if (sort_over_time) { /* Go through the files */ for (ifile=0; ifilefile = ifile; slice_ptr->slice = islice; scx_sort[isort].sort_value = slice_ptr; isort++; } } } /* Sort the slices */ qsort(scx_sort, num_sort, sizeof(*scx_sort), sortcmp); /* Loop through sorted list */ for (isort=0; isortordered_file = isort; } else { slice_ptr = scx_sort[isort].sort_value; ifile = slice_ptr->file; islice = slice_ptr->slice; scx_file_info[ifile].ordered_slices[islice] = isort; FREE(slice_ptr); } } /* Free the sorting array */ FREE(scx_sort); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sortcmp @INPUT : val1 - first value val2 - second value @OUTPUT : (none) @RETURNS : 0 if values are the same, -1 if val1->sort_key < val2->sort_key and +1 if val1->sort_key > val2->sort_key. @DESCRIPTION: Compares two double precision values. If they are the same, then return 0. If val1 < val2, return -1. If val1 > val2, return +1. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int sortcmp(const void *val1, const void *val2) { if (((scx_sort_type *)val1)->sort_key < ((scx_sort_type *)val2)->sort_key) return -1; else if (((scx_sort_type *)val1)->sort_key > ((scx_sort_type *)val2)->sort_key) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_file @INPUT : mincid - id of minc file write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). copy_all_header - boolean indicating whether all of the scanditronix header information should be copied or not. ndims - number of dimensions for minc file count - lengths of dimensions minc file scx_filename - name of scanditronix file for header values num_scx_files - number of scanditronix files. scx_file_info - array of information on scanditronix files. scx_general_info - general information about scx files blood_file - name of blood file containing data to include @OUTPUT : (nothing) @RETURNS : Image conversion variable id or MI_ERROR if an error occurs. @DESCRIPTION: Initializes the header of the minc file using information from the scanditronix file and the other structures. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int setup_minc_file(int mincid, int write_byte_data, int copy_all_header, int ndims, long count[], int num_scx_files, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info, char *blood_file) { static char *dim_names_array[]={MItime, MIzspace, MIyspace, MIxspace}; char **dim_names; static char *dimwidth_names_array[]={ MItime_width, MIzspace_width, MIyspace_width, MIxspace_width}; char **dimwidth_names; int dim[MAX_DIMS]; int img, imgmax, imgmin, dimvarid, widvarid, icv, varid, scx_var; int idim, imnem; int bloodid; double vrange[2]; /* Create the dimensions */ dim_names = dim_names_array + MAX_DIMS - ndims; dimwidth_names = dimwidth_names_array + MAX_DIMS - ndims; for (idim=0; idim1)) ? 1 : 0, &dim[idim]); widvarid=micreate_std_variable(mincid, dimwidth_names[idim], NC_DOUBLE, (strcmp(dim_names[idim], MItime)==0) ? 1 : 0, &dim[idim]); /* Add attributes to the dimension variables */ if (strcmp(dim_names[idim], MIzspace)==0) { /* Write out step and start. We will rewrite this for irregularly spaced files */ (void) miattputdbl(mincid, dimvarid, MIstep, scx_file_info[0].zstep); (void) miattputdbl(mincid, dimvarid, MIstart, scx_file_info[0].zstart); (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); (void) miattputdbl(mincid, widvarid, MIwidth, (double) scx_general_info->zwidth); (void) miattputstr(mincid, widvarid, MIunits, "mm"); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } else if ((strcmp(dim_names[idim], MIyspace)==0) || (strcmp(dim_names[idim], MIxspace)==0)) { (void) miattputstr(mincid, dimvarid, MIunits, "mm"); (void) miattputdbl(mincid, dimvarid, MIstep, (double) scx_general_info->xystep); (void) miattputstr(mincid, dimvarid, MIspacetype, MI_NATIVE); (void) miattputdbl(mincid, widvarid, MIwidth, (double) scx_general_info->xywidth); (void) miattputstr(mincid, widvarid, MIfiltertype, MI_GAUSSIAN); } else if (strcmp(dim_names[idim], MItime)==0) { (void) miattputstr(mincid, dimvarid, MIunits, "seconds"); (void) miattputstr(mincid, widvarid, MIunits, "seconds"); } } /* Create the image variable */ if (write_byte_data) { img=micreate_std_variable(mincid, MIimage, NC_BYTE, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_UNSIGNED); vrange[0]=0; vrange[1]=255; } else { img=micreate_std_variable(mincid, MIimage, NC_SHORT, ndims, dim); (void) miattputstr(mincid, img, MIsigntype, MI_SIGNED); vrange[0] = -scx_general_info->vmax; vrange[1] = scx_general_info->vmax; } (void) ncattput(mincid, img, MIvalid_range, NC_DOUBLE, 2, vrange); (void) miattputstr(mincid, img, MIcomplete, MI_FALSE); /* Create the image max and min variables */ imgmax=micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); imgmin=micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); (void) miattputstr(mincid, imgmax, MIunits, scx_general_info->img_units); (void) miattputstr(mincid, imgmin, MIunits, scx_general_info->img_units); /* Create the image conversion variable */ icv=miicv_create(); (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT); (void) miicv_setdbl(icv, MI_ICV_VALID_MAX, (double) scx_general_info->vmax); (void) miicv_setdbl(icv, MI_ICV_VALID_MIN, (double) -scx_general_info->vmax); /* Save patient info */ varid = micreate_group_variable(mincid, MIpatient); (void) miattputstr(mincid, varid, MIfull_name, scx_general_info->patient_name); (void) miattputstr(mincid, varid, MIsex, scx_general_info->patient_sex); (void) ncattput(mincid, varid, MIage, NC_LONG, 1, &scx_general_info->patient_age); /* Save study info */ varid = micreate_group_variable(mincid, MIstudy); (void) miattputstr(mincid, varid, MImodality, MI_PET); (void) miattputstr(mincid, varid, MImanufacturer, "Scanditronix"); (void) miattputstr(mincid, varid, MIstudy_id, scx_general_info->study_id); (void) miattputstr(mincid, varid, MIstart_time, scx_general_info->start_time); (void) ncattput(mincid, varid, MIstart_year, NC_LONG, 1, &scx_general_info->start_year); (void) ncattput(mincid, varid, MIstart_month, NC_LONG, 1, &scx_general_info->start_month); (void) ncattput(mincid, varid, MIstart_day, NC_LONG, 1, &scx_general_info->start_day); (void) ncattput(mincid, varid, MIstart_hour, NC_LONG, 1, &scx_general_info->start_hour); (void) ncattput(mincid, varid, MIstart_minute, NC_LONG, 1, &scx_general_info->start_minute); (void) ncattput(mincid, varid, MIstart_seconds, NC_FLOAT, 1, &scx_general_info->start_seconds); /* Save acquisition info */ varid = micreate_group_variable(mincid, MIacquisition); (void) miattputstr(mincid, varid, MIradionuclide, scx_file_info[0].isotope); if (scx_file_info[0].half_life > 0.0) { (void) miattputdbl(mincid, varid, MIradionuclide_halflife, (double) scx_file_info[0].half_life); } (void) miattputstr(mincid, varid, MItracer, scx_general_info->tracer); (void) miattputstr(mincid, varid, MIinjection_time, scx_general_info->injection_time); (void) ncattput(mincid, varid, MIinjection_hour, NC_LONG, 1, &scx_general_info->injection_hour); (void) ncattput(mincid, varid, MIinjection_minute, NC_LONG, 1, &scx_general_info->injection_minute); (void) ncattput(mincid, varid, MIinjection_seconds, NC_FLOAT, 1, &scx_general_info->injection_seconds); (void) ncattput(mincid, varid, MIinjection_dose, NC_FLOAT, 1, &scx_general_info->injection_dose); (void) miattputstr(mincid, varid, MIdose_units, "mCurie"); /* If the MNI generic reconstruction was used, indicate the fact */ if (scx_general_info->used_MNI_generic_reconstruction) { varid = ncvardef(mincid, "MNI_PET_rec", NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI PET reconstruction info"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); (void) miattputstr(mincid, varid, "generic_reconstruction", MI_TRUE); } /* If we want all of the values from the scanditronix header, get them */ if (copy_all_header) { /* Create a variable for scx mnemonics */ scx_var = ncvardef(mincid, "scanditronix", NC_LONG, 0, NULL); (void) miattputstr(mincid, scx_var, MIvartype, MI_GROUP); (void) miattputstr(mincid, scx_var, MIvarid, "MNI SCX variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), scx_var); /* Loop through mnemonics */ for (imnem=0; imnemnum_mnems; imnem++){ ncattput(mincid, scx_var, scx_general_info->mnem_list[imnem].name, scx_general_info->mnem_list[imnem].type, scx_general_info->mnem_list[imnem].mult, scx_general_info->mnem_list[imnem].att_vector); } /* Loop through mnemonics */ } /* If copy_all_header */ /* Open the blood file and create the variables if needed */ if (blood_file != NULL) { bloodid = ncopen(blood_file, NC_NOWRITE); CreateBloodStructures(mincid, bloodid); } /* Attach the icv */ (void) ncendef(mincid); (void) miicv_attach(icv, mincid, img); /* Copy the blood data */ if (blood_file != NULL) { FillBloodStructures(mincid, bloodid); ncclose(bloodid); } return icv; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_scx_slice @INPUT : scx_fp - file pointer for scanditronix file slice_num - slice to copy scx_file_info - information on scanditronix file. scx_general_info - general scx file information @OUTPUT : pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds image - scanditronix image @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Gets a scanditronix image. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_scx_slice(scx_file *scx_fp, int slice_num, long *pixel_max, float *image_max, short *image, scx_file_info_type *scx_file_info, scx_general_info_type *scx_general_info) /* ARGSUSED */ { long npix, ix, iy, y_offset, off1, off2; short temp; /* Get the image from the scanditronix file */ if (scx_get_image(scx_fp, slice_num, image)) return TRUE; /* Flip the scanditronix image to give positive x & y axes */ npix = scx_file_info->image_size * scx_file_info->image_size; for (iy=0; iyimage_size/2; iy++) { y_offset = iy * scx_file_info->image_size; for (ix=0; iximage_size; ix++) { off1 = y_offset + ix; off2 = npix - off1 - 1; temp = image[off1]; image[off1] = image[off2]; image[off2] = temp; } } /* Get image and pixel max */ if (scx_get_mnem(scx_fp, SCX_MAG, slice_num, NULL, image_max, NULL)) return TRUE; if (scx_get_mnem(scx_fp, SCX_MAX, 0, pixel_max, NULL, NULL)) return TRUE; if ((*pixel_max<=0) || (*pixel_max>32767)) *pixel_max = 32000; return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_minc_slice @INPUT : scale - scale for decay correcting image write_byte_data - boolean indicating whether data should be written as bytes (TRUE) or shorts (FALSE). mincid - id of minc file icvid - id of image conversion variable start - coordinate of slice in minc file count - edge lengths of image to write in minc file image - pointer to image buffer pixel_max - maximum pixel value image_max - real value to which pixel_max corresponds scan_time - time of slice time_width - time width of slice zpos - z position of slice scx_file_info - information on scanditronix file. scx_general_info - general scx file information @OUTPUT : (nothing) @RETURNS : Returns TRUE if an error occurs. @DESCRIPTION: Writes out the image to the minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 12, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int write_minc_slice(double scale, int write_byte_data, int mincid, int icvid, int ndims,long start[], long count[], short *image, int image_size, long pixel_max, float image_max, double scan_time, double time_width, double zpos) /*ARGSUSED*/ { double pixmin, pixmax; long ipix, npix; double maximum, minimum; /* Search for pixel max and min */ npix = image_size * image_size; pixmin = pixmax = image[0]; for (ipix=1; ipixpixmax) pixmax = image[ipix]; if (image[ipix]; close(PIPEHANDLE); $/ = $oldsep; 1; } # Routine to extract a vax integer from a string sub get_uchar { if (scalar(@_) != 2) { die "Argument error in get_uchar"; } return unpack('C',scalar(substr($_[0], $_[1], 1))); } # Routine to extract an integer from a string sub get_int { if (scalar(@_) != 2) { die "Argument error in get_int"; } if ($Swap_needed) { return unpack('i',scalar(reverse(substr($_[0], $_[1], 4)))); } else { return unpack('i',scalar(substr($_[0], $_[1], 4))); } } # Routine to extract a vax integer from a string sub get_short { if (scalar(@_) != 2) { die "Argument error in get_short"; } if ($Swap_needed) { return unpack('s',scalar(reverse(substr($_[0], $_[1], 2)))); } else { return unpack('s',scalar(substr($_[0], $_[1], 2))); } } # Routine to extract a vax float from a string sub get_float { if (scalar(@_) != 2) { die "Argument error in get_float"; } local($string) = substr($_[0], $_[1], 4); # $string =~ s/(.|\n)(.|\n)/$2$1/g; # return unpack('f',$string) / 4.0; return unpack('f',$string); } # Routine to extract a clean string (no nulls) from a string sub get_string { if (scalar(@_) != 3) { die "Argument error in get_string"; } local($string) = substr($_[0], $_[1], $_[2]); $string =~ s/\0.*$//g; return $string; } # Routine to read the main file header # # # The image data is stored a 2 bytes per pixel. The images are always 256 x 256. # # Notes: # 1) The byte order of all numerical fields within the MIF file is PC order. # i.e. short (16 bit) - LOW BYTE | HIGH BYTE # Hence, this will appear _byte_ _reversed_ if the file was read on a # Sun. # # 2) The #defines in this document follow the following conventtion: # MIF_ - indicates the define originates within the MIF definition # MIF_CONST_ - indicates the define is one for a field which has a # constant value # MIF_DEFAULT_ - indicates the default value for a field. # MIF_LEN_ - indicates the length of the field # # The function supplied within mifheader.cpp sets all of the constant fields # to their defines and set all non-constant fields to their default values, # again, as specified in their defines. # sub read_main_header { if (scalar(@_) != 2) { die "Argument error in read_main_header"; } local($fh, *header_info) = @_; local($main_header_offset) = 0; local($header); seek($fh, $main_header_offset, 0) || die "header seek: $!"; read($fh, $header, $header_size); # read the whole header in one shot # this header info comes from m2f.h #ifndef _MIFHEADER_H #define _MIFHEADER_H # This is a table of defines for the mifheader. It is a combination of # constant fields and default values for fields. They should always be # used to insure the consistency of the fields in the MIF header. # include #define MIF_HEADER_TOTAL_LENGTH 512 #define SIGNA 71 # GE scanner #define MIF_CONST_FILLER 0x00 #define MIF_CONST_HEADER_OFFSET 1 # # struct mifheader # { # Data area ... Format and order is as the MIF header # BYTE RANGE LENGTH BUFFER FORMAT COMMENT #------------------------------------------------------------------------------ # 000-019 20 0 char string #<<<<<<>>>>>># #define MIF_LEN_ID 20 #define MIF_CONST_MIF_ID "#<<<<<<>>>>>>#" # char mif_id[MIF_LEN_ID]; $header_info{'mif_id'} = &get_string($header, 0, 20); # 020-023 4 0 char string empty unused field # char buffer1[4]; $header_info{'buffer1'} = &get_string($header, 20, 4); # 024-033 9 1 char string scan date from tape - format 16-DEC-71 # NULL terminated to # ensure old version 2.0 # programs work. See note # at the bottom of this # file about these dates. #define MIF_LEN_SCAN_DATE 9 # char scan_date[MIF_LEN_SCAN_DATE + 1]; $header_info{'scan_date'} = &get_string($header, 24, 9); # 034-043 9 1 char string Creation date of the MIF file # format 16-DEC-71 NULL # terminated to ensure # old version 2.0 programs work #define MIF_LEN_MIF_FILE_DATE 9 # char mif_file_date[MIF_LEN_MIF_FILE_DATE + 1]; $header_info{'mif_file_date'} = &get_string($header, 34, 9); # 044-045 2 0 short MIF version code, constant at ... 3 #define MIF_CONST_VERSION 3 # short mif_version; $header_info{'mif_version'} = &get_short($header, 44); # 046-047 2 0 short MIF subversion number set to 0 # This field is not # really used and is a # prime target for # re-assignment #define MIF_CONST_SUBVERSION 0 # short mif_subversion; $header_info{'mif_subversion'} = &get_short($header, 46); # 048-049 2 0 short Scanner ID; See above for a # list of valid scanner ids #define MIF_DEFAULT_SCANNER_ID -1 # short scanner_id; $header_info{'scanner_id'} = &get_short($header, 48); # 050-062 13 0 char string # = (SE or LE time)/1000 + # (patient initials) + (last 8 chars of scanid) # Default = -1, example : 30MKG12306618 #define MIF_LEN_FILE_ID 13 # char file_id[MIF_LEN_FILE_ID]; $header_info{'file_id'} = &get_string($header, 50, 13); # 063-070 8 3 char string scan time in 24 hour format, # e.g. 14:25:33 #define MIF_LEN_SCAN_TIME 8 #char scan_time[MIF_LEN_SCAN_TIME]; $header_info{'scan_time'} = &get_string($header, 63, 8); # 071-071 1 0 unsigned char Number of echos per slice # Used when multiple # echos of the same slice # needs to be placed # within an MIF # file. Usually 1, 0 denotes # an unknown number of echos. # unsigned char echo_count; $header_info{'echo_count'} = &get_uchar($header, 71); # 072-073 2 0 char string empty unused field # char buffer2[2]; $header_info{'buffer2'} = &get_string($header, 72, 2); # 074-077 4 0 long file sequence - scan_id, # unique for every scan #define MIF_DEFAULT_SCAN_ID -1 # long int scan_id; $header_info{'scan_id'} = &get_int($header, 74); # 078-102 25 0 char string patient name #define MIF_LEN_PATIENT_NAME 25 #char patient_name[MIF_LEN_PATIENT_NAME]; $header_info{'patient_name'} = &get_string($header, 78, 25); # 103-127 25 0 char string patient id number #define MIF_LEN_PATIENT_NUMBER 25 #char patient_number[MIF_LEN_PATIENT_NUMBER]; $header_info{'patient_number'} = &get_string($header, 103, 25); # 128-131 4 0 long The date and time of the # executable of the conversion # program used to create this # MIF file. The long is given # by the function time_t # time(time_t *). It is converted # to a string by ctime. #long conversion_exe_time; $header_info{'conversion_exe_time'} = &get_int($header, 128); # 131-133 2 0 short The version number of the # conversion program. # 100's indicate major # versions, units indicate a change. # short conversion_version; $header_info{'conversion_version'} = &get_short($header, 131); # 134-137 4 0 long TE echo time in microseconds #define MIF_DEFAULT_TE -1 #long te; $header_info{'te'} = &get_int($header, 134); # 138-141 4 0 long TR repititon time in # microseconds #define MIF_DEFAULT_TR -1 #long tr; $header_info{'tr'} = &get_int($header, 138); # 142-145 4 0 long slice gap in micrometres. #define MIF_DEFAULT_SLICE_GAP -9999999 #long slice_gap; $header_info{'slice_gap'} = &get_int($header, 142); # 146-151 6 0 char string MIF file creation time, # format hhmmss, e.g. 142530 #define MIF_LEN_MIF_TIME 6 #char mif_time[MIF_LEN_MIF_TIME]; $header_info{'mif_time'} = &get_int($header, 146); # 152-215 64 0 short[32] slice offset array. divided # by 512 to fit into a short + 1 # - starting position of each image e.g. [2,130,258 ....] # short image_offsets[32]; # # here I read only the 1st image offset - and not all of them! (LC) # $header_info{'image_offsets'} = &get_short($header, 152); # 216-217 2 0 short 0 compression is never used # in MIF files #define MIF_DEFAULT_COMPRESSION 0 # short compression; $header_info{'compression'} = &get_short($header, 216); # 218-219 2 0 short window type - set to 0 as # not used # This field was never used so # is prime for re-assignment. #define MIF_CONST_WINDOW_TYPE 0 # short window; $header_info{'window'} = &get_short($header, 218); # 220-221 2 0 short number of bytes used per # pixel - set to 1 for 8 bits per pixel #define MIF_DEFAULT_BPP 1 # short bpp; $header_info{'bpp'} = &get_short($header, 220); # 222-225 4 0 long x pixel size in micrometres, # default -1 #define MIF_DEFAULT_PIX_SIZE_X -1 # long pixel_size_x; $header_info{'pixel_size_x'} = &get_int($header, 222); # 226-229 4 0 long y pixel size in micrometres, # default -1 #define MIF_DEFAULT_PIX_SIZE_Y -1 # long pixel_size_y; $header_info{'pixel_size_y'} = &get_int($header, 226); # 230-233 4 0 long slice thickness in micrometres, # default -1; #define MIF_DEFAULT_SLICE_THICK -1 #long slice_thickness; $header_info{'slice_thickness'} = &get_int($header, 230); # 234-235 2 0 short x matrix dimension, usually # 256 (or number of rows) #define MIF_DEFAULT_DIM_X -1 # short dim_x; $header_info{'dim_x'} = &get_short($header, 234); # 236-237 2 0 short y max matrix dimension, # usually 256 (or number columns) #define MIF_DEFAULT_DIM_Y -1 # short dim_y; $header_info{'dim_y'} = &get_short($header, 236); # 238-239 2 0 short number of data slices in the # file, usually 24 # This number must be less # than or equal to 32 for version 2.0 # or version 3.0 8 bits per # pixel images. It can theoretically # be unlimited for 16 bit images. #define MIF_DEFAULT_SLICE_COUNT -1 # short slice_count; $header_info{'slice_count'} = &get_short($header, 238); # 240-265 26 0 char string name of MIF file created #define MIF_LEN_MIF_NAME 26 #char mif_name[MIF_LEN_MIF_NAME]; $header_info{'mif_name'} = &get_string($header, 240, 26); # 266-329 64 0 short[32] minimum pixel value for each # slice, these are all set to 0 # for a 16 bit MIF #define MIF_DEFAULT_PIXEL_MIN 0 #short pixel_min[32]; # # this will read only the first slice pixel min, and not all of them. # $header_info{'pixel_min'} = &get_short($header, 266); # 330-393 64 0 short[32] maximum pixel value for each # slice, these are all set to 0 # for a 16 bit MIF #define MIF_DEFAULT_PIXEL_MAX 0 # short pixel_max[32]; # # this will read only the first slice pixel max, and not all of them. # $header_info{'pixel_max'} = &get_short($header, 330); # 394-457 64 0 short[32] factor to restore original # pixel values, not used so array of 1's #define MIF_CONST_PIXEL_DIVISOR 1 #short pixel_divisor[32]; # # this will read only the first divisor, and not all of them. # $header_info{'divisor'} = &get_short($header, 394); # 458-469 10 2 char string patient date of birth, e.g. # 1971.12.16 or age where this # information is not available #define MIF_LEN_PATIENT_BIRTH_DATE 12 # char patient_birth_date[MIF_LEN_PATIENT_BIRTH_DATE]; $header_info{'patient_birth_date'} = &get_string($header, 458, 10); # The following transformation and rotation fields are not # presently in use. However, they may be used so # they should not be the first target for any extra fields added. # 470-473 4 0 long x scan orientation, set to 0 # since not used. #define MIF_CONST_TRANS_X 0 # long trans_x; $header_info{'trans_x'} = &get_int($header, 470); # 474-477 4 0 long y scan orientation, set to 0 # since not used. #define MIF_CONST_TRANS_Y 0 # long trans_y; $header_info{'trans_y'} = &get_int($header, 474); # 478-481 4 0 long z scan orientation, set to 0 # since not used. #define MIF_CONST_TRANS_Z 0 # long trans_z; $header_info{'trans_z'} = &get_int($header, 478); # 482-485 4 0 long x scan rotation, set to 0 # since not used. #define MIF_CONST_ROT_X 0 #long rot_x; $header_info{'rot_x'} = &get_int($header, 482); # 486-489 4 0 long y scan rotation, set to 0 # since not used. #define MIF_CONST_ROT_Y 0 #long rot_y; $header_info{'rot_y'} = &get_int($header, 486); # 490-493 4 0 long z scan rotation, set to 0 # since not used. #define MIF_CONST_ROT_Z 0 #long rot_z; $header_info{'rot_z'} = &get_int($header, 490); # 494-501 8 0 char string tape name extracted from # full directory pathname # e.g. SULH0001 #define MIF_LEN_TAPE_NAME 8 #char tape_name[MIF_LEN_TAPE_NAME]; $header_info{'tape_name'} = &get_string($header, 494, 8); # 502-504 3 0 char string patient initials #define MIF_LEN_PATIENT_INITIALS 3 #char patient_initials[MIF_LEN_PATIENT_INITIALS]; $header_info{'patient_initials'} = &get_string($header, 502, 3); # 505-509 4 0 long File count of MIF. The conversion # counts the input files as they are # processed. This yields a unique number for # each input file. The filecount is the unique # number of theA0101L01.M2F first input file to make up the # current MIF file. It is used primarily in the # renaming of the file to distinguish between # two identical scans of the same patient that # may have been saved on the same site tape. #define MIF_DEFAULT_FILE_COUNT -1 #long file_count; $header_info{'file_count'} = &get_int($header, 505); # 509-511 0 7 char string 3 blanks to round of the # header to 512 bytes . #char filler[3]; $header_info{'filler'} = &get_string($header, 509, 3); # Calculate slice locations for $slice (0..$header_info{'slice_count'}) { if ($slice <= 0) { push(@slice_locations, $header_size); } else { $nbytes = $slice_locations[$slice-1] + $header_info{'dim_x'} * $header_info{'dim_y'} * 2; push(@slice_locations, $nbytes); } } } sub print_header { print "mif_id\t\t\t" . $header_info{'mif_id'} . "\n"; print "buffer1\t\t" . $header_info{'buffer1'} . "\n"; print "scan_date\t\t" . $header_info{'scan_date'} . "\n"; print "mif_file_date\t\t" . $header_info{'mif_file_date'} . "\n"; print "mif_version\t\t" . $header_info{'mif_version'} . "\n"; print "mif_subversion\t\t" . $header_info{'mif_subversion'} . "\n"; print "scanner_id\t\t" . $header_info{'scanner_id'} . "\n"; print "file_id\t\t\t" . $header_info{'file_id'} . "\n"; print "scan_time\t\t" . $header_info{'scan_time'} . "\n"; print "echo_count\t\t" . $header_info{'echo_count'} . "\n"; print "buffer2\t\t" . $header_info{'buffer2'} . "\n"; print "scan_id\t\t\t" . $header_info{'scan_id'} . "\n"; print "patient_name\t\t" . $header_info{'patient_name'} . "\n"; print "patient_number\t\t" . $header_info{'patient_number'} . "\n"; print "conversion_exe_time\t" . $header_info{'conversion_exe_time'} . "\n"; print "conversion_version\t" . $header_info{'conversion_version'} . "\n"; print "te\t\t\t" . $header_info{'te'} . "\n"; print "tr\t\t\t" . $header_info{'tr'} . "\n"; print "slice_gap\t\t" . $header_info{'slice_gap'} . "\n"; print "mif_time\t\t" . $header_info{'mif_time'} . "\n"; print "image_offsets\t\t" . $header_info{'image_offsets'} . "\n"; print "compression\t\t" . $header_info{'compression'} . "\n"; print "window\t\t\t" . $header_info{'window'} . "\n"; print "bpp\t\t\t" . $header_info{'bpp'} . "\n"; print "pixel_size_x\t\t" . $header_info{'pixel_size_x'} . "\n"; print "pixel_size_y\t\t" . $header_info{'pixel_size_y'} . "\n"; print "slice_thickness\t\t" . $header_info{'slice_thickness'} . "\n"; print "dim_x\t\t\t" . $header_info{'dim_x'} . "\n"; print "dim_y\t\t\t" . $header_info{'dim_y'} . "\n"; print "slice_count\t\t" . $header_info{'slice_count'} . "\n"; print "mif_name\t\t" . $header_info{'mif_name'} . "\n"; print "pixel_min\t\t" . $header_info{'pixel_min'} . "\n"; print "pixel_max\t\t" . $header_info{'pixel_max'} . "\n"; print "divisor\t\t\t" . $header_info{'divisor'} . "\n"; print "patient_birth_date\t" . $header_info{'patient_birth_date'} . "\n"; print "trans_x\t\t\t" . $header_info{'trans_x'} . "\n"; print "trans_y\t\t\t" . $header_info{'trans_y'} . "\n"; print "trans_z\t\t\t" . $header_info{'trans_z'} . "\n"; print "rot_x\t\t\t" . $header_info{'rot_x'} . "\n"; print "rot_y\t\t\t" . $header_info{'rot_y'} . "\n"; print "rot_z\t\t\t" . $header_info{'rot_z'} . "\n"; print "tape_name\t\t" . $header_info{'tape_name'} . "\n"; print "patient_initials\t" . $header_info{'patient_initials'} . "\n"; print "file_count\t\t" . $header_info{'file_count'} . "\n"; } # Routine to create the minc file sub create_minc_file { if (scalar(@_) != 4) { die "Argument error in create_minc_file"; } local($fh, $mincfile, *slice_locations, *header_info) = @_; # # Print out header information # foreach $key (sort(keys(%header_info))) { # print "$key : $header_info{$key}\n"; # } # Open pipe to rawtominc local($nslc) = $header_info{'slice_count'}; local($nrow) = $header_info{'dim_y'}; local($ncol) = $header_info{'dim_x'}; local($xstep) = -1.0 * $header_info{'pixel_size_x'} / 1000.0; local($ystep) = -1.0 * $header_info{'pixel_size_y'} / 1000.0; local($zstep) = $header_info{'slice_thickness'} / 1000.0; local($type_flag, $type_to_input_cmd); if ($header_info{'bpp'} eq "1") { $data_type = "byte"; $type_flag = "-byte"; $type_to_input_cmd = "cat"; } elsif ($header_info{'bpp'} eq "2") { $data_type = "short"; $type_flag = "-short"; if ($Swap_needed) { $type_to_input_cmd = "byte_swap"; } else { $type_to_input_cmd = "cat"; } # $type_to_input_cmd = "itof"; } else { die "Unknown bytes per pixel <$header_info{'bbp'}>\n"; } print("rawtominc $mincfile $nslc $nrow $ncol -transverse " . "$type_flag -mri -xstep $xstep -ystep $ystep -zstep $zstep ". "-attribute patient:full_name='" .$header_info{'patient_name'}."' ". "-attribute patient:number='" .$header_info{'patient_number'}."' ". "-attribute patient:birth_date='".$header_info{'patient_birth_date'}."' ". "-attribute patient:initials='" .$header_info{'patient_initials'}."' ". "-attribute acquisition:echo_time='" .$header_info{'tr'}."' ". "-attribute acquisition:repetition_time='" .$header_info{'te'}."' ". "-attribute acquisition:scan_date='" .$header_info{'scan_date'}."' ". "-attribute study:study_id='" .$header_info{'scan_id'}."' ". "\n") if ($Debug); open(MINC, "| rawtominc $mincfile $nslc $nrow $ncol -transverse " . "$type_flag -mri -xstep $xstep -ystep $ystep -zstep $zstep ". "-scan_range ". "-attribute patient:full_name='" .$header_info{'patient_name'}."' ". "-attribute patient:number='" .$header_info{'patient_number'}."' ". "-attribute patient:birth_date='".$header_info{'patient_birth_date'}."' ". "-attribute patient:initials='" .$header_info{'patient_initials'}."' ". "-attribute acquisition:echo_time='" .$header_info{'tr'}."' ". "-attribute acquisition:repetition_time='" .$header_info{'te'}."' ". "-attribute acquisition:scan_date='" .$header_info{'scan_date'}."' ". "-attribute study:study_id='" .$header_info{'scan_id'}."' ". ""); # Loop through images, reading them in and scaling them local($nbytes) = $ncol * $nrow * ($data_type eq "short" ? 2 : 1); local($inimage, $outimage); print "Converting slices:"; foreach $slice (0..$nslc-1) { # $image = ($nslc - 1 - $slice); $image = $slice; print STDOUT "."; seek($fh, scalar($slice_locations[$image]), 0) || die "seek: $!"; if (read($fh, $inimage, $nbytes) != $nbytes) {die "read: $!";} &thrupipe($inimage, "$type_to_input_cmd ", $outimage); print MINC $outimage; } print "Done.\n"; # Close minc file close(MINC); } # MAIN PROGRAM # Constants # Get program name $0 =~ /([^\/]+)$/; my($prog) = $1; $| = 1; $header_size = 512; $blksize = 512; # Save history string my($history) = `date`; chop($history); $history .= ">>>> "; $history .= join(" ", $prog, @ARGV) . "\n"; # Parse arguments $Swap_needed = TRUE; while ((scalar(@ARGV) > 0) && ($ARGV[0] =~ /^-./)) { my($arg) = shift; if ($arg =~ /^-clob(ber)?$/) { print "Clobber ON\n"; $Clobber = TRUE; } elsif ($arg =~ /^-deb(ug)?$/) { print "Debug ON\n"; $Debug = TRUE; } elsif ($arg =~ /^-swap?$/) { print "Swap ON\n"; $Swap_needed = TRUE; } elsif ($arg =~ /^-noswap?$/) { print "Swap OFF\n"; undef $Swap_needed; } elsif ($arg =~ /^-h(elp)?$/) { print < $tmpfile" || die "Unable to uncompress $filename ($!)"; open($fh, "<$tmpfile") || die "Unable to open file $filename ($!)"; unlink ($tmpfile) ; } else { open($fh, "<$filename") || die "Unable to open file $filename ($!)"; } # Get general header information &read_main_header($fh, *header_info); &print_header if ($Debug); if (-e $mincfile) { if ($Clobber) { unlink $mincfile; } else { die "$mincfile exists already, use -clobber\n"; } } # Create minc file &create_minc_file($fh, $mincfile, *slice_locations, *header_info); close($fh); minc-tools-2.3.00+dfsg/conversion/sdt2mnc/0002755000175000000620000000000012574624760017363 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/0002755000175000000620000000000012574624760020130 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/node57_files/0002755000175000000620000000000012574624760022413 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/node57_files/img97.gif0000644000175000000620000000061412574624760024035 0ustar stevestaffGIF89a$!,$@*޼iHbTlrz"󢃽SwWwTN̍Ԓ% KP3etkNή8MwfgaP~wV'E4vD(e5e$CIYLj4'WYZZ*Zxi;3H&&HXY}y]W;i=pyTBfrc@Vpda Xq@uB^ȔJL$b:l&3M,|3}2/HGT+ԩTZ5֭\z 6رd˚=6ڵlۺ} 7;minc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/node57_files/img95.gif0000644000175000000620000000010012574624760024021 0ustar stevestaffGIF89a!,D~Xn q^}H&Y;minc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/node57_files/img96.gif0000644000175000000620000000445512574624760024043 0ustar stevestaffGIF89aI!,Iڋ޼H扦ʶ @LF,͂Ģ;ʦČEԪpjB۬.IŶsZjSpP>EB&ŧb@bķ'XXyY#Y9YJVI*z5 Hk %3ѫK'ךDV'|h+ 컛[=#(7 nޛ?/nm~=']nTߕ H04zNT^1~#N?S%T#dn1bI&p v#( ` E4f~^ldtHdJoǐbPe̱3:}+jzM\1^={Q"@ʒĭMOↆ]0JT^s"I>"hϪ[/u U:hۆ{;RݱY`ċ?<5=A7=ܻC[]wU|<꟦'>N7PLf7ipXI]1mFYZ Q'tuׁ~ȢK(<I"a8P7Vv!|~b^!X*ƉVbxW/.h_J@dX0DS6'}"z^^iجƗX{,B-FP V8nR#\F9z'%yɜ *#[Fli盕"~JeZZyOn٩NYVѦlm6n$皺i"{$ڼ]Lpۚ0i 7L*8=|g9JLR)2zgP|YfBfJ шS%^q}r%zi \lVFjP5WDÑs@i&G9JtYOEY(Յ֖ȵ%жhX$kuciXRݣY%b*?O:w$)q~8W;Bx+@+%ZƒnyJ/3םםU}lJn{ӷy9j,¾8㰮Zlzw۷:׬v힯.8"0yU:ʹW̩R]+[~X?"MgDYR,@\xTY FOm3?P<` wBPE+{Nc2H=IcL4PO!Ҡ;/fʮp80oFJK .PS6HYQ`4E3kphw>Ei!m U1r+Ӷ9!2uᛜ*v=q>q ْDF k圷;$G꽎RH7:P|{ )'V-o/͓$(yJCr$E!=r{1Yɚ,|ָu/XcxA[^dϳ %#;)e-'PDS- @9;$B{І:A ,Jjtv0zQ.K!MzyJ(=)lqFt#i8QM̶;PlQz HK]5aڋ\0<5Q]`U^}唛Jt⍰++DZIUٰZy%;\_PMVw<& jOP gܤ9LZrNOeow7NRS3VzۚvA7-]gTS[%\_ż*նk) 'ZW.(Ko}WJԄ寀xxt nPL +-8 'P8Ѣ{C稆PeM6oc_ņզ>Eefh[چlEqTk0E8-NH/ؽ jRT\j`e_: ǻzɪXL͖*uR:8:MOmtS&o~.9+GNږj,1~N>g*Ff5Nvcy=|}d,C9K.GVB^/Ne?9qʋnv6%lhZɧ_IG&~YM0 [PmjmlVrZD%[K6Ta2AWY{(cr zko}q=^JZG|NBGp\e$+:NQNs l;minc-tools-2.3.00+dfsg/conversion/sdt2mnc/doc/sdt_description.html0000644000175000000620000001430612574624760024215 0ustar stevestaffAppendix: Sdt File Format next up previous contents
Next: Appendix: Changes Version 4.0 Up: User's Guide Software Previous: References

Appendix: Sdt File Format

The sdt file format actually consists of a file pair designated by the extensions, ``.sdt'' and ``.spr''.

    { } xxxx

    .sdt
    Contains the Signal DaTa and consists of only raw binary data.

    .spr
    Contains the Signal PaRameters and consists of ASCII attribute:value pairs.

The ``.spr'' file has one ASCII parameter string per line (i.e. newline 'tex2html_wrap_inline4216n' terminated). The parameters format consists first of a colon terminated attribute name, followed by a newline terminated value.

There are three mandatory parameters:

    { } xxxxxxxxxx

    numDim
    The number of dimensions for the data.
    dim
    The number of data points in each dimension.
    dataType
    The data type. There are five data types supported as given with their corresponding sizes in Table13.

table2177
Table 13: Supported Values for the dataType Attribute.

A minimal ``.spr'' example:

 
numDim: 		 4

dim: 64 64 27 110

dataType: REAL

Optional parameters:

    { } xxxxxxxxxxxxx

    origin
    Position of the center of the first voxel. One value for each dimension. If the origin is not specified, but the fov is, then the image is assumed to be centered: tex2html_wrap_inline4218

    fov
    Field of view: The distance between the outside edges of the first and last voxel along each dimension; one value for each dimension. If the fov is not specified it is calculated according to: fov = interval * dim

    interval
    The center to center distance between adjacent voxels along each dimension; one value for each dimension. If the interval is not specified it is calculated according to: interval = fov / dim

    displayRange
    Two values giving the low_value and high_value. Voxel values below the low_value will be displayed as black and voxels with values above the high_value will be displayed as white. Voxels with values within the display range are displayed with a grey value that is scaled linearly between the low_value and high_value.

A typical stimulate ``.spr'' example:

 
numDim: 		 4

dim: 128 128 20 150

origin: -10.000000 -10.000000 0.000000 0.000000

fov: 20.000000 20.000000 2.000000 15.000000

interval: 0.156250 0.156250 1.000000 1.000000

dataType: REAL

displayRange: 0.000000 347.904358

fidName: stimFid.sdt

sdtOrient: sag


next up previous contents
Next: Appendix: Changes Version 4.0 Up: User's Guide Software Previous: References minc-tools-2.3.00+dfsg/conversion/sdt2mnc/sdt2mnc0000755000175000000620000001326612574624760020671 0ustar stevestaff#! /usr/bin/env perl # # Andrew Janke - a.janke@gmail.com # Center for Magnetic Resonance # The University of Queensland # # Copyright Andrew Janke, The University of Queensland. # 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 the University of Queensland make no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Converts a .sdt file to minc # # Mon Oct 20 16:08:45 EDT 2003 - initial versions use strict; use warnings "all"; use Getopt::Tabular; use File::Basename; my($Help, $Usage, $me, @opt_table, $tmpdir, %opt, $history); my(@args, $infile, $inbase, $outfile); $me = &basename($0); %opt = ( 'verbose' => 0, 'clobber' => 0, # 'byte_swap' => 0, ); $Help = < ['-byte', '-signed'], WORD => ['-short', '-signed'], LWORD => ['-int', '-signed'], REAL => ['-float'], COMPLEX => ['-float', '-vector', 2] ); my(%sdt_orients) = ( AX => '-transverse', ); # first convert the header to a hash foreach (split(/\n/, `cat $infile`)){ ($key, $val) = split(/\:/, $_, 2); # clean it up a bit $val =~ s/^(\ )*//; $val =~ s/\ \ /\ /g; $val =~ s/(\ )*$//; $hdr{$key} = $val; } # datatype if(!defined($sdt_dtypes{$hdr{dataType}})) { die "$me: unknown data type: $hdr{dataType}\n\n"; } $gen_hdr->{datatype} = $sdt_dtypes{$hdr{dataType}}; # $gen_hdr->{voxel_min} = $ana_hdr->{glmin}; # $gen_hdr->{voxel_max} = $ana_hdr->{glmax}; # $gen_hdr->{real_min} = $ana_hdr->{cal_min}; # $gen_hdr->{real_max} = $ana_hdr->{cal_max}; # dimension sizes, steps and starts (@{$gen_hdr->{sizes}}) = split(/\ /, $hdr{dim}); (@{$gen_hdr->{starts}}) = split(/\ /, $hdr{origin}); (@{$gen_hdr->{steps}}) = split(/\ /, $hdr{interval}); # orientation if(!defined($sdt_orients{$hdr{sdtOrient}})) { warn "$me: unknown orientation $hdr{sdtOrient}, assuming transverse\n\n"; $gen_hdr->{orientation} = '-transverse'; } else{ $gen_hdr->{orientation} = $sdt_orients{$hdr{sdtOrient}}; } if(defined($opt{orientation})){ warn "$me: overriding file orientation with $opt{orientation}\n"; $gen_hdr->{orientation} = $opt{orientation}; } return $gen_hdr; } # return an ASCII dump of a general header sub dump_general_header{ my($h) = shift; my($tmp); $tmp = "General Header\n"; foreach (sort(keys(%{$h}))){ if($h->{$_} =~ /ARRAY/){ $tmp .= " $_\t<". join(' ' , @{$h->{$_}}) . ">\n"; } else{ $tmp .= " $_\t<$h->{$_}>\n"; } } $tmp .= "\n"; return $tmp; } # write a MINC file from a general header sub write_minc{ my($gen_hdr, $outfile, $infile) = @_; # Set up rawtominc command my(@args) = ('rawtominc', '-clobber'); # datatype and ranges push(@args, @{$gen_hdr->{datatype}}); # push(@args, '-range', $gen_hdr->{voxel_min}, $gen_hdr->{voxel_max}); if($gen_hdr->{real_min} < $gen_hdr->{real_max}){ push(@args, '-real_range', $gen_hdr->{real_min}, $gen_hdr->{real_max}); } else{ push(@args, '-scan_range'); } # orientation and step information push(@args, $gen_hdr->{orientation}, '-xstep', $gen_hdr->{steps}[0], '-ystep', $gen_hdr->{steps}[1], '-zstep', $gen_hdr->{steps}[2], '-xstart', $gen_hdr->{starts}[0], '-ystart', $gen_hdr->{starts}[1], '-zstart', $gen_hdr->{starts}[2]); # files and sizes push(@args, '-input', $infile, $outfile, reverse(@{$gen_hdr->{sizes}}) ); # do the conversion &do_cmd(@args); # add history string &do_cmd('minc_modify_header', '-sinsert', ":history=$history", $outfile); } sub do_cmd { print STDERR "@_\n" if $opt{'verbose'}; system(@_) == 0 or die; } minc-tools-2.3.00+dfsg/conversion/image_filters/0002755000175000000620000000000012574624760020623 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/image_filters/ftoui.c0000644000175000000620000000254212574624760022116 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ftoui.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and writes the floating point equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) -read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #include #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define ARRSIZE 1500 #define INTYPE float #define OUTTYPE unsigned short int #define ROUND( x ) ( (signed long int) ( ((x) > (0)) ? (x)+0.5 : (x)-0.5 )) #define MAXVAL USHRT_MAX #define MINVAL 0 main() { INTYPE value[ARRSIZE], temp; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { temp = ROUND(value[i]); temp = MIN(MAXVAL, temp); temp = MAX(MINVAL, temp); output[i] = (OUTTYPE) temp; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/atof.c0000644000175000000620000000130312574624760021713 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : atof.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads ascii decimal values from standard input and prints the binary equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define OUTTYPE float #define CONTROL_STRING " %f" main() { OUTTYPE value; while (scanf(CONTROL_STRING, &value) > 0) { (void) fwrite(&value, sizeof(OUTTYPE), 1, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/imageinvert.c0000644000175000000620000000576212574624760023311 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : imageinvert.c @INPUT : argc - number of arguments argv - arguments 1 - image size in x (-ve means invert) 2 - image size in y (-ve means invert) 3 - number of bytes per pixel (optional - default = 1) @OUTPUT : (none) @DESCRIPTION: Reads an image from standard input and copies it to standard output, inverting along either or both dimensions according to the arguments @METHOD : @GLOBALS : @CALLS : @CREATED : December 3,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define ABS( x ) ( ((x) > 0) ? (x) : (-(x)) ) #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { int i,j,k,oi,image_size,offset,bytes_per_pixel,row_size,nread; int xsize,ysize,xstart,ystart,xstop,ystop,xstep,ystep; char *pname; char *buffer,*outbuf; /* Check arguments */ pname=argv[0]; if ((argc != 3)&&(argc != 4)) { (void) fprintf(stderr,"Usage : %s xsize ysize \n",pname); (void) exit(ERROR_STATUS); } xsize = atol(argv[1]); ysize = atol(argv[2]); if (argc == 4) { bytes_per_pixel = atol(argv[3]); if (bytes_per_pixel <=0) { (void) fprintf(stderr,"%s : Negative bytes per pixel\n",pname); } } else { bytes_per_pixel = 1; } if ((xsize == 0) || (ysize == 0)) { (void) fprintf(stderr,"%s : Illegal image size\n",pname); (void) exit(ERROR_STATUS); } image_size = ABS(xsize*ysize); row_size = ABS(xsize); if (((buffer=malloc(image_size*bytes_per_pixel)) == NULL) || ((outbuf=malloc(row_size*bytes_per_pixel)) == NULL)){ (void) fprintf(stderr,"%s : Image too large\n",pname); (void) exit(ERROR_STATUS); } /* Get range of loop */ xstart = MAX(0,-xsize-1); xstop = MAX(0,xsize-1); xstep = SIGN(xsize); ystart = MAX(0,-ysize-1); ystop = MAX(0,ysize-1); ystep = SIGN(ysize); /* Loop through images */ while ((nread=fread(buffer, bytes_per_pixel, image_size, stdin)) == image_size) { /* Write out inverted image */ for (j=ystart; ystep*j <= ystop; j += ystep) { offset=j*ABS(xsize); for (i=xstart, oi=0; xstep*i <= xstop; i += xstep, oi++) { for (k=0; k0) { (void) fprintf(stderr,"%s : Insufficient data\n",pname); (void) exit(ERROR_STATUS); } return NORMAL_STATUS; } minc-tools-2.3.00+dfsg/conversion/image_filters/ltoa.c0000644000175000000620000000135712574624760021732 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ltoa.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and prints the ascii decimal equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define INTYPE signed long int #define CONTROL_STRING "%d\n" #define PRINTF_TYPE int main() { INTYPE value; while (fread(&value, sizeof(INTYPE), 1, stdin) == 1) { (void) printf(CONTROL_STRING, (PRINTF_TYPE) value); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/uitof.c0000644000175000000620000000201112574624760022105 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : uitof.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and writes the floating point equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #define ARRSIZE 1500 #define INTYPE unsigned short int #define OUTTYPE float main() { INTYPE value[ARRSIZE]; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { output[i] = (OUTTYPE) value[i]; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/imagetranspose.c0000644000175000000620000000476612574624760024023 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : imagetranspose.c @INPUT : argc - number of arguments argv - arguments 1 - image size in x 2 - image size in y 3 - number of bytes per pixel (optional - default = 1) @OUTPUT : (none) @DESCRIPTION: Reads a series of images from standard input and copies them to standard output, transposing the x and y axes. @METHOD : @GLOBALS : @CALLS : @CREATED : July 16, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { int i,j,k,image_size,bytes_per_pixel,row_size,nread; int xsize,ysize; char *pname; char *buffer,*outbuf; /* Check arguments */ pname=argv[0]; if ((argc != 3)&&(argc != 4)) { (void) fprintf(stderr,"Usage : %s xsize ysize \n",pname); (void) exit(ERROR_STATUS); } xsize = atol(argv[1]); ysize = atol(argv[2]); if (argc == 4) { bytes_per_pixel = atol(argv[3]); if (bytes_per_pixel <=0) { (void) fprintf(stderr,"%s : Negative bytes per pixel\n",pname); } } else { bytes_per_pixel = 1; } if ((xsize <= 0) || (ysize <= 0)) { (void) fprintf(stderr,"%s : Illegal image size\n",pname); (void) exit(ERROR_STATUS); } image_size = xsize*ysize; row_size = ysize; if (((buffer=malloc(image_size*bytes_per_pixel)) == NULL) || ((outbuf=malloc(row_size*bytes_per_pixel)) == NULL)){ (void) fprintf(stderr,"%s : Image too large\n",pname); (void) exit(ERROR_STATUS); } /* Loop through images */ while ((nread=fread(buffer, bytes_per_pixel, image_size, stdin)) == image_size) { /* Write out transposed image */ for (i=0; i < xsize; i++) { for (j=0; j < ysize; j++) { for (k=0; k0) { (void) fprintf(stderr,"%s : Insufficient data\n",pname); (void) exit(ERROR_STATUS); } return NORMAL_STATUS; } minc-tools-2.3.00+dfsg/conversion/image_filters/atol.c0000644000175000000620000000131512574624760021724 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : atol.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads ascii decimal values from standard input and prints the binary equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define OUTTYPE signed long int #define CONTROL_STRING " %d" main() { OUTTYPE value; while (scanf(CONTROL_STRING, &value) > 0) { (void) fwrite(&value, sizeof(OUTTYPE), 1, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/btof.c0000644000175000000620000000200312574624760021712 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : btof.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and writes the floating point equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #define ARRSIZE 1500 #define INTYPE unsigned char #define OUTTYPE float main() { INTYPE value[ARRSIZE]; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { output[i] = (OUTTYPE) value[i]; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/itof.c0000644000175000000620000000200612574624760021724 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : itof.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and writes the floating point equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #define ARRSIZE 1500 #define INTYPE signed short int #define OUTTYPE float main() { INTYPE value[ARRSIZE]; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { output[i] = (OUTTYPE) value[i]; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/extract.c0000644000175000000620000000521112574624760022436 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : extract.c @INPUT : argc - number of arguments argv - arguments 1 - number of bytes to skip (default = 0) 2 - number of bytes to copy (default = all) 3 - filename (if not present uses stdin) @OUTPUT : (none) @DESCRIPTION: Reads a given number of bytes from a file or standard input starting at a given byte. Writes those bytes to standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : January 13,1991 (P.N.) @MODIFIED : June 3,1992 (P.N.) - added -h argument to print usage ---------------------------------------------------------------------------- */ #include #include #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define ARRSIZE 1500 #define INTYPE char main(int argc, char **argv) { char *pname; FILE *fp1; INTYPE value[ARRSIZE]; int nread,skip,pass,all; /* Get arguments */ if ((argc--) > 0) pname=(*argv++); /* program name */ if ((argc > 0) && (strcmp(*argv,"-h") == 0)) { (void) fprintf(stderr, "Usage: %s \n", pname); return 0; } if ((argc--)>0) /* bytes to skip */ skip=atol(*argv++); else skip=0; if ((argc--)>0) /* bytes to copy */ pass=atol(*argv++); else pass=(-1); all=(pass<0); if ((argc--) >0) { /* filename */ fp1=fopen(*argv,"r"); if (fp1 == NULL) { fprintf(stderr,"%s can't open %s\n",pname,*argv); exit(2); } /* Seek to right place on file */ if(fseek(fp1, skip, 0) == -1){ fprintf(stderr,"%s can't fseek on a terminal\n",pname); exit(2); } } else { fp1=stdin; /* Read to skip on stdin */ while(skip > 0) { if (skip > ARRSIZE) { nread = fread(value, sizeof(INTYPE),ARRSIZE,fp1); } else { nread = fread(value, sizeof(INTYPE),skip,fp1); } skip -= nread; if (nread <= 0) return 0; } } /* Copy bytes */ while ((pass > 0) || all) { if ((pass > ARRSIZE)||all) { nread = fread(value, sizeof(INTYPE),ARRSIZE,fp1); } else { nread = fread(value, sizeof(INTYPE),pass,fp1); } fwrite(value,sizeof(INTYPE),nread,stdout); pass -= nread; if (nread<=0) { pass = -1; all = FALSE; } } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/itoa.c0000644000175000000620000000136012574624760021721 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : itoa.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and prints the ascii decimal equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define INTYPE signed short int #define CONTROL_STRING "%d\n" #define PRINTF_TYPE int main() { INTYPE value; while (fread(&value, sizeof(INTYPE), 1, stdin) == 1) { (void) printf(CONTROL_STRING, (PRINTF_TYPE) value); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/Makefile0000644000175000000620000000024612574624760022263 0ustar stevestaffSHELL = /bin/sh CFLAGS = -O LDFLAGS = default: $(MAKE) `ls *.c |sed 's/\.c$$//'` clean: rm -f `ls *.c |sed 's/\.c$$//'` .c: $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) minc-tools-2.3.00+dfsg/conversion/image_filters/byte_swap4.c0000644000175000000620000000214612574624760023051 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : byte_swap4.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads bytes from standard input and writes the swapped bytes standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : January 13,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define BYTES 4 #define ARRSIZE 800*BYTES #define VALTYPE char main() { VALTYPE input[ARRSIZE]; VALTYPE output[ARRSIZE]; int i,nread,extra; while ((nread = fread(input, sizeof(VALTYPE), sizeof(input)/sizeof(VALTYPE), stdin)) > 0) { if ((extra = nread % BYTES) != 0) nread = nread-extra; for (i=0; i < nread; i+=BYTES) { output[i] = (VALTYPE) input[i+3]; output[i+1] = (VALTYPE) input[i+2]; output[i+2] = (VALTYPE) input[i+1]; output[i+3] = (VALTYPE) input[i]; } (void) fwrite(output, sizeof(VALTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/btoa.c0000644000175000000620000000135512574624760021716 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : btoa.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and prints the ascii decimal equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define INTYPE unsigned char #define CONTROL_STRING "%d\n" #define PRINTF_TYPE int main() { INTYPE value; while (fread(&value, sizeof(INTYPE), 1, stdin) == 1) { (void) printf(CONTROL_STRING, (PRINTF_TYPE) value); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/byte_swap.c0000644000175000000620000000201112574624760022754 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : byte_swap.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads bytes from standard input and writes the swapped bytes standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : January 13,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define BYTES 2 #define ARRSIZE 800*BYTES #define VALTYPE char main() { VALTYPE input[ARRSIZE]; VALTYPE output[ARRSIZE]; int i,nread,extra; while ((nread = fread(input, sizeof(VALTYPE), sizeof(input)/sizeof(VALTYPE), stdin)) > 0) { if ((extra = nread % BYTES) != 0) nread = nread-extra; for (i=0; i < nread; i+=BYTES) { output[i] = (VALTYPE) input[i+1]; output[i+1] = (VALTYPE) input[i]; } (void) fwrite(output, sizeof(VALTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/ftoi.c0000644000175000000620000000254512574624760021734 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ftoi.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and writes the floating point equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) -read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #include #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define ARRSIZE 1500 #define INTYPE float #define OUTTYPE signed short int #define ROUND( x ) ( (signed long int) ( ((x) > (0)) ? (x)+0.5 : (x)-0.5 )) #define MAXVAL SHRT_MAX #define MINVAL SHRT_MIN main() { INTYPE value[ARRSIZE], temp; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { temp = ROUND(value[i]); temp = MIN(MAXVAL, temp); temp = MAX(MINVAL, temp); output[i] = (OUTTYPE) temp; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/ftoa.c0000644000175000000620000000134712574624760021723 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ftoa.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads binary values from standard input and prints the ascii decimal equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define INTYPE float #define CONTROL_STRING "%g\n" #define PRINTF_TYPE float main() { INTYPE value; while (fread(&value, sizeof(INTYPE), 1, stdin) == 1) { (void) printf(CONTROL_STRING, (PRINTF_TYPE) value); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/skipdata.c0000644000175000000620000000273312574624760022572 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : skipdata.c @INPUT : argc - number of arguments argv - arguments 1 - number of bytes to copy 2 - number of bytes to skip @OUTPUT : (none) @DESCRIPTION: Alternatedly copies and skips bytes read from standard input (writing to standard output). No leading bytes are skipped. @METHOD : @GLOBALS : @CALLS : @CREATED : July 16,1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include int main(int argc, char *argv[]) { int bytes_to_copy, bytes_to_skip, the_byte, i; /* Get arguments */ if (argc != 3) { (void) fprintf(stderr, "Usage: %s \n", argv[0]); return EXIT_FAILURE; } bytes_to_copy = atoi(argv[1]); bytes_to_skip = atoi(argv[2]); if ((bytes_to_copy < 1) || (bytes_to_skip < 1)) { (void) fprintf(stderr, "%s: bytes to copy and skip must be > 0\n", argv[0]); return EXIT_FAILURE; } /* Loop, copying data and skipping data */ do { for (i=0; (i < bytes_to_copy) && ((the_byte = getchar()) != EOF); i++) { (void) putchar(the_byte); } for (i=0; (i < bytes_to_skip) && ((the_byte = getchar()) != EOF); i++) {} } while (the_byte != EOF); return EXIT_SUCCESS; } minc-tools-2.3.00+dfsg/conversion/image_filters/ftob.c0000644000175000000620000000253612574624760021725 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ftob.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads floating point values from standard input and writes the integer equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - read values into large array for optimization ---------------------------------------------------------------------------- */ #include #include #include #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define ARRSIZE 1500 #define INTYPE float #define OUTTYPE unsigned char #define ROUND( x ) ( (signed long int) ( ((x) > (0)) ? (x)+0.5 : (x)-0.5 )) #define MAXVAL UCHAR_MAX #define MINVAL 0 main() { INTYPE value[ARRSIZE], temp; OUTTYPE output[ARRSIZE]; int i,nread; while ((nread = fread(value, sizeof(INTYPE), sizeof(value)/sizeof(INTYPE), stdin)) > 0) { for (i=0; i < nread; i++) { temp = ROUND(value[i]); temp = MIN(MAXVAL, temp); temp = MAX(MINVAL, temp); output[i] = (OUTTYPE) temp; } (void) fwrite(output, sizeof(OUTTYPE), nread, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/frange.c0000644000175000000620000000435712574624760022240 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : frange.c @INPUT : argc - number of arguments argv - arguments 1 - lower bound 2 - upper bound 3 - null value @OUTPUT : (none) @DESCRIPTION: Reads binary floating point values from standard input, checks to see if they are in the allowed range and copies the result to standard output (in binary form). If the value is outside of the range, it is set to the null value. If the lower bound is greater than the upper bound, then values between upper and lower bound are set to the null value. @METHOD : @GLOBALS : @CALLS : @CREATED : January 6,1992 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - reads large blocks for greater efficiency ---------------------------------------------------------------------------- */ #include #include #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define ARRSIZE 1500 #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { float low,high,null,value[ARRSIZE]; int within,i,nread; char *pname; /* Check arguments */ pname=argv[0]; if (argc != 4) { (void) fprintf(stderr, "Usage : %s low high null\n", pname); (void) exit(ERROR_STATUS); } low = atof(argv[1]); high = atof(argv[2]); null = atof(argv[3]); /* If low <= high, then values should be between low and high, otherwise, they should be outside the range (remember that low >= high) */ within = (low <= high); /* Read in values, do calculation and write out result */ while ((nread=fread(value, sizeof(&value[0]), ARRSIZE, stdin)) > 0) { for (i=0; i=low) && (value[i]<=high) ) ? value[i] : null ); } else { value[i] = ( ( (value[i]>=low) || (value[i]<=high) ) ? value[i] : null ); } } (void) fwrite(value, sizeof(&value[0]), nread, stdout); } return NORMAL_STATUS; } minc-tools-2.3.00+dfsg/conversion/image_filters/fscale.c0000644000175000000620000000323512574624760022225 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : fscale.c @INPUT : argc - number of arguments argv - arguments 1 - scale factor 2 - offset @OUTPUT : (none) @DESCRIPTION: Reads binary floating point values from standard input, scales them according to the arguments and writes the result to standard output (in binary form). Scale and offset are used as follows : result = value * scale + offset @METHOD : @GLOBALS : @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : January 13,1991 (P.N.) - reads large blocks for greater efficiency ---------------------------------------------------------------------------- */ #include #include #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define ARRSIZE 1500 #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { float scale,offset,value[ARRSIZE]; int i,nread; char *pname; /* Check arguments */ pname=argv[0]; if (argc != 3) { (void) fprintf(stderr, "Usage : %s scale offset (o = i*scale + offset)\n", pname); (void) exit(ERROR_STATUS); } scale = atof(argv[1]); offset = atof(argv[2]); /* Read in values, do calculation and write out result */ while ((nread=fread(value, sizeof(&value[0]), ARRSIZE, stdin)) > 0) { for (i=0; i #include #define OUTTYPE unsigned char #define CONTROL_STRING " %d" main() { OUTTYPE value; int temp; while (scanf(CONTROL_STRING, &temp) > 0) { value = (OUTTYPE) temp; (void) fwrite(&value, sizeof(OUTTYPE), 1, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/insert.c0000644000175000000620000000451112574624760022272 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : insert.c @INPUT : argc - number of arguments argv - arguments 1 - filename 2 - number of bytes to skip (default = 0) 3 - number of bytes to copy (default = all) @OUTPUT : (none) @DESCRIPTION: Copies bytes from standard input to filename, starting at the given byte. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : January 13,1991 (P.N.) @MODIFIED : June 3,1992 (P.N.) - added -h option to give usage ---------------------------------------------------------------------------- */ #include #include #define ARRSIZE 1500 #define INTYPE char #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif main(int argc, char **argv) { char *pname; FILE *fp1; INTYPE value[ARRSIZE]; int nread,skip,pass,all; /* Get arguments */ if ((argc--) > 0) pname=(*argv++); /* program name */ if ((argc > 0) && (strcmp(*argv,"-h") == 0)) { (void) fprintf(stderr, "Usage: %s \n", pname); return 0; } /* Open file */ if ((argc--) > 0) { fp1=fopen(*argv,"r+"); if( fp1 == NULL){ fprintf(stderr,"%s can't open %s\n",pname,*argv); exit(2); } } else { fprintf(stderr,"%s - useage file offset numberofbytes\n",pname); exit(2); } *argv++; /* Get bytes to skip and to copy */ if ((argc--)>0) /* bytes to skip */ skip=atol(*argv++); else skip=0; if ((argc--)>0) /* bytes to copy */ pass=atol(*argv++); else pass=(-1); all = (pass<0); /* Seek to right place on file */ if(fseek(fp1, skip, 0) == -1){ fprintf(stderr,"%s can't fseek on a terminal\n",pname); exit(2); } /* Copy bytes */ while(pass > 0 || all) { if ((pass > ARRSIZE)||all) { nread = fread(value, sizeof(INTYPE),ARRSIZE,stdin); } else { nread = fread(value, sizeof(INTYPE),pass,stdin); } fwrite(value,sizeof(INTYPE),nread,fp1); pass -= nread; if (nread<=0) { pass = (-1); all = FALSE; } } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/atoi.c0000644000175000000620000000131712574624760021723 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : atoi.c @INPUT : (none) @OUTPUT : (none) @DESCRIPTION: Reads ascii decimal values from standard input and prints the binary equivalent on standard output. @METHOD : @GLOBALS : (none) @CALLS : @CREATED : December 4,1991 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define OUTTYPE signed short int #define CONTROL_STRING " %hd" main() { OUTTYPE value; while (scanf(CONTROL_STRING, &value) > 0) { (void) fwrite(&value, sizeof(OUTTYPE), 1, stdout); } return 0; } minc-tools-2.3.00+dfsg/conversion/image_filters/reecho.c0000644000175000000620000000337712574624760022244 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : reecho.c @INPUT : argc - number of arguments argv - arguments 1 - number of bytes to read from input 2 - number of times to repeat @OUTPUT : (none) @DESCRIPTION: Reads a given number of bytes from standard input and repeatedly writes out those bytes a given number of times. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10,1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { int nread,nrepeat,i; char *input; char *pname; /* Check arguments */ pname=argv[0]; if (argc != 3) { (void) fprintf(stderr, "Usage : %s nread nrepeat \n", pname); (void) exit(ERROR_STATUS); } nread = atol(argv[1]); nrepeat = atol(argv[2]); /* Get buffer for input */ if (nread <= 0) { (void) fprintf(stderr, "%s: number of bytes to read must be positive\n", pname); (void) exit(ERROR_STATUS); } if ((input = malloc(nread)) == NULL) { (void) fprintf(stderr,"%s: unable to allocate memory\n",pname); (void) exit(ERROR_STATUS); } /* Read input */ if (fread(input, sizeof(*input), nread, stdin) != nread ) { (void) fprintf(stderr,"%s: too few bytes on input\n",pname); (void) exit(ERROR_STATUS); } /* Write out input nrepeat times */ for (i=0; i #include #include #include #define SIGN( x ) ( ((x) > (0)) ? (1) : (-1) ) #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define ARRSIZE 1500 #define ERROR_STATUS -1 #define NORMAL_STATUS 0 main(int argc, char *argv[]) { float maximum,minimum,value[ARRSIZE]; int i,nread; char *pname; /* Check arguments */ pname=argv[0]; if (argc != 1) { (void) fprintf(stderr, "Usage : %s \n", pname); (void) exit(ERROR_STATUS); } /* Initialize values */ maximum = -FLT_MAX; minimum = FLT_MAX; /* Read in values and test for max and min */ while ((nread=fread(value, sizeof(&value[0]), ARRSIZE, stdin)) > 0) { for (i=0; i #include #include #include #include #include #include #include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : connection_okay @INPUT : sockfd - input file descriptor which might be a socket @OUTPUT : (none) @RETURNS : TRUE if connection is okay, FALSE otherwise @DESCRIPTION: Checks whether the connection is allowed. Looks at sockfd to find out if remote host is allowed to connect. If sockfd is a file and not a socket, then the connection is allowed. @METHOD : @GLOBALS : @CALLS : @CREATED : February 20, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private int connection_okay(int sockfd) { struct sockaddr_in us, them; int status; int namelen; extern int Do_logging; /* Get our own id. If sockfd is a file, then its okay. Check that we have an internet connection. */ namelen = sizeof(us); if (getsockname(sockfd, &us, &namelen) != 0) { if (errno == ENOTSOCK) return TRUE; else { (void) fprintf(stderr, "Unable to get our own host address.\n"); return FALSE; } } else if (us.sin_family != AF_INET) { (void) fprintf(stderr, "Connection is not from network.\n"); return FALSE; } /* Try to get id of host at other end of connection */ namelen = sizeof(us); status = getpeername(sockfd, &them, &namelen); if (status != 0) { (void) fprintf(stderr, "Unable to check connection source.\n"); return FALSE; } /* */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Connection from %s ", inet_ntoa(them.sin_addr)); } /* Compare the addresses. Make sure that we have the same IP domain assuming class C structure. */ if ((us.sin_addr.s_addr & IN_CLASSC_NET) != (them.sin_addr.s_addr & IN_CLASSC_NET)) { if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "refused.\n"); } return FALSE; } if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "accepted.\n"); } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_connection @INPUT : argc - number of command-line arguments argv - array of command-line arguments @OUTPUT : afpin - Acr file pointer for input afpout - Acr file pointer for output @RETURNS : (nothing) @DESCRIPTION: Opens the connection for reading writing dicom messages. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void open_connection(int argc, char *argv[], Acr_File **afpin, Acr_File **afpout) { /* Set default file pointers */ *afpin = *afpout = NULL; /* Check for a valid connection */ if (!connection_okay(fileno(stdin))) return; /* Open the connection */ *afpin=acr_initialize_dicom_input(stdin, 0, acr_stdio_read); *afpout=acr_initialize_dicom_output(stdout, 0, acr_stdio_write); /* Ignore SIGPIPE errors in case connection gets closed when we are doing output */ (void) signal(SIGPIPE, SIG_IGN); } minc-tools-2.3.00+dfsg/conversion/dicomserver/siemens_dicom_read.c0000644000175000000620000007336412574624760024323 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_read.c @DESCRIPTION: Code to read siemens dicom files and get info from them. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_read.c,v $ * Revision 6.2 1999-10-29 17:51:58 neelin * Fixed Log keyword * * Revision 6.1 1999/08/05 20:00:34 neelin * Get acquisition id from series or study element, depending on the * version of the Siemens software. * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/10 19:36:13 neelin * Small fix to set default direction cosines when they are absent from the * dicom data. * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.1 1997/06/13 12:51:21 neelin * Changed definition of time index and acquisition id to match change * in Siemens dicom software. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int SPI_Vision_version_pre33A; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_info @INPUT : group_list - input data @OUTPUT : file_info - file-specific info general_info - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_file_info(Acr_Group group_list, File_Info *file_info, General_Info *general_info) { Mri_Index imri; World_Index iworld, jworld; Volume_Index ivolume; int nrows, ncolumns, spatial_sizes[VOL_NDIMS]; int study_id, acq_id, rec_num; int cur_index; int index; int number_of_3D_partitions; Orientation orientation; World_Index volume_to_world[VOL_NDIMS]; double coordinate[WORLD_NDIMS], dircos[VOL_NDIMS][WORLD_NDIMS]; double steps[VOL_NDIMS], starts[VOL_NDIMS], slice_index; Acr_Element_Id mri_index_list[MRI_NDIMS]; Acr_Element_Id mri_total_list[MRI_NDIMS]; /* Array of elements for mri dimensions */ mri_index_list[SLICE] = SPI_Current_slice_number; mri_index_list[ECHO] = ACR_Echo_number; mri_index_list[TIME] = (SPI_Vision_version_pre33A ? ACR_Study : ACR_Series); mri_index_list[PHASE] = NULL; mri_index_list[CHEM_SHIFT] = NULL; mri_total_list[SLICE] = SPI_Number_of_slices_nominal; mri_total_list[ECHO] = SPI_Number_of_echoes; mri_total_list[TIME] = ACR_Acquisitions_in_series; mri_total_list[PHASE] = NULL; mri_total_list[CHEM_SHIFT] = NULL; /* Get image dimensions */ nrows = acr_find_short(group_list, ACR_Rows, 0); ncolumns = acr_find_short(group_list, ACR_Columns, 0); spatial_sizes[VROW] = nrows; spatial_sizes[VCOLUMN] = ncolumns; spatial_sizes[VSLICE] = 1; /* Get intensity information */ get_intensity_info(group_list, file_info); /* Check for necessary values not found */ if ((nrows <= 0) || (ncolumns <= 0) || (file_info->bits_stored <= 0) || (file_info->bits_alloc <= 0)) { file_info->valid = FALSE; return; } /* Get study, acq, rec, image type id's */ get_identification_info(group_list, &study_id, &acq_id, &rec_num, NULL); /* Get number of 3D partitions for working out number of slices */ number_of_3D_partitions = acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 1); if (number_of_3D_partitions < 1) number_of_3D_partitions = 1; /* Get image indices */ for (imri=0; imri < MRI_NDIMS; imri++) { if (mri_index_list[imri] != NULL) { file_info->index[imri] = acr_find_int(group_list, mri_index_list[imri], 1); } else { file_info->index[imri] = 1; } } /* Get coordinate information */ get_coordinate_info(group_list, file_info, &orientation, volume_to_world, spatial_sizes, dircos, steps, starts, coordinate); /* Replace slice index with slice position in hundredths of millimetres if we have more than one partition */ if (number_of_3D_partitions > 1) { slice_index = file_info->coordinate[SLICE] * 100.0; if (slice_index >= 0.0) slice_index += 0.5; else slice_index -= 0.5; slice_index = (int) slice_index; file_info->index[SLICE] = slice_index; } /* Set up general info on first pass */ if (!general_info->initialized) { /* Get row and columns sizes */ general_info->nrows = nrows; general_info->ncolumns = ncolumns; /* Save the study, acquisition, reconstruction and image type identifiers */ general_info->study_id = study_id; general_info->acq_id = acq_id; general_info->rec_num = rec_num; /* No image type is available */ general_info->image_type_string[0] = '\0'; /* Get dimension information */ for (imri=0; imri < MRI_NDIMS; imri++) { /* Gets sizes */ general_info->size[imri] = 1; if (mri_total_list[imri] != NULL) { general_info->total_size[imri] = acr_find_int(group_list, mri_total_list[imri], 1); } else general_info->total_size[imri] = 1; if (general_info->total_size[imri] < 1) general_info->total_size[imri] = 1; /* Check for 3D partitions for slice dimensions */ if (imri == SLICE) { general_info->total_size[imri] *= number_of_3D_partitions; } /* Set initial values */ general_info->default_index[imri] = file_info->index[imri]; general_info->image_index[imri] = -1; /* Allocate space for index and coordinate arrays if total_size > 1. Set the first values. */ if (general_info->total_size[imri] > 1) { general_info->indices[imri] = MALLOC(general_info->total_size[imri] * sizeof(int)); general_info->coordinates[imri] = MALLOC(general_info->total_size[imri] * sizeof(double)); for (index=0; index < general_info->total_size[imri]; index++) { general_info->indices[imri][index] = -1; general_info->coordinates[imri][index] = 0; } general_info->search_start[imri] = 0; general_info->indices[imri][0] = file_info->index[imri]; general_info->coordinates[imri][0] = file_info->coordinate[imri]; } } /* Loop over dimensions */ /* Get spatial coordinate information */ general_info->slice_world = volume_to_world[VSLICE]; general_info->row_world = volume_to_world[VROW]; general_info->column_world = volume_to_world[VCOLUMN]; for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; general_info->step[iworld] = steps[ivolume]; general_info->start[iworld] = starts[ivolume]; for (jworld=0; jworld < WORLD_NDIMS; jworld++) { general_info->dircos[volume_to_world[ivolume]][jworld] = dircos[ivolume][jworld]; } } /* Set data type and range */ if (file_info->bits_alloc <= 8) general_info->datatype = NC_BYTE; else general_info->datatype = NC_SHORT; general_info->is_signed = ((general_info->datatype == NC_SHORT) && (file_info->bits_stored < 16)); general_info->pixel_min = file_info->pixel_min; general_info->pixel_max = file_info->pixel_max; /* Save display window info */ general_info->window_min = file_info->window_min; general_info->window_max = file_info->window_max; /* Get the rest of the header information */ get_general_header_info(group_list, general_info); /* Copy the group list */ general_info->group_list = acr_copy_group_list(group_list); /* Set initialized flag */ general_info->initialized = TRUE; } /* Set up file info */ /* Update general info and validate file on later passes */ else { /* Check for consistent data type */ if (((general_info->datatype == NC_BYTE) && (file_info->bits_alloc > 8)) || ((general_info->datatype == NC_SHORT) && (file_info->bits_alloc <= 8))) { file_info->valid = FALSE; return; } /* Check row and columns sizes */ if ((nrows != general_info->nrows) && (ncolumns != general_info->ncolumns)) { file_info->valid = FALSE; return; } /* Check study and acquisition id's */ if ((general_info->study_id != study_id) || (general_info->acq_id != acq_id)) { file_info->valid = FALSE; return; } /* Look to see if indices have changed */ for (imri=0; imri < MRI_NDIMS; imri++) { /* Get current index */ cur_index = file_info->index[imri]; /* Check whether this index is in the list */ if (general_info->size[imri] == 1) { index = ((cur_index == general_info->default_index[imri]) ? 0 : -1); } else { index = search_list(cur_index, general_info->indices[imri], general_info->size[imri], general_info->search_start[imri]); } /* If it is not, then add it */ if (index < 0) { /* Check whether we can add a new index */ if (general_info->size[imri] >= general_info->total_size[imri]) { file_info->valid = FALSE; return; } /* Add the index and coordinate to the lists */ index = general_info->size[imri]; general_info->search_start[imri] = index; general_info->indices[imri][index] = cur_index; general_info->coordinates[imri][index] = file_info->coordinate[imri]; general_info->size[imri]++; } } /* Loop over Mri_Index */ /* Update display window info */ if (general_info->window_min > file_info->window_min) general_info->window_min = file_info->window_min; if (general_info->window_max < file_info->window_max) general_info->window_max = file_info->window_max; } /* Update general info for this file */ /* If we get to here, then we have a valid file */ file_info->valid = TRUE; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_identification_info @INPUT : group_list - input data @OUTPUT : study_id acq_id rec_num image_type @RETURNS : (nothing) @DESCRIPTION: Routine to get image identification information. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_identification_info(Acr_Group group_list, int *study_id, int *acq_id, int *rec_num, int *image_type) { int number_of_frames; if (study_id != NULL) { *study_id = acr_find_int(group_list, ACR_Study_date, 0); } if (acq_id != NULL) { *acq_id = acr_find_int(group_list, (SPI_Vision_version_pre33A ? ACR_Study : ACR_Series), 0); number_of_frames = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); if ((number_of_frames > 1) || (*acq_id == 0)) { *acq_id = acr_find_int(group_list, ACR_Study_time, 0); } } if (rec_num != NULL) *rec_num = 0; if (image_type != NULL) *image_type = 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_intensity_info @INPUT : group_list - input data @OUTPUT : file_info - file-specific info @RETURNS : (nothing) @DESCRIPTION: Routine to get intensity information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_intensity_info(Acr_Group group_list, File_Info *file_info) { double window_centre, window_width; /* Get pixel storage information */ file_info->bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); file_info->bits_stored = acr_find_short(group_list, ACR_Bits_stored, 0); /* Get pixel value information */ file_info->pixel_min = acr_find_short(group_list, ACR_Smallest_pixel_value, 0); file_info->pixel_max = acr_find_short(group_list, ACR_Largest_pixel_value, (1 << file_info->bits_stored) - 1); file_info->slice_min = file_info->pixel_min; file_info->slice_max = file_info->pixel_max; /* Get window min and max */ window_centre = (file_info->slice_max + file_info->slice_min) / 2.0; window_width = file_info->slice_max - file_info->slice_min; window_centre = acr_find_double(group_list, ACR_Window_centre, window_centre); window_width = acr_find_double(group_list, ACR_Window_width, window_width); window_width /= 2.0; file_info->window_min = window_centre - window_width; file_info->window_max = window_centre + window_width; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_coordinate_info @INPUT : group_list - input data sizes - size of each spatial dimension @OUTPUT : file_info - file-specific info volume_to_world - volume index to world coordinate index mapping dircos - direction cosines for spatial dimensions steps - step sizes for spatial dimensions starts - start positions for spatial dimensions (for a slice) coordinate - coordinate of centre of slice @RETURNS : (nothing) @DESCRIPTION: Routine to get coordinate information for a slice from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]) { Volume_Index ivolume; World_Index iworld; Acr_Element_Id dircos_elid[VOL_NDIMS]; Acr_Element element; int found_dircos[VOL_NDIMS], found_coordinate; double frame_time, start_time; double magnitude, largest, centre; double darray[2]; static Orientation orientation_list[WORLD_NDIMS] = {SAGITTAL, CORONAL, TRANSVERSE}; /* Set direction cosine element ids. Note that the reversal of rows and columns is intentional - their idea of the meaning of theses labels is different from ours. (Their row vector points along the row and not along the row dimension.) */ dircos_elid[VSLICE] = SPI_Image_normal; dircos_elid[VROW] = SPI_Image_column; dircos_elid[VCOLUMN] = SPI_Image_row; /* Get direction cosines */ for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { found_dircos[ivolume] = FALSE; element = acr_find_group_element(group_list, dircos_elid[ivolume]); if (element == NULL) continue; if (acr_get_element_numeric_array(element, WORLD_NDIMS, dircos[ivolume]) != WORLD_NDIMS) continue; convert_coordinate(dircos[ivolume]); found_dircos[ivolume] = TRUE; } /* Normalize the direction cosines */ for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { magnitude = 0.0; for (iworld=0; iworld < WORLD_NDIMS; iworld++) { magnitude += dircos[ivolume][iworld] * dircos[ivolume][iworld]; } if (magnitude <= 0) { found_dircos[ivolume] = FALSE; continue; } magnitude = sqrt(magnitude); for (iworld=0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] /= magnitude; } } /* If we don't find direction cosines, then assume transverse volume */ if (!found_dircos[VSLICE] || !found_dircos[VROW] || !found_dircos[VCOLUMN]) { for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { for (iworld=0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] = ((ivolume == (WORLD_NDIMS-iworld-1)) ? -1.0 : 0.0); } } } /* Figure out volume index to world index mapping and sign of direction cosines */ for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { largest = -1.0; for (iworld=0; iworld < WORLD_NDIMS; iworld++) { magnitude = dircos[ivolume][iworld]; if (magnitude < 0.0) magnitude = -magnitude; if (magnitude > largest) { largest = magnitude; volume_to_world[ivolume] = iworld; } } } /* Get orientation */ *orientation = orientation_list[volume_to_world[VSLICE]]; /* Get step information. */ for (ivolume=0; ivolume < sizeof(darray)/sizeof(darray[0]); ivolume++) darray[ivolume] = -DBL_MAX; element = acr_find_group_element(group_list, ACR_Pixel_size); if (element != NULL) (void) acr_get_element_numeric_array(element, sizeof(darray)/sizeof(darray[0]), darray); if (darray[0] == -DBL_MAX) darray[0] = 1.0; if (darray[1] == -DBL_MAX) darray[1] = darray[0]; steps[VCOLUMN] = darray[0]; steps[VROW] = darray[0]; steps[VSLICE] = acr_find_double(group_list, ACR_Slice_thickness, 1.0); /* Make sure that direction cosines point the right way (dot product of direction cosine and axis is positive) and that step has proper sign */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; if (dircos[ivolume][iworld] < 0.0) { steps[ivolume] *= -1.0; for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] *= -1.0; } } } /* Find 3D coordinate of slice */ element = acr_find_group_element(group_list, SPI_Image_position); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) != WORLD_NDIMS)) { found_coordinate = FALSE; for (iworld=0; iworld < WORLD_NDIMS; iworld++) coordinate[iworld] = 0.0; } else found_coordinate = TRUE; convert_coordinate(coordinate); /* Work out start positions */ for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && found_dircos[VCOLUMN]) { centre = coordinate[XCOORD] * dircos[ivolume][XCOORD] + coordinate[YCOORD] * dircos[ivolume][YCOORD] + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; } else { centre = 0.0; } starts[ivolume] = centre - (steps[ivolume] * (sizes[ivolume] - 1.0) / 2.0); } /* Find position along each dimension */ file_info->coordinate[SLICE] = starts[VSLICE]; file_info->coordinate[ECHO] = acr_find_double(group_list, ACR_Echo_time, 0.0) / (double) MS_PER_SECOND; start_time = acr_find_double(group_list, ACR_Study_time, 0.0); frame_time = acr_find_double(group_list, ACR_Acquisition_time, start_time); start_time = convert_time_to_seconds(start_time); frame_time = convert_time_to_seconds(frame_time) - start_time; if (frame_time < 0.0) frame_time += (double) SECONDS_PER_DAY; file_info->coordinate[TIME] = frame_time; file_info->coordinate[PHASE] = 0.0; file_info->coordinate[CHEM_SHIFT] = 0.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_coordinate @INPUT : coordinate @OUTPUT : coordinate @RETURNS : (nothing) @DESCRIPTION: Routine to convert a coordinate to the correct orientation @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void convert_coordinate(double coordinate[WORLD_NDIMS]) { coordinate[XCOORD] = -coordinate[XCOORD]; coordinate[ZCOORD] = -coordinate[ZCOORD]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_general_header_info @INPUT : group_list - input data @OUTPUT : general_info - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract general header information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_general_header_info(Acr_Group group_list, General_Info *general_info) { int maxlen, length; char *string, *ptr; /* Maximum length for strings */ maxlen = sizeof(Cstring) - 1; /* Get intensity units */ (void) strncpy(general_info->units, "", maxlen); /* Get patient info */ (void) strncpy(general_info->patient.name, acr_find_string(group_list, ACR_Patient_name, ""), maxlen); (void) strncpy(general_info->patient.identification, acr_find_string(group_list, ACR_Patient_identification, ""), maxlen); (void) strncpy(general_info->patient.birth_date, acr_find_string(group_list, ACR_Patient_birth_date, ""), maxlen); string = acr_find_string(group_list, ACR_Patient_sex, ""); if (*string == 'M') (void) strncpy(general_info->patient.sex, MI_MALE, maxlen); else if (*string == 'F') (void) strncpy(general_info->patient.sex, MI_FEMALE, maxlen); else if (*string == 'O') (void) strncpy(general_info->patient.sex, MI_OTHER, maxlen); else (void) strncpy(general_info->patient.sex, "", maxlen); general_info->patient.weight = acr_find_double(group_list, ACR_Patient_weight, -DBL_MAX); /* Get study info */ (void) strncpy(general_info->study.start_time, acr_find_string(group_list, ACR_Study_date, ""), maxlen - 1); length = strlen(general_info->study.start_time); general_info->study.start_time[length] = ' '; length++; (void) strncpy(&general_info->study.start_time[length], acr_find_string(group_list, ACR_Study_time, ""), maxlen - length); string = acr_find_string(group_list, ACR_Modality, ""); if (strcmp(string, ACR_MODALITY_MR) == 0) (void) strncpy(general_info->study.modality, MI_MRI, maxlen); (void) strncpy(general_info->study.manufacturer, acr_find_string(group_list, ACR_Manufacturer, ""), maxlen); (void) strncpy(general_info->study.model, acr_find_string(group_list, ACR_Manufacturer_model, ""), maxlen); (void) strncpy(general_info->study.institution, acr_find_string(group_list, ACR_Institution_id, ""), maxlen); (void) strncpy(general_info->study.station_id, acr_find_string(group_list, ACR_Station_id, ""), maxlen); (void) strncpy(general_info->study.ref_physician, acr_find_string(group_list, ACR_Referring_physician, ""), maxlen); (void) strncpy(general_info->study.procedure, acr_find_string(group_list, ACR_Procedure_description, ""), maxlen); (void) sprintf(general_info->study.study_id, "%d", general_info->study_id); (void) sprintf(general_info->study.acquisition_id, "%d_%d", acr_find_int(group_list, ACR_Series, 0), general_info->acq_id); /* Get acquisition information */ string = acr_find_string(group_list, SPI_Sequence_file_name, ""); ptr = string + strlen(string) - 1; while ((ptr > string) && (*ptr != '/')) {ptr--;} if ((ptr >= string) && (*ptr == '/')) string = ptr + 1; (void) strncpy(general_info->acq.scan_seq, string, maxlen); ptr = general_info->acq.scan_seq; while (*ptr != '\0') { if (*ptr == '.') *ptr = '\0'; ptr++; } general_info->acq.rep_time = acr_find_double(group_list, ACR_Repetition_time, -DBL_MAX); if (general_info->acq.rep_time != -DBL_MAX) general_info->acq.rep_time /= 1000.0; general_info->acq.echo_time = acr_find_double(group_list, ACR_Echo_time, -DBL_MAX); if (general_info->acq.echo_time != -DBL_MAX) general_info->acq.echo_time /= 1000.0; general_info->acq.inv_time = acr_find_double(group_list, ACR_Inversion_time, -DBL_MAX); if (general_info->acq.inv_time != -DBL_MAX) general_info->acq.inv_time /= 1000.0; general_info->acq.flip_angle = acr_find_double(group_list, ACR_Flip_angle, -DBL_MAX); general_info->acq.num_avg = acr_find_double(group_list, ACR_Nr_of_averages, -DBL_MAX); general_info->acq.imaging_freq = acr_find_double(group_list, ACR_Imaging_frequency, -DBL_MAX); if (general_info->acq.imaging_freq != -DBL_MAX) general_info->acq.imaging_freq *= 1e6; (void) strncpy(general_info->acq.imaged_nucl, acr_find_string(group_list, ACR_Imaged_nucleus, ""), maxlen); (void) strncpy(general_info->acq.comments, "", maxlen); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_time_to_seconds @INPUT : dicom_time @OUTPUT : (none) @RETURNS : real time in seconds from beginning of day @DESCRIPTION: Routine to convert dicom seconds (decimal hhmmss.xxxxx) to real seconds since the start of day. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public double convert_time_to_seconds(double dicom_time) { /* Constants */ #define DICOM_SECONDS_PER_HOUR 10000 #define DICOM_SECONDS_PER_MINUTE 100 /* Variables */ double real_time, hours, minutes, seconds; /* Get the components of the time */ hours = (int) (dicom_time / (double) DICOM_SECONDS_PER_HOUR); dicom_time -= hours * DICOM_SECONDS_PER_HOUR; minutes = (int) (dicom_time / (double) DICOM_SECONDS_PER_MINUTE); dicom_time -= minutes * DICOM_SECONDS_PER_MINUTE; seconds = dicom_time; /* Work out the number of seconds */ real_time = hours * SECONDS_PER_HOUR + minutes * SECONDS_PER_MINUTE + seconds; return real_time; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_siemens_dicom_image @INPUT : group_list - input data @OUTPUT : image - image data structure (user must free data) @RETURNS : (nothing) @DESCRIPTION: Routine to get an image from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image) { /* Variables */ Acr_Element element; int nrows, ncolumns; int bits_alloc; int bits_stored; int image_group; void *data = NULL; long imagepix, ipix; struct Acr_Element_Id elid; nc_type datatype; /* Get the image information */ bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); bits_stored = acr_find_short(group_list, ACR_Bits_stored, bits_alloc); nrows = acr_find_short(group_list, ACR_Rows, 0); ncolumns = acr_find_short(group_list, ACR_Columns, 0); image_group = acr_find_short(group_list, ACR_Image_location, ACR_ACTUAL_IMAGE_GID); /* Figure out type */ if (bits_alloc > CHAR_BIT) datatype = NC_SHORT; else datatype = NC_BYTE; /* Set image info */ image->nrows = nrows; image->ncolumns = ncolumns; imagepix = nrows * ncolumns; image->data = (unsigned short *) MALLOC(imagepix * sizeof(short)); image->free = TRUE; /* Get image pointer */ elid.group_id = image_group; elid.element_id = SPI_IMAGE_ELEMENT; element = acr_find_group_element(group_list, &elid); if (element == NULL) { (void) memset(image->data, 0, imagepix * sizeof(short)); return; } data = acr_get_element_data(element); /* Convert the data according to type */ /* Look for byte data */ if (datatype == NC_BYTE) { for (ipix=0; ipix < imagepix; ipix++) { image->data[ipix] = *((unsigned char *) data + ipix); } } else { /* Look for unpacked short data */ if (bits_alloc == nctypelen(datatype) * CHAR_BIT) { acr_get_short(acr_get_element_byte_order(element), nrows*ncolumns, data, image->data); } /* Fill with zeros in any other case */ else { (void) memset(image->data, 0, imagepix * sizeof(short)); } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver/string_to_filename.c0000644000175000000620000000552512574624760024354 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename.c @DESCRIPTION: Code to convert a string to something that can be used in a file name. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1997 (Peter Neelin) @MODIFIED : * $Log: string_to_filename.c,v $ * Revision 6.1 1999-10-29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #define SEPARATOR '_' /* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename @INPUT : string - string to convert maxlen - maximum length of output string (including terminating '\0') @OUTPUT : filename - output string @RETURNS : (nothing) @DESCRIPTION: Routine to convert a string to something that can be used in a filename @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void string_to_filename(char *string, char *filename, int maxlen) { int length, isrc, idst; int ch; int found_first, need_separator; /* Get string length */ length = strlen(string); if (length > maxlen-1) length = maxlen - 1; /* Loop through characters */ idst = 0; found_first = FALSE; need_separator = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalnum(ch)) { found_first = TRUE; if (need_separator) { filename[idst++] = SEPARATOR; need_separator = FALSE; } filename[idst++] = tolower(ch); } else if (found_first) { need_separator = TRUE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; return; } minc-tools-2.3.00+dfsg/conversion/dicomserver/dicomserver-debug.c0000644000175000000620000000026412574624760024105 0ustar stevestaff#if 0 # define DO_SELF_SUSPEND #endif #if 0 # define DO_INPUT_TRACING #endif #if 0 # define KEEP_FILES #endif #if 0 # define DO_HIGH_LOGGING #endif #include "dicomserver.c" minc-tools-2.3.00+dfsg/conversion/dicomserver/use_the_files.c0000644000175000000620000001564212574624760023323 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : use_the_files.c @DESCRIPTION: Code to do something with the files copied through dicom. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: use_the_files.c,v $ * Revision 6.2 2002-12-07 13:02:28 neelin * Fixed prototype for gethostname * * Revision 6.1 1999/10/29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* Function prototypes */ int gethostname (char *name, size_t namelen); /* ----------------------------- MNI Header ----------------------------------- @NAME : use_the_files @INPUT : project_name - name to use for project file num_files - number of image files file_list - list of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to do something with the files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 23, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void use_the_files(char *project_name, int num_files, char *file_list[], Data_Object_Info *data_info[]) { int ifile; extern int Do_logging; char **acq_file_list; int num_acq_files; int *used_file; int found_first; int cur_study; int cur_acq; int cur_rec; int cur_imgtyp; int cur_echo; int cur_dyn_scan; int echoes_in_one_file; int dyn_scans_in_one_file; int exit_status; char *output_file_name; char file_prefix[256]; int output_uid, output_gid; char command_line[512]; char string[512]; FILE *fp; /* Look for defaults file */ (void) read_project_file(project_name, file_prefix, &output_uid, &output_gid, command_line, (int) sizeof(command_line)); /* Allocate space for acquisition file list */ acq_file_list = MALLOC(num_files * sizeof(*acq_file_list)); used_file = MALLOC(num_files * sizeof(*used_file)); for (ifile=0; ifile < num_files; ifile++) used_file[ifile] = FALSE; /* Separate files into acquisitions. Loop until we don't find any more acquisitions */ do { /* Loop through files, looking for an acquisition */ found_first = FALSE; num_acq_files = 0; for (ifile=0; ifile < num_files; ifile++) { if (used_file[ifile]) continue; if (!found_first) { found_first = TRUE; cur_study = data_info[ifile]->study_id; cur_acq = data_info[ifile]->acq_id; cur_rec = data_info[ifile]->rec_num; cur_imgtyp = data_info[ifile]->image_type; cur_echo = data_info[ifile]->echo_number; cur_dyn_scan = data_info[ifile]->dyn_scan_number; echoes_in_one_file = (data_info[ifile]->num_echoes > 2); dyn_scans_in_one_file = (data_info[ifile]->num_dyn_scans > 2); used_file[ifile] = TRUE; } else if ((data_info[ifile]->study_id == cur_study) && (data_info[ifile]->acq_id == cur_acq) && (data_info[ifile]->rec_num == cur_rec) && (data_info[ifile]->image_type == cur_imgtyp) && ((data_info[ifile]->echo_number == cur_echo) || echoes_in_one_file) && ((data_info[ifile]->dyn_scan_number == cur_dyn_scan) || dyn_scans_in_one_file)) { used_file[ifile] = TRUE; } if (used_file[ifile]) { acq_file_list[num_acq_files] = file_list[ifile]; num_acq_files++; } } /* Use the files for this acquisition */ if (found_first) { /* Print out the file names */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\nFiles copied:\n"); for (ifile=0; ifile < num_acq_files; ifile++) { (void) fprintf(stderr, " %s\n", acq_file_list[ifile]); } } /* Create minc file */ exit_status = siemens_dicom_to_minc(num_acq_files, acq_file_list, NULL, FALSE, file_prefix, &output_file_name); if (exit_status != EXIT_SUCCESS) continue; /* Print log message */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Created minc file %s.\n", output_file_name); } /* Invoke a command on the file (if requested) and get the returned file name */ if (strlen(command_line) > (size_t) 0) { (void) sprintf(string, "%s %s", command_line, output_file_name); if ((fp=popen(string, "r")) != NULL) { (void) fscanf(fp, "%s", output_file_name); if (pclose(fp) != EXIT_SUCCESS) { (void) fprintf(stderr, "Error executing command\n \"%s\"\n", string); } else if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Executed command \"%s\",\nproducing file %s.\n", string, output_file_name); } } else { (void) fprintf(stderr, "Error executing command \"%s\"\n", string); } } /* Change the ownership */ if ((output_uid != INT_MIN) && (output_gid != INT_MIN)) { (void) chown(output_file_name, (uid_t) output_uid, (gid_t) output_gid); } } } while (found_first); /* Free acquisition file list */ FREE(acq_file_list); FREE(used_file); } minc-tools-2.3.00+dfsg/conversion/dicomserver/dicomserver.h0000644000175000000620000000560212574624760023027 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomserver.h @DESCRIPTION: Header file that includes things needed for dicomserver. @METHOD : @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicomserver.h,v $ * Revision 6.1 1999-10-29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include #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 #include #include #include #include #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define FILE_ALLOC_INCREMENT 10 /* Connection timeout length in seconds */ #define CONNECTION_TIMEOUT (60*5) /* Time to sleep between image reads when a child process is running. This prevents the server from outrunning its children. */ #define SERVER_SLEEP_TIME 3 /* Define logging constants */ #define NO_LOGGING 0 #define LOW_LOGGING 1 #define HIGH_LOGGING 2 /* File containing defaults for dicomserver */ #define OUTPUT_DEFAULT_FILE_DIR "/usr/local/lib" #define OUTPUT_DEFAULT_FILE_PREFIX "dicomserver." /* System log file (set to NULL for no logging of error) */ #define SYSTEM_LOG "/dev/log" /* Type for carrying around object information */ typedef struct { int study_id; int acq_id; int rec_num; int image_type; int num_echoes; int echo_number; int num_dyn_scans; int dyn_scan_number; } Data_Object_Info; /* Define macro for array size */ #define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) #include #include minc-tools-2.3.00+dfsg/conversion/dicomserver/minc_file.c0000644000175000000620000005715312574624760022435 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_file.c @DESCRIPTION: Code to do minc file handling. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: minc_file.c,v $ * Revision 6.3 2000-03-02 20:56:40 neelin * Write out original image indices as attributes in the minc file under * the dicominfo variable. * * Revision 6.2 2000/03/02 16:20:57 neelin * Clamp maximum voxel to second highest if difference between maximum and * second is greater than difference between second and minimum. * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* Global for minc history (sorry, but it was kludged in afterwards) */ char *minc_history = NULL; /* Define mri dimension names */ static char *mri_dim_names[] = { NULL, "echo_time", MItime, "phase_number", "chemical_shift", NULL}; /* Macros */ #define STRLEN(s) ((int) strlen(s)) /* ----------------------------- MNI Header ----------------------------------- @NAME : create_minc_file @INPUT : minc_file - name of file to create. If NULL, a name is generated internally. clobber - if TRUE, any existing file will be overwritten. general_info - information for creating the file. file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : id of image conversion variable (MI_ERROR in case of error). @DESCRIPTION: Routine to create the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int create_minc_file(char *minc_file, int clobber, General_Info *general_info, char *file_prefix, char **output_file_name) { static char temp_name[256]; char patient_name[256]; char *filename; int minc_clobber; int mincid, icvid; Mri_Index imri; char scan_label[MRI_NDIMS][20]; /* Prefixes for creating file name */ static char *scan_prefix[MRI_NDIMS] = {"sl", "e", "d", "p", "cs"}; /* Turn off fatal errors */ ncopts = NCOPTS_DEFAULT; /* Create the file name if needed */ if (minc_file != NULL) { filename = minc_file; } else { /* Get patient name */ string_to_filename(general_info->patient.name, patient_name, sizeof(patient_name)); if (STRLEN(patient_name) == 0) { (void) strcpy(patient_name, "unknown"); } /* Get strings for echo number, etc. */ for (imri=0; imri < MRI_NDIMS; imri++) { if ((general_info->size[imri] < general_info->total_size[imri]) && (general_info->size[imri] == 1)) { (void) sprintf(scan_label[imri], "_%s%d", scan_prefix[imri], general_info->default_index[imri]+1); } else { (void) strcpy(scan_label[imri], ""); } } /* Create file name */ (void) sprintf(temp_name, "%s%s_%s_%s%s%s%s%s%s_mri.mnc", file_prefix, patient_name, general_info->study.study_id, general_info->study.acquisition_id, scan_label[SLICE], scan_label[ECHO], scan_label[TIME], scan_label[PHASE], scan_label[CHEM_SHIFT]); filename = temp_name; } /* Set output file name */ if (output_file_name != NULL) *output_file_name = filename; /* Set the clobber value */ if (clobber) minc_clobber = NC_CLOBBER; else minc_clobber = NC_NOCLOBBER; /* Create the file */ mincid = micreate(filename, minc_clobber); if (mincid == MI_ERROR) return MI_ERROR; /* Set up variables */ setup_minc_variables(mincid, general_info); /* Put the file in data mode */ (void) ncsetfill(mincid, NC_NOFILL); if (ncendef(mincid) == MI_ERROR) { return MI_ERROR; } /* Create the icv */ icvid = miicv_create(); /* Set the type and range */ (void) miicv_setint(icvid, MI_ICV_TYPE, NC_SHORT); if (general_info->is_signed) (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); else (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, general_info->pixel_min); (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, general_info->pixel_max); /* Attach the icv */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_variables @INPUT : mincid general_info @OUTPUT : general_info @RETURNS : (nothing) @DESCRIPTION: Routine to setup minc variables. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void setup_minc_variables(int mincid, General_Info *general_info) { Mri_Index imri; Volume_Index ivol; World_Index iworld; int ndims; int dim[MAX_VAR_DIMS]; long dimsize; char *dimname; int varid, imgid, dicomvar; double valid_range[2]; char name[MAX_NC_NAME]; int index; int regular; double separation, diff; Acr_Group cur_group; Acr_Element cur_element; int length; char *data; nc_type datatype; int is_char; int ich; long *indices; /* Define the spatial dimension names */ static char *spatial_dimnames[WORLD_NDIMS] = {MIxspace, MIyspace, MIzspace}; /* Create the dimensions from slowest to fastest */ ndims=0; /* Create the non-spatial dimensions (from slowest to fastest) */ for (imri=MRI_NDIMS-1; (int) imri > SLICE; imri--) { dimsize = general_info->size[imri]; if (general_info->size[imri] > 1) { dimname = mri_dim_names[imri]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (imri == TIME) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); (void) miattputstr(mincid, varid, MIunits, "s"); } else if (imri == ECHO) { varid = ncvardef(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); (void) miattputstr(mincid, varid, MIvartype, MI_DIMENSION); (void) miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); (void) miattputstr(mincid, varid, MIunits, "s"); } general_info->image_index[imri] = ndims; ndims++; } } /* Next the spatial dimensions */ for (ivol=0; ivol < VOL_NDIMS; ivol++) { switch (ivol) { case VSLICE: dimsize = general_info->size[SLICE]; iworld = general_info->slice_world; break; case VROW: dimsize = general_info->nrows; iworld = general_info->row_world; break; case VCOLUMN: dimsize = general_info->ncolumns; iworld = general_info->column_world; break; } dimname = spatial_dimnames[iworld]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (ivol == VSLICE) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); /* Check for regular slices */ regular = TRUE; separation = general_info->step[general_info->slice_world]; for (index=1; index < general_info->size[SLICE]; index++) { diff = general_info->coordinates[SLICE][index] - general_info->coordinates[SLICE][index-1] - separation; if (diff < 0.0) diff = -diff; if (separation != 0.0) diff /= separation; if (diff > COORDINATE_EPSILON) { regular = FALSE; break; } } if (regular) (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); } else varid = micreate_std_variable(mincid, dimname, NC_LONG, 0, NULL); (void) miattputdbl(mincid, varid, MIstep, general_info->step[iworld]); (void) miattputdbl(mincid, varid, MIstart, general_info->start[iworld]); (void) miattputstr(mincid, varid, MIspacetype, MI_NATIVE); (void) ncattput(mincid, varid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, general_info->dircos[iworld]); if (ivol == VSLICE) { general_info->image_index[SLICE] = ndims; } ndims++; } /* Set up image variable */ imgid = micreate_std_variable(mincid, MIimage, general_info->datatype, ndims, dim); if (general_info->is_signed) (void) miattputstr(mincid, imgid, MIsigntype, MI_SIGNED); else (void) miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); valid_range[0] = general_info->pixel_min; valid_range[1] = general_info->pixel_max; (void) ncattput(mincid, imgid, MIvalid_range, NC_DOUBLE, 2, valid_range); (void) miattputstr(mincid, imgid, MIcomplete, MI_FALSE); /* Create image max and min variables */ varid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); if (STRLEN(general_info->units) > 0) (void) miattputstr(mincid, varid, MIunits, general_info->units); varid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); if (STRLEN(general_info->units) > 0) (void) miattputstr(mincid, varid, MIunits, general_info->units); /* Create the patient variable */ varid = micreate_group_variable(mincid, MIpatient); if (STRLEN(general_info->patient.name) > 0) (void) miattputstr(mincid, varid, MIfull_name, general_info->patient.name); if (STRLEN(general_info->patient.identification) > 0) (void) miattputstr(mincid, varid, MIidentification, general_info->patient.identification); if (STRLEN(general_info->patient.birth_date) > 0) (void) miattputstr(mincid, varid, MIbirthdate, general_info->patient.birth_date); if (STRLEN(general_info->patient.sex) > 0) (void) miattputstr(mincid, varid, MIsex, general_info->patient.sex); if (general_info->patient.weight != -DBL_MAX) (void) miattputdbl(mincid, varid, MIweight, general_info->patient.weight); /* Create the study variable */ varid = micreate_group_variable(mincid, MIstudy); if (STRLEN(general_info->study.start_time) > 0) (void) miattputstr(mincid, varid, MIstart_time, general_info->study.start_time); if (STRLEN(general_info->study.modality) > 0) (void) miattputstr(mincid, varid, MImodality, general_info->study.modality); if (STRLEN(general_info->study.institution) > 0) (void) miattputstr(mincid, varid, MIinstitution, general_info->study.institution); if (STRLEN(general_info->study.station_id) > 0) (void) miattputstr(mincid, varid, MIstation_id, general_info->study.station_id); if (STRLEN(general_info->study.ref_physician) > 0) (void) miattputstr(mincid, varid, MIreferring_physician, general_info->study.ref_physician); if (STRLEN(general_info->study.procedure) > 0) (void) miattputstr(mincid, varid, MIprocedure, general_info->study.procedure); if (STRLEN(general_info->study.study_id) > 0) (void) miattputstr(mincid, varid, MIstudy_id, general_info->study.study_id); if (STRLEN(general_info->study.acquisition_id) > 0) (void) miattputstr(mincid, varid, "acquisition_id", general_info->study.acquisition_id); /* Create acquisition variable */ varid = micreate_group_variable(mincid, MIacquisition); if (STRLEN(general_info->acq.scan_seq) > 0) (void) miattputstr(mincid, varid, MIscanning_sequence, general_info->acq.scan_seq); if (general_info->acq.rep_time != -DBL_MAX) (void) miattputdbl(mincid, varid, MIrepetition_time, general_info->acq.rep_time); if ((general_info->acq.echo_time != -DBL_MAX) && (general_info->size[ECHO] <= 1)) (void) miattputdbl(mincid, varid, MIecho_time, general_info->acq.echo_time); if (general_info->acq.inv_time != -DBL_MAX) (void) miattputdbl(mincid, varid, MIinversion_time, general_info->acq.inv_time); if (general_info->acq.flip_angle != -DBL_MAX) (void) miattputdbl(mincid, varid, "flip_angle", general_info->acq.flip_angle); if (general_info->acq.num_avg != -DBL_MAX) (void) miattputdbl(mincid, varid, MInum_averages, general_info->acq.num_avg); if (general_info->acq.imaging_freq != -DBL_MAX) (void) miattputdbl(mincid, varid, MIimaging_frequency, general_info->acq.imaging_freq); if (STRLEN(general_info->acq.imaged_nucl) > 0) (void) miattputstr(mincid, varid, MIimaged_nucleus, general_info->acq.imaged_nucl); if (STRLEN(general_info->acq.comments) > 0) (void) miattputstr(mincid, varid, MIcomments, general_info->acq.comments); /* Create the dicom info variable */ varid = ncvardef(mincid, "dicominfo", NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI DICOM information variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); if (STRLEN(general_info->image_type_string) > 0) (void) miattputstr(mincid, varid, "image_type", general_info->image_type_string); (void) miattputdbl(mincid, varid, "window_min", general_info->window_min); (void) miattputdbl(mincid, varid, "window_max", general_info->window_max); /* Put the index list into the dicominfo variable */ for (imri=0; (int) imri < MRI_NDIMS; imri++) { dimsize = general_info->size[imri]; if ((dimsize > 1) && ((indices = MALLOC(sizeof(*indices) * dimsize)) != NULL)) { for (index=0; index < dimsize; index++) { indices[index] = general_info->indices[imri][index]; } if (imri == SLICE) { dimname = spatial_dimnames[general_info->slice_world]; } else { dimname = mri_dim_names[imri]; } (void) sprintf(name, "%s-indices", dimname); (void) ncattput(mincid, varid, name, NC_LONG, (int) dimsize, indices); FREE(indices); } } /* Put group info in header */ cur_group = general_info->group_list; dicomvar = ncvardef(mincid, DICOM_ROOT_VAR, NC_LONG, 0, NULL); (void) miattputstr(mincid, dicomvar, MIvartype, MI_GROUP); (void) miattputstr(mincid, dicomvar, MIvarid, "MNI DICOM variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), dicomvar); while (cur_group != NULL) { /* Create variable for group */ (void) sprintf(name, "dicom_0x%04x", acr_get_group_group(cur_group)); varid = ncvardef(mincid, name, NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI DICOM variable"); (void) miadd_child(mincid, dicomvar, varid); /* Loop through elements of group */ cur_element = acr_get_group_element_list(cur_group); while (cur_element != NULL) { (void) sprintf(name, "el_0x%04x", acr_get_element_element(cur_element)); is_char = TRUE; length = acr_get_element_length(cur_element); data = acr_get_element_data(cur_element); for (ich=0; ich < length; ich++) { if (!isprint((int) data[ich])) { is_char = FALSE; break; } } if (is_char) datatype = NC_CHAR; else datatype = NC_BYTE; ncattput(mincid, varid, name, datatype, length, data); cur_element = acr_get_element_next(cur_element); } cur_group = acr_get_group_next(cur_group); } /* Create the history attribute */ if (minc_history != NULL) { (void) miattputstr(mincid, NC_GLOBAL, MIhistory, minc_history); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : save_minc_image @INPUT : icvid general_info file_info image @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to save the image in the minc file @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void save_minc_image(int icvid, General_Info *general_info, File_Info *file_info, Image_Data *image) { int mincid, imgid; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int file_index, array_index; int idim; Mri_Index imri; char *dimname; unsigned short pvalue, pmax, pmin, pmax2; double dvalue, maximum, minimum, scale, offset; long ipix, imagepix, imax; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); (void) miicv_inqint(icvid, MI_ICV_VARID, &imgid); /* Create start and count variables */ idim = 0; for (imri=MRI_NDIMS-1; (int) imri >= 0; imri--) { if (general_info->image_index[imri] >= 0) { file_index = general_info->image_index[imri]; if (general_info->size[imri] > 1) { array_index = search_list(file_info->index[imri], general_info->indices[imri], general_info->size[imri], general_info->search_start[imri]); if (array_index < 0) array_index = 0; general_info->search_start[imri] = array_index; } else { array_index = 0; } start[file_index] = array_index; count[file_index] = 1; idim++; } } start[idim] = 0; start[idim+1] = 0; count[idim] = general_info->nrows; count[idim+1] = general_info->ncolumns; /* Write out slice position */ switch (general_info->slice_world) { case XCOORD: dimname = MIxspace; break; case YCOORD: dimname = MIyspace; break; case ZCOORD: dimname = MIzspace; break; default: dimname = MIzspace; } (void) mivarput1(mincid, ncvarid(mincid, dimname), &start[general_info->image_index[SLICE]], NC_DOUBLE, NULL, &file_info->coordinate[SLICE]); /* Write out time of slice, if needed */ if (general_info->size[TIME] > 1) { (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[TIME]), &start[general_info->image_index[TIME]], NC_DOUBLE, NULL, &file_info->coordinate[TIME]); } /* Write out echo time of slice, if needed */ if (general_info->size[ECHO] > 1) { (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[ECHO]), &start[general_info->image_index[ECHO]], NC_DOUBLE, NULL, &file_info->coordinate[ECHO]); } /* Search image for max and min. If there is a single voxel that is much brighter than the rest, we want to ignore this. We do this by finding the two maximum values (pmax and pmax2) and then comparing the difference between pmin and pmax2 with pmax2 and pmax. We also keep track of the index of the maximum voxel. If it is too big, then we clamp it to the second largest value. */ imagepix = general_info->nrows * general_info->ncolumns; pmax = pmax2 = 0; pmin = USHRT_MAX; imax = 0; for (ipix=0; ipix < imagepix; ipix++) { pvalue = image->data[ipix]; if (pvalue > pmax) { pmax2 = pmax; pmax = pvalue; imax = ipix; } else if (pvalue > pmax2) { pmax2 = pvalue; } if (pvalue < pmin) { pmin = pvalue; } } if (((int) pmax - (int) pmax2) > ((int) pmax2 - (int) pmin)) { pmax = pmax2; image->data[imax] = pmax2; } /* Re-scale the images */ if (pmax > pmin) scale = (general_info->pixel_max - general_info->pixel_min) / ((double) pmax - (double) pmin); else scale = 0.0; offset = general_info->pixel_min - scale * (double) pmin; for (ipix=0; ipix < imagepix; ipix++) { dvalue = image->data[ipix]; image->data[ipix] = dvalue * scale + offset; } /* Calculate new intensity max and min */ if (general_info->pixel_max > general_info->pixel_min) scale = (file_info->slice_max - file_info->slice_min) / (general_info->pixel_max - general_info->pixel_min); else scale = 0.0; offset = file_info->slice_min - scale * general_info->pixel_min; minimum = (double) pmin * scale + offset; maximum = (double) pmax * scale + offset; /* Write out the max and min values */ (void) mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, NULL, &minimum); (void) mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, NULL, &maximum); /* Write out the image */ (void) miicv_put(icvid, start, count, image->data); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_file @INPUT : icvid - value returned by create_minc_file @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 30, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void close_minc_file(int icvid) { int mincid; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Write out the complete attribute */ (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); /* Close the file */ (void) miclose(mincid); (void) miicv_free(icvid); return; } minc-tools-2.3.00+dfsg/conversion/dicomserver/siemens_dicom_to_minc.c0000644000175000000620000003174612574624760025036 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc.c @DESCRIPTION: Code to convert a list of Siemens dicom files to minc format. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_to_minc.c,v $ * Revision 6.1 1999-10-29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; /* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc @INPUT : num_files - number of image files file_list - list of file names minc_file - name of output minc file (NULL means make one up) clobber - if TRUE, then open the output with NC_CLOBBER file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : EXIT_SUCCESS if no error, EXIT_FAILURE on error. @DESCRIPTION: Routine to convert a list of Siemens dicom files to minc format. @METHOD : @GLOBALS : Do_logging @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int siemens_dicom_to_minc(int num_files, char *file_list[], char *minc_file, int clobber, char *file_prefix, char **output_file_name) { Acr_Group group_list; int max_group; File_Info *file_info; General_Info general_info; Image_Data image; int icvid; int ifile; Mri_Index imri; char *out_file_name; int isep; /* Allocate space for the file information */ file_info = MALLOC(num_files * sizeof(*file_info)); /* Last group needed for first pass */ max_group = ACR_ACTUAL_IMAGE_GID - 1; /* Add all control characters as numeric array separators to handle odd behaviour with Siemens dicom files */ for (isep=0; isep < 31; isep++) { (void) acr_element_numeric_array_separator(isep); } /* Initialize some values for general info */ general_info.initialized = FALSE; general_info.group_list = NULL; for (imri=0; imri < MRI_NDIMS; imri++) { general_info.indices[imri] = NULL; general_info.coordinates[imri] = NULL; } /* Loop through file list getting information */ for (ifile=0; ifile < num_files; ifile++) { /* Read the file */ group_list = read_siemens_dicom(file_list[ifile], max_group); /* Get file-specific information */ get_file_info(group_list, &file_info[ifile], &general_info); /* Delete the group list */ acr_delete_group_list(group_list); /* Print log message if not using file */ if (!file_info[ifile].valid) { if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Not using file %s\n", file_list[ifile]); } } } /* Sort the dimensions */ sort_dimensions(&general_info); /* Create the output file */ if (general_info.initialized) { icvid = create_minc_file(minc_file, clobber, &general_info, file_prefix, &out_file_name); } if (output_file_name != NULL) *output_file_name = out_file_name; /* Check that we found the general info and that the minc file was created okay */ if ((!general_info.initialized) || (icvid == MI_ERROR)) { if (general_info.initialized) { (void) fprintf(stderr, "Error creating minc file %s.\n", out_file_name); } free_info(&general_info, file_info, num_files); FREE(file_info); return EXIT_FAILURE; } /* We now read all groups */ max_group = 0; /* Loop through the files again and put images into the minc file */ for (ifile=0; ifile < num_files; ifile++) { /* Check that we have a valid file */ if (!file_info[ifile].valid) { continue; } /* Read the file */ group_list = read_siemens_dicom(file_list[ifile], max_group); /* Get image */ get_siemens_dicom_image(group_list, &image); /* Save the image and any other information */ save_minc_image(icvid, &general_info, &file_info[ifile], &image); /* Delete the group list */ acr_delete_group_list(group_list); /* Free the image data */ if ((image.data != NULL) && (image.free)) FREE(image.data); } /* Close the output file */ close_minc_file(icvid); /* Free the general_info and file_info stuff */ free_info(&general_info, file_info, num_files); FREE(file_info); return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_siemens_dicom @INPUT : filename - name of siemens dicom file to read max_group - maximum group number to read @OUTPUT : (none) @RETURNS : group list read in from file @DESCRIPTION: Routine to read in a group list from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group read_siemens_dicom(char *filename, int max_group) { FILE *fp; Acr_File *afp; Acr_Group group_list; /* Open the file */ fp = fopen(filename, "r"); if (fp == NULL) return NULL; /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); /* Read in group list */ (void) acr_input_group_list(afp, &group_list, max_group); /* Close the file */ acr_file_free(afp); (void) fclose(fp); return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_info @INPUT : general_info file_info num_files @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to free contents of general and file info structures. @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void free_info(General_Info *general_info, File_Info *file_info, int num_files) { Mri_Index imri; /* Free the general info pointers */ for (imri=0; imri < MRI_NDIMS; imri++) { if (general_info->indices[imri] != NULL) { FREE(general_info->indices[imri]); } if (general_info->coordinates[imri] != NULL) { FREE(general_info->coordinates[imri]); } } /* Free the group list */ if (general_info->group_list != NULL) { acr_delete_group_list(general_info->group_list); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : search_list @INPUT : value list list_length starting_point - point from which search should start @OUTPUT : (none) @RETURNS : Index in list where value is found, or -1 is value not found. @DESCRIPTION: Routine to search a list for a value, returning the index into the list. If the value is not found, then -1 is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int search_list(int value, int list[], int list_length, int starting_point) { int index; /* Check list length and starting point */ if (list_length <= 0) return -1; if ((starting_point >= list_length) || (starting_point < 0)) { starting_point = 0; } /* Loop over indices, wrapping at the end of the list */ index = starting_point; do { if (list[index] == value) return index; index++; if (index >= list_length) index = 0; } while (index != starting_point); /* If we get to here, we didn't find the value */ return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_dimensions @INPUT : general_info @OUTPUT : general_info @RETURNS : (nothing) @DESCRIPTION: Routine to sort the MRI dimensions according to their coordinates. It also fills in the step and start values for the SLICE dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void sort_dimensions(General_Info *general_info) { Mri_Index imri; Sort_Element *sort_array; int nvalues, ival, jval; int reverse_array; /* Sort the dimensions, if needed */ for (imri = 0; imri < MRI_NDIMS; imri++) { if (general_info->size[imri] > 1) { /* Set up the array for sorting */ nvalues = general_info->size[imri]; sort_array = MALLOC(nvalues * sizeof(*sort_array)); for (ival=0; ival < nvalues; ival++) { sort_array[ival].identifier = general_info->indices[imri][ival]; sort_array[ival].original_index = ival; sort_array[ival].value = general_info->coordinates[imri][ival]; } /* Sort the array */ qsort((void *) sort_array, (size_t) nvalues, sizeof(*sort_array), dimension_sort_function); /* Figure out if we should reverse the array to keep something similar to the original ordering */ reverse_array = (sort_array[0].original_index > sort_array[nvalues-1].original_index); /* Copy the information back into the appropriate arrays */ for (ival=0; ival < nvalues; ival++) { jval = (reverse_array ? nvalues - ival - 1 : ival); general_info->indices[imri][ival] = sort_array[jval].identifier; general_info->coordinates[imri][ival] = sort_array[jval].value; } /* Free the array */ FREE(sort_array); /* Update slice step and start */ if (imri == SLICE) { if (general_info->coordinates[imri][0] != general_info->coordinates[imri][nvalues-1]) { general_info->step[general_info->slice_world] = (general_info->coordinates[imri][nvalues-1] - general_info->coordinates[imri][0]) / ((double) general_info->size[imri] - 1.0); } general_info->start[general_info->slice_world] = general_info->coordinates[imri][0]; } } /* If size > 1 */ } /* Loop over dimensions */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : dimension_sort_function @INPUT : v1, v2 - values to compare @OUTPUT : (none) @RETURNS : -1, 0 or 1 if v1 < v2, v1 == v2 or v1 > v2 @DESCRIPTION: Function to compare to array elements for sorting. Elements are compared first on value, then on their original array index (this tries to preserve the original sequence). @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int dimension_sort_function(const void *v1, const void *v2) { Sort_Element *value1, *value2; value1 = (Sort_Element *) v1; value2 = (Sort_Element *) v2; if (value1->value < value2->value) return -1; else if (value1->value > value2->value) return 1; else if (value1->original_index < value2->original_index) return -1; else if (value1->original_index > value2->original_index) return 1; else return 0; } minc-tools-2.3.00+dfsg/conversion/dicomserver/save_transferred_object.c0000644000175000000620000001174712574624760025372 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : save_transferred_object.c @DESCRIPTION: Routine to save data object. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: save_transferred_object.c,v $ * Revision 6.1 1999-10-29 17:51:58 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* ----------------------------- MNI Header ----------------------------------- @NAME : save_transferred_object @INPUT : group_list - list of acr-nema groups that make up object file_prefix - prefix for file names @OUTPUT : new_file_name - name for newly created file data_info - information about data object @RETURNS : (nothing) @DESCRIPTION: Routine to save the object in a file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void save_transferred_object(Acr_Group group_list, char *file_prefix, char **new_file_name, Data_Object_Info *data_info) { Acr_Group group; Acr_Element element; char temp_name[256]; char patient_name[256]; int study_id, acquisition_id, image_id; Acr_File *afp; FILE *fp; Acr_Status status; Acr_VR_encoding_type vr_encoding; Acr_byte_order byte_order; static int file_counter = 0; /* Get the VR encoding state and byte order */ element = acr_get_group_element_list(group_list); vr_encoding = acr_get_element_vr_encoding(element); byte_order = acr_get_element_byte_order(element); /* Get data info */ get_identification_info(group_list, &(data_info->study_id), &(data_info->acq_id), &(data_info->rec_num), &(data_info->image_type)); /* Get number of echos, echo number, number of dynamic scans and dynamic_scan_number */ data_info->num_echoes = acr_find_int(group_list, SPI_Number_of_echoes, 1); data_info->echo_number = acr_find_int(group_list, ACR_Echo_number, 1); data_info->num_dyn_scans = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); data_info->dyn_scan_number = acr_find_int(group_list, ACR_Series, 1); /* Look for patient name */ element = acr_find_group_element(group_list, ACR_Patient_name); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_name, sizeof(patient_name)); } if ((element == NULL) || (strlen(patient_name) == 0)) (void) strcpy(patient_name, "unknown"); /* Look for study and image numbers */ study_id = data_info->study_id; acquisition_id = data_info->acq_id; image_id = acr_find_int(group_list, ACR_Image, 0); /* Create the new file name */ (void) sprintf(temp_name, "%s-%04d-%s_%d_%d_%d.dcm", file_prefix, file_counter++, patient_name, study_id, acquisition_id, image_id); /* Create the file and write out the data */ fp = fopen(temp_name, "w"); if (fp == NULL) { (void) fprintf(stderr, "Error opening file for write: %s\n", temp_name); } else { /* Set up the output stream */ afp = acr_file_initialize(fp, 0, acr_stdio_write); acr_set_vr_encoding(afp, vr_encoding); acr_set_byte_order(afp, byte_order); /* Loop over groups */ group = group_list; status = ACR_OK; while ((group != NULL) && (status == ACR_OK)) { /* Write out the group */ status = acr_output_group(afp, group); if (status != ACR_OK) { (void) fprintf(stderr, "Error writing file %s\n", temp_name); } group = acr_get_group_next(group); } /* Close the file */ acr_file_free(afp); (void) fclose(fp); } /* Copy the name */ *new_file_name = strdup(temp_name); return; } minc-tools-2.3.00+dfsg/conversion/dicomserver/dicomserver.c0000644000175000000620000005175412574624760023033 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomserver.c @DESCRIPTION: Program to receive images from Siemens Vision. @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicomserver.c,v $ * Revision 6.7 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.6 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.5 2001/06/26 10:19:41 neelin * Only check for children when doing forking. * * Revision 6.4 2001/03/20 16:17:01 neelin * Changed from tmpnam to tempnam so that TMPDIR can be used. * * Revision 6.3 2001/03/19 18:57:45 neelin * Added -nodaemon option to allow server to run with input from a pipe * and without forking or messing with stderr. * * Revision 6.2 2000/05/17 20:25:51 neelin * Added ability for server to suspend itself. This allows debugging even when * the server is invoked through inetd. Also added code to close file * descriptors after a fork to avoid problems with buffer flushing when * the child exits. Only STDERR is left open, and it should be line-buffered. * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.2 1997/07/10 17:35:35 neelin * Changed error handling and fixed message deletion. * * Revision 4.1 1997/07/08 23:15:09 neelin * Added support for C_ECHO command. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include /* Global for minc history */ extern char *minc_history; /* State of server. Note that DISCONNECTING is used for a high-level protocol error and TERMINATING is used for a low-level error or end of input */ typedef enum { WAITING_FOR_ASSOCIATION, WAITING_FOR_DATA, DISCONNECTING, TERMINATING } Server_state; /* Do we do logging? */ int Do_logging = #ifndef DO_HIGH_LOGGING LOW_LOGGING; #else HIGH_LOGGING; #endif /* Do we suspend ourselves? */ int Do_self_suspend = #ifndef DO_SELF_SUSPEND FALSE; #else TRUE; #endif /* Do we keep files or are they temporary? */ static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif /* In what directory do we run? */ static char *run_dir = "/var/tmp"; /* Globals for handling connection timeouts */ int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; int main(int argc, char *argv[]) { char *pname; Acr_File *afpin, *afpout; Acr_Status status; Server_state state; int acr_command; Acr_Group group_list; Acr_Message input_message, output_message; int exit_status; char exit_string[256]; char **file_list; Data_Object_Info **file_info_list; int num_files, num_files_alloc; char file_prefix[256] = "dicomserver"; char *temp_dir; int continue_looping; FILE *fptemp; char last_file_name[256]; char *project_name = NULL; char logfilename[256]; int pdu_type; int process_files, have_extra_file; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; int pres_context_id; long maximum_length; pid_t server_pid, child_pid; int statptr; int do_fork = TRUE; int reopen_log = TRUE; /* Check whether we are running as a server or not */ if (argc > 1 && strcmp(argv[1], "-nodaemon") == 0) { do_fork = FALSE; reopen_log = FALSE; run_dir = NULL; } /* Get server process id */ server_pid = getpid(); /* Suspend ourselves for debugging */ if (Do_self_suspend) { (void) kill(server_pid, SIGSTOP); } /* Change to tmp directory */ if (run_dir != NULL) { (void) chdir(run_dir); } /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* Re-open stderr if we are logging */ if (Do_logging > NO_LOGGING && reopen_log) { (void) sprintf(logfilename, "dicomserver-%d.log", (int) getpid()); (void) freopen(logfilename, "w", stderr); setbuf(stderr, NULL); } /* Print message at start */ pname = argv[0]; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "%s: Started dicom server.\n", pname); } /* Make connection */ open_connection(argc, argv, &afpin, &afpout); /* Check that the connection was made */ if ((afpin == NULL) || (afpout == NULL)) { (void) fprintf(stderr, "%s: Error opening connection.\n", pname); exit(EXIT_FAILURE); } /* Print connection message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "%s: Connection accepted.\n", pname); } #ifdef DO_INPUT_TRACING /* Enable input tracing */ acr_dicom_enable_trace(afpin); acr_dicom_enable_trace(afpout); #endif /* Create file prefix. Create the temporary file to avoid file name clashes */ temp_dir = NULL; if (! Keep_files) { temp_dir = tempnam(NULL, NULL); if (mkdir(temp_dir, (mode_t) 0777)) { (void) fprintf(stderr, "%s: Unable to create directory for temporary files.\n", pname); perror(pname); exit(EXIT_FAILURE); } (void) strcpy(file_prefix, temp_dir); (void) strcat(file_prefix, "/dicom"); } /* Get space for file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; file_list = MALLOC((size_t) num_files_alloc * sizeof(*file_list)); file_info_list = MALLOC(num_files_alloc * sizeof(*file_info_list)); /* Loop while reading messages */ state = WAITING_FOR_ASSOCIATION; continue_looping = TRUE; num_files = 0; while (continue_looping) { /* Wait for any children that have finished */ if (do_fork) { while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} /* If there are children, slow down the processing */ if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } } /* Read in the message */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status=acr_input_dicom_message(afpin, &input_message); (void) alarm(0); /* Check for error */ if (status != ACR_OK) { continue_looping = FALSE; state = TERMINATING; break; } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Get group list */ group_list = acr_get_message_group_list(input_message); /* Get PDU type. Default is data transfer */ pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); /* Deal with PDU state */ switch (pdu_type) { /* Associate request */ case ACR_PDU_ASSOC_RQ: if (state != WAITING_FOR_ASSOCIATION) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Work out reply and get connection info */ output_message = associate_reply(input_message, &project_name, &pres_context_id, &byte_order, &vr_encoding, &maximum_length); /* Modify the input and output streams according to the connection info */ acr_set_byte_order(afpin, byte_order); acr_set_vr_encoding(afpin, vr_encoding); acr_set_byte_order(afpout, byte_order); acr_set_vr_encoding(afpout, vr_encoding); acr_set_dicom_pres_context_id(afpout, pres_context_id); acr_set_dicom_maximum_length(afpout, maximum_length); /* Get ready for files */ num_files = 0; state = WAITING_FOR_DATA; break; /* Release */ case ACR_PDU_REL_RQ: if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } output_message = release_reply(input_message); state = TERMINATING; process_files = TRUE; break; /* Abort */ case ACR_PDU_ABORT_RQ: output_message = abort_reply(input_message); state = TERMINATING; break; /* Data transfer */ case ACR_PDU_DATA_TF: /* Check state */ if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Check command and compose a reply */ acr_command = acr_find_short(group_list, ACR_Command, -1); switch (acr_command) { case ACR_C_STORE_RQ: case ACR_C_ECHO_RQ: output_message = data_reply(input_message); break; default: status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Carry on only if we have a store command */ if (acr_command != ACR_C_STORE_RQ) break; /* Get rid of the command groups */ group_list = skip_command_groups(group_list); /* Was the data attached to the command? If not, read in the next message - it should contain the data */ if (group_list == NULL) { /* Delete the previous message */ if (input_message != NULL) acr_delete_message(input_message); /* Read the data and check the status */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status=acr_input_dicom_message(afpin, &input_message); (void) alarm(0); if (status != ACR_OK) { state = DISCONNECTING; break; } /* Check that we have a data PDU */ group_list = acr_get_message_group_list(input_message); if (acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF) != ACR_PDU_DATA_TF) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Skip command groups and check for no data */ group_list = skip_command_groups(group_list); if (group_list == NULL) break; } /* Extend file list if necessary */ if (num_files >= num_files_alloc) { num_files_alloc = num_files + FILE_ALLOC_INCREMENT; file_list = REALLOC(file_list, num_files_alloc * sizeof(*file_list)); file_info_list = REALLOC(file_info_list, num_files_alloc * sizeof(*file_info_list)); } file_list[num_files] = NULL; file_info_list[num_files] = MALLOC(sizeof(*file_info_list[num_files])); /* Save the object */ save_transferred_object(group_list, file_prefix, &file_list[num_files], file_info_list[num_files]); num_files++; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, " Copied %s\n", file_list[num_files-1]); } /* Check whether we have reached the end of a group of files */ if (num_files > 1) { if ((file_info_list[num_files-1]->study_id != file_info_list[0]->study_id) || (file_info_list[num_files-1]->acq_id != file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; } } break; /* Unknown command */ default: status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* End of switch on pdu_type */ /* Delete input message */ if (input_message != NULL) acr_delete_message(input_message); /* Use the files if we have a complete acquisition */ if (process_files) { /* Log the fact */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\nCopied one acquisition.\n"); } /* Check for file from next acquisition */ if (have_extra_file) num_files--; /* Fork child to process the files */ if (do_fork) { child_pid = fork(); } else { child_pid = 0; } if (child_pid > 0) { /* Parent process */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Forked process to create minc files.\n"); } } /* Error forking */ else if (child_pid < 0) { (void) fprintf(stderr, "Error forking child to create minc file\n"); return; } else { /* Child process */ /* Close file descriptors to avoid buffering problems. STDERR is left open, since it is line buffered and may be needed. */ if (do_fork) { int fd; for (fd=getdtablesize()-1; fd >= 0; fd--) { if (fd != 2) { /* Leave stderr open */ (void) close(fd); } } } /* Do something with the files */ use_the_files(project_name, num_files, file_list, file_info_list); /* Remove the temporary files */ cleanup_files(num_files, file_list); /* Remove the temporary directory if the server has finished */ if ((temp_dir != NULL) && (kill(server_pid, 0) != 0)) { cleanup_files(1, &temp_dir); } /* Print message about child finishing */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Minc creation process finished.\n"); } /* Exit from child */ if (do_fork) { exit(EXIT_SUCCESS); } } /* End of child process */ /* Put blank line in log file */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n"); } /* Reset the lists */ free_list(num_files, file_list, file_info_list); if (have_extra_file) { file_list[0] = file_list[num_files]; file_info_list[0] = file_info_list[num_files]; file_list[num_files] = NULL; file_info_list[num_files] = NULL; } num_files = (have_extra_file ? 1 : 0); } /* Check for disconnection */ if (state == DISCONNECTING) { continue_looping = FALSE; break; } /* Send reply */ Alarmed_afp = afpout; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status = acr_output_dicom_message(afpout, output_message); (void) alarm(0); /* Delete output message */ if (output_message != NULL) acr_delete_message(output_message); if (status != ACR_OK) { state = TERMINATING; break; } } /* End of loop over messages */ /* Free the input and output streams */ acr_close_dicom_file(afpin); acr_close_dicom_file(afpout); /* Save name of first file in last set transferred */ if ((num_files > 0) && (file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name, file_list[0], sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } /* Clean up files, if needed */ if (num_files > 0) { cleanup_files(num_files, file_list); free_list(num_files, file_list, file_info_list); num_files = 0; } FREE(file_list); FREE(file_info_list); /* Remove the file prefix directory (this only happens if it is empty). */ cleanup_files(1, &temp_dir); FREE(temp_dir); /* Check for connection timeout */ if (Connection_timeout) { (void) fprintf(stderr, "Connection timed out.\n"); } /* Print final message */ if ((status == ACR_OK) || (status == ACR_END_OF_INPUT)) { (void) sprintf(exit_string, "Finished transfer."); exit_status = EXIT_SUCCESS; } else { (void) sprintf(exit_string, "%s. Disconnecting.", acr_status_string(status)); exit_status = EXIT_FAILURE; } if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n%s: %s\n", pname, exit_string); } if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { if (SYSTEM_LOG != NULL) { if ((fptemp = fopen(SYSTEM_LOG, "w")) != NULL) { if ((int) strlen(last_file_name) > 0) { (void) fprintf(fptemp, "%s: File \"%s\"\n", pname, last_file_name); } (void) fprintf(fptemp, "%s: %s\n", pname, exit_string); (void) fclose(fptemp); } } } /* Free the project_name string */ if (project_name != NULL) FREE(project_name); exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : timeout_handler @INPUT : @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to handle connection timeouts. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void timeout_handler(int sig) { Connection_timeout = TRUE; if (Alarmed_afp != NULL) { acr_dicom_set_eof(Alarmed_afp); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : skip_command_groups @INPUT : group_list @OUTPUT : (none) @RETURNS : Pointer to head of group list @DESCRIPTION: Skips over command groups in a group list, returning the rest of the list. @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group skip_command_groups(Acr_Group group_list) { while ((group_list != NULL) && ((acr_get_group_group(group_list) == DCM_PDU_GRPID) || (acr_get_group_group(group_list) == ACR_MESSAGE_GID))) { group_list = acr_get_group_next(group_list); } return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver/dicom_prototypes.h0000644000175000000620000001005212574624760024103 0ustar stevestaffpublic void timeout_handler(int sig); public Acr_Group skip_command_groups(Acr_Group group_list); public void cleanup_files(int num_files, char *file_list[]); public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list); public int create_minc_file(char *minc_file, int clobber, General_Info *general_info, char *file_prefix, char **output_file_name); public void setup_minc_variables(int mincid, General_Info *general_info); public void save_minc_image(int icvid, General_Info *general_info, File_Info *file_info, Image_Data *image); public void close_minc_file(int icvid); public void open_connection(int argc, char *argv[], Acr_File **afpin, Acr_File **afpout); public int read_project_file(char *project_name, char *file_prefix, int *output_uid, int *output_gid, char *command_line, int maxlen_command); public void get_project_option_string(char *project_option_string, int maxlen_project_option); public Acr_Message associate_reply(Acr_Message input_message, char **project_name, int *pres_context_id, Acr_byte_order *byte_order, Acr_VR_encoding_type *vr_encoding, long *maximum_length); public Acr_Message associate_reply_reject(Acr_Message input_message, int reason); public Acr_Message release_reply(Acr_Message input_message); public Acr_Message abort_reply(Acr_Message input_message); public Acr_Message data_reply(Acr_Message input_message); public void save_transferred_object(Acr_Group group_list, char *file_prefix, char **new_file_name, Data_Object_Info *data_info); public void get_file_info(Acr_Group group_list, File_Info *file_info, General_Info *general_info); public void get_identification_info(Acr_Group group_list, int *study_id, int *acq_id, int *rec_num, int *image_type); public void get_intensity_info(Acr_Group group_list, File_Info *file_info); public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]); public void convert_coordinate(double coordinate[WORLD_NDIMS]); public void get_general_header_info(Acr_Group group_list, General_Info *general_info); public double convert_time_to_seconds(double dicom_time); public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image); public int siemens_dicom_to_minc(int num_files, char *file_list[], char *minc_file, int clobber, char *file_prefix, char **output_file_name); public Acr_Group read_siemens_dicom(char *filename, int max_group); public void free_info(General_Info *general_info, File_Info *file_info, int num_files); public int search_list(int value, int list[], int list_length, int starting_point); public void sort_dimensions(General_Info *general_info); public int dimension_sort_function(const void *v1, const void *v2); public void string_to_filename(char *string, char *filename, int maxlen); public void use_the_files(char *project_name, int num_files, char *file_list[], Data_Object_Info *data_info[]); minc-tools-2.3.00+dfsg/conversion/dicomserver/dicom_element_defs.h0000644000175000000620000001277012574624760024316 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_element_defs.h @DESCRIPTION: Element definitions for dicom @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ /* Define standard UID's */ #define FAVORITE_ABSTRACT_SYNTAX ACR_MR_IMAGE_STORAGE_UID #define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" #define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" #define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" #define ACR_APPLICATION_CONTEXT_UID "1.2.840.10008.3.1.1.1" /* Define constants for accepting association */ #define ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC 7 #define ACR_ASSOC_RJ_NO_REASON 1 #define ACR_ASSOC_RJ_PERM 1 #define ACR_ASSOC_RJ_USER 1 #define ACR_ASSOC_PR_CN_ACCEPT 0 #define ACR_ASSOC_PR_CN_REJECT 1 #define ACR_PDU_ITEM_USER_INFORMATION 0x50 /* Define group numbers */ #define ACR_MESSAGE_GID 0 #define ACR_ACTUAL_IMAGE_GID 0x7fe0 /* Define commands */ #define ACR_C_STORE_RQ 0x0001 #define ACR_C_STORE_RSP 0x8001 #define ACR_C_ECHO_RQ 0x0030 #define ACR_C_ECHO_RSP 0x8030 /* Define dataset type */ #define ACR_NULL_DATASET 0x0101 /* Define status codes */ #define ACR_SUCCESS 0x0000 /* Define data object types */ #define ACR_IMAGE_OBJECT 0x0000 #define ACR_OTHER_OBJECT 0x0100 /* Define acr-nema constants */ #define ACR_MODALITY_MR "MR" /* Element id's for DICOM */ GLOBAL_ELEMENT(ACR_Affected_SOP_class_UID , 0x0000, 0x0002, UI); GLOBAL_ELEMENT(ACR_Command , 0x0000, 0x0100, US); GLOBAL_ELEMENT(ACR_Message_id , 0x0000, 0x0110, US); GLOBAL_ELEMENT(ACR_Message_id_brt , 0x0000, 0x0120, US); GLOBAL_ELEMENT(ACR_Priority , 0x0000, 0x0700, US); GLOBAL_ELEMENT(ACR_Dataset_type , 0x0000, 0x0800, US); GLOBAL_ELEMENT(ACR_Status , 0x0000, 0x0900, US); GLOBAL_ELEMENT(ACR_Affected_SOP_instance_UID , 0x0000, 0x1000, UI); GLOBAL_ELEMENT(ACR_Move_originator_AE_title , 0x0000, 0x1031, AE); GLOBAL_ELEMENT(ACR_Study_date , 0x0008, 0x0020, DA); GLOBAL_ELEMENT(ACR_Acquisition_date , 0x0008, 0x0022, DA); GLOBAL_ELEMENT(ACR_Study_time , 0x0008, 0x0030, TM); GLOBAL_ELEMENT(ACR_Acquisition_time , 0x0008, 0x0032, TM); GLOBAL_ELEMENT(ACR_Modality , 0x0008, 0x0060, CS); GLOBAL_ELEMENT(ACR_Manufacturer , 0x0008, 0x0070, LO); GLOBAL_ELEMENT(ACR_Institution_id , 0x0008, 0x0080, LO); GLOBAL_ELEMENT(ACR_Referring_physician , 0x0008, 0x0090, PN); GLOBAL_ELEMENT(ACR_Station_id , 0x0008, 0x1010, SH); GLOBAL_ELEMENT(ACR_Procedure_description , 0x0008, 0x1030, LO); GLOBAL_ELEMENT(ACR_Manufacturer_model , 0x0008, 0x1090, LO); GLOBAL_ELEMENT(ACR_Patient_name , 0x0010, 0x0010, PN); GLOBAL_ELEMENT(ACR_Patient_identification, 0x0010, 0x0020, LO); GLOBAL_ELEMENT(ACR_Patient_birth_date , 0x0010, 0x0030, DA); GLOBAL_ELEMENT(ACR_Patient_sex , 0x0010, 0x0040, CS); GLOBAL_ELEMENT(ACR_Patient_weight , 0x0010, 0x1030, DS); GLOBAL_ELEMENT(ACR_Scanning_sequence , 0x0018, 0x0020, CS); GLOBAL_ELEMENT(ACR_Slice_thickness , 0x0018, 0x0050, DS); GLOBAL_ELEMENT(ACR_Repetition_time , 0x0018, 0x0080, DS); GLOBAL_ELEMENT(ACR_Echo_time , 0x0018, 0x0081, DS); GLOBAL_ELEMENT(ACR_Inversion_time , 0x0018, 0x0082, DS); GLOBAL_ELEMENT(ACR_Nr_of_averages , 0x0018, 0x0083, DS); GLOBAL_ELEMENT(ACR_Imaging_frequency , 0x0018, 0x0084, DS); GLOBAL_ELEMENT(ACR_Imaged_nucleus , 0x0018, 0x0085, SH); GLOBAL_ELEMENT(ACR_Echo_number , 0x0018, 0x0086, IS); GLOBAL_ELEMENT(ACR_Flip_angle , 0x0018, 0x1314, DS); GLOBAL_ELEMENT(ACR_Acq_comments , 0x0018, 0x4000, LT); GLOBAL_ELEMENT(ACR_Study , 0x0020, 0x0010, SH); GLOBAL_ELEMENT(ACR_Series , 0x0020, 0x0011, IS); GLOBAL_ELEMENT(ACR_Acquisition , 0x0020, 0x0012, IS); GLOBAL_ELEMENT(ACR_Image , 0x0020, 0x0013, IS); GLOBAL_ELEMENT(ACR_Acquisitions_in_series, 0x0020, 0x1001, IS); GLOBAL_ELEMENT(ACR_Rows , 0x0028, 0x0010, US); GLOBAL_ELEMENT(ACR_Columns , 0x0028, 0x0011, US); GLOBAL_ELEMENT(ACR_Pixel_size , 0x0028, 0x0030, DS); GLOBAL_ELEMENT(ACR_Bits_allocated , 0x0028, 0x0100, US); GLOBAL_ELEMENT(ACR_Bits_stored , 0x0028, 0x0101, US); GLOBAL_ELEMENT(ACR_Smallest_pixel_value , 0x0028, 0x0106, US); GLOBAL_ELEMENT(ACR_Largest_pixel_value , 0x0028, 0x0107, US); GLOBAL_ELEMENT(ACR_Image_location , 0x0028, 0x0200, US); GLOBAL_ELEMENT(ACR_Window_centre , 0x0028, 0x1050, DS); GLOBAL_ELEMENT(ACR_Window_width , 0x0028, 0x1051, DS); GLOBAL_ELEMENT(ACR_Image_data, ACR_ACTUAL_IMAGE_GID, 0x0010, UNKNOWN); #include minc-tools-2.3.00+dfsg/conversion/dicomserver/spi_element_defs.h0000644000175000000620000000366512574624760024021 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : spi_element_defs.h @DESCRIPTION: Element definitions for spi @METHOD : @GLOBALS : @CALLS : @CREATED : November 23, 1993 (Peter Neelin) @MODIFIED : @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. ---------------------------------------------------------------------------- */ /* Element number for images */ #define SPI_IMAGE_ELEMENT 0x0010 /* Define spi constants */ #define SPI_TRANSVERSE_ORIENTATION 1 #define SPI_SAGITTAL_ORIENTATION 2 #define SPI_CORONAL_ORIENTATION 3 /* Element id's for SPI */ GLOBAL_ELEMENT(SPI_Sequence_file_name , 0x0019, 0x1511, LO); GLOBAL_ELEMENT(SPI_Image_position , 0x0021, 0x1160, DS); GLOBAL_ELEMENT(SPI_Image_normal , 0x0021, 0x1161, DS); GLOBAL_ELEMENT(SPI_Image_row , 0x0021, 0x116a, DS); GLOBAL_ELEMENT(SPI_Image_column , 0x0021, 0x116b, DS); GLOBAL_ELEMENT(SPI_Number_of_3D_raw_partitions_nominal, 0x0021, 0x1330, IS); GLOBAL_ELEMENT(SPI_Actual_3D_partition_number , 0x0021, 0x1336, IS); GLOBAL_ELEMENT(SPI_Number_of_slices_nominal , 0x0021, 0x1340, IS); GLOBAL_ELEMENT(SPI_Current_slice_number , 0x0021, 0x1342, IS); GLOBAL_ELEMENT(SPI_Number_of_echoes , 0x0021, 0x1370, IS); minc-tools-2.3.00+dfsg/conversion/dicomserver/Makefile0000644000175000000620000000202112574624760021764 0ustar stevestaff# -------------------------------------------------------------------- # # MINC Makefile # ROOT = ../../minc ACR_LIB_DIR = ../Acr_nema include $(ACR_LIB_DIR)/Make_acrdefs include $(ROOT)/Make_machine_specific include $(ROOT)/Make_configuration # Executable names PROGS = dicomserver dicomserver-debug EXTRA_OBJS = dicom_element_defs.o open_connection.o reply.o \ save_transferred_object.o use_the_files.o \ siemens_dicom_to_minc.o siemens_dicom_read.o minc_file.o \ string_to_filename.o project_file.o HEADERS = dicomserver.h dicom_prototypes.h dicom_element_defs.h \ spi_element_defs.h siemens_dicom_to_minc.h CDEFINES = -DDEBUG# cpp defines INCLUDES = -I/usr/include -I. -I$(ACR_LIB_DIR) -I$(PROG_LIB_DIR) \ -I$(VOLIO_LIB_DIR)/Include \ -I$(MINC_LIB_DIR) -I$(NETCDF_INCLUDE_DIR) LDOPT = $(CC_ACR_LIB) $(PROG_LDOPT) MANSECT = 1 #MANPAGES = $(PROGS).$(MANSECT) include $(ROOT)/progs/Make_progs dicomserver-debug.o: dicomserver.c minc-tools-2.3.00+dfsg/conversion/dicomserver/project_file.c0000644000175000000620000001655512574624760023156 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : project_file.c @DESCRIPTION: Code to do manipulate the project files (files containing info on what to do with files for each project). @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: project_file.c,v $ * Revision 6.2 2002-12-07 13:02:28 neelin * Fixed prototype for gethostname * * Revision 6.1 1999/10/29 17:51:56 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include /* Function prototypes */ int gethostname (char *name, size_t namelen); /* ----------------------------- MNI Header ----------------------------------- @NAME : read_project_file @INPUT : project_name - name to use for project file @OUTPUT : file_prefix - string used as prefix for output files (can be NULL) output_uid - uid for created files (can be NULL). Set to INT_MIN if file not found. output_gid - gid for created files (can be NULL). Set to INT_MIN if file not found. command_line - command to execute on new file (can be NULL) maxlen_command - maximum length for command_line @RETURNS : TRUE if an error occurs, FALSE otherwise. @DESCRIPTION: Routine to read in default information for a given project. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int read_project_file(char *project_name, char *file_prefix, int *output_uid, int *output_gid, char *command_line, int maxlen_command) { char project_string[256]; char output_default_file[256]; char temp_file_prefix[256]; int temp_uid, temp_gid; char temp_command_line[4]; int ichar, ochar; int length, index; FILE *fp; char string[512]; int project_name_given; /* Check that the user actually wants return values */ if (file_prefix == NULL) file_prefix = temp_file_prefix; if (output_uid == NULL) output_uid = &temp_uid; if (output_gid == NULL) output_gid = &temp_gid; if ((command_line == NULL) || (maxlen_command <= 0)) { command_line = temp_command_line; maxlen_command = sizeof(temp_command_line); } /* Set some default values */ file_prefix[0] = '\0'; command_line[0] = '\0'; *output_uid = *output_gid = INT_MIN; /* Copy the project name, removing spaces */ if (project_name != NULL) length = strlen(project_name); else length = 0; for (ichar=0, ochar=0; (ichar < length) && (ochar < sizeof(project_string)-1); ichar++) { if (isprint((int) project_name[ichar]) && !isspace((int) project_name[ichar])) { project_string[ochar] = (char) toupper((int) project_name[ichar]); ochar++; } } project_string[ochar] = '\0'; /* Get the host name if there is no project string */ project_name_given = (strlen(project_string) > (size_t) 0); if (!project_name_given) (void) gethostname(project_string, sizeof(project_string) - 1); (void) sprintf(output_default_file, "%s/%s%s", OUTPUT_DEFAULT_FILE_DIR, OUTPUT_DEFAULT_FILE_PREFIX, project_string); /* Open and read the defaults file - if it isn't there then return TRUE if the caller gave a project name */ if ((fp=fopen(output_default_file, "r")) == NULL) { return project_name_given; } if (fgets(string, (int) sizeof(string), fp) == NULL) { return TRUE; } if (sscanf(string, "%s %d %d", file_prefix, output_uid, output_gid) != 3) { return TRUE; } (void) fgets(command_line, maxlen_command, fp); index = strlen(command_line) - 1; if ((index >= 0) && (command_line[index] == '\n')) command_line[index] = '\0'; (void) fclose(fp); return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_project_option_string @INPUT : (none) @OUTPUT : project_option_string - string containing list of options for project name maxlen_project_option - maximum length for the string (including '\0' at end) @RETURNS : (nothing) @DESCRIPTION: Routine to get a list of possibilities for the project name (looking for appropriately named files). @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_project_option_string(char *project_option_string, int maxlen_project_option) { DIR *dirp; struct dirent *dp; int length; char *name, *filler; int compare_length; /* Set up the string */ if (maxlen_project_option > 0) { project_option_string[0] = '\0'; length = 1; } /* Open directory */ if ((dirp = opendir(OUTPUT_DEFAULT_FILE_DIR)) == NULL) return; /* Loop through directory entries */ compare_length = strlen(OUTPUT_DEFAULT_FILE_PREFIX); while ((dp = readdir(dirp)) != NULL) { /* Check for an entry with the right prefix */ if (strncmp(OUTPUT_DEFAULT_FILE_PREFIX, dp->d_name, compare_length) == 0) { /* Check for an uppercase letter */ if ((strlen(dp->d_name) > (size_t) compare_length) && (isupper(dp->d_name[compare_length]))) { name = &dp->d_name[compare_length]; /* Check that we can read the project file */ if (!read_project_file(name, NULL, NULL, NULL, NULL, 0)) { if (length > 1) filler = ", "; else filler = ""; if ((strlen(name) + length + strlen(filler)) < (size_t) maxlen_project_option) { (void) strcat(strcat(project_option_string, filler), name); length += strlen(filler) + strlen(name); } } /* We can read the project file */ } /* Found uppercase letter */ } /* Found file matching prefix */ } /* Loop over files */ } minc-tools-2.3.00+dfsg/conversion/dicomserver/reply.c0000644000175000000620000004670212574624760021641 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : reply.c @DESCRIPTION: Routines for dealing with dicom messages. @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: reply.c,v $ * Revision 6.4 2002-12-08 20:45:34 neelin * Added new implementation class uid for Siemens MREASE_VA21A * * Revision 6.3 1999/10/29 17:51:57 neelin * Fixed Log keyword * * Revision 6.2 1999/08/05 20:01:16 neelin * Check for broken Siemens software using a list of implementation UIDs. * * Revision 6.1 1998/05/19 19:27:43 neelin * Test for Siemens Vision machine by looking for implementation uid * rather than AE title * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/08 23:15:09 neelin * Added support for C_ECHO command. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; /* List of implementation UIDs for Siemens Vision scanners with broken handling of transfer syntax. These should be in ascending order of software version, since the UID for version VB33A is used to identify a change in element use. */ static char *SPI_Vision_Implementation_UIDs[] = { "2.16.840.1.113669.2.931128", "1.3.12.2.1107.5.1995.1", /* Version VB33A */ "1.3.12.2.1107.5.2", /* Version MREASE_VA21A */ NULL }; /* Index into above array for version VB33A */ #define SPI_VISION_VB33A_INDEX 1 /* Global to indicate whether we have a pre VB33A version */ int SPI_Vision_version_pre33A = TRUE; /* Macros */ #define SAVE_SHORT(group, elid, value) \ acr_group_add_element(group, \ acr_create_element_short(elid, (unsigned short) (value))) /* ----------------------------- MNI Header ----------------------------------- @NAME : uid_equal @INPUT : uid1 uid2 @OUTPUT : (nothing) @RETURNS : TRUE if uid's are equal, FALSE otherwise @DESCRIPTION: Responds to READYq message @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private int uid_equal(char *uid1, char *uid2) { int len1, len2, i; len1 = strlen(uid1); len2 = strlen(uid2); /* Skip leading blanks */ while (isspace(*uid1)) {uid1++;} while (isspace(*uid2)) {uid2++;} /* Skip trailing blanks */ for (i=len1-1; (i >= 0) && isspace(uid1[i]); i++) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; for (i=len2-1; (i >= 0) && isspace(uid1[i]); i++) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; /* Compare the strings */ return (strcmp(uid1, uid2) == 0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_message @INPUT : group_list @OUTPUT : (nothing) @RETURNS : output message. @DESCRIPTION: Convert a group list into a message. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private Acr_Message make_message(Acr_Group group_list) { Acr_Group next_group, group; Acr_Message output_message; /* Create the output message */ output_message = acr_create_message(); /* Loop through groups, adding them to the message */ group = group_list; while (group != NULL) { next_group = acr_get_group_next(group); acr_set_group_next(group, NULL); acr_message_add_group(output_message, group); group = next_group; } return output_message; } /* ----------------------------- MNI Header ----------------------------------- @NAME : associate_reply @INPUT : input_message @OUTPUT : project_name - name to use for project file pres_context_id - presentation context id to use for output. If this is < 0, then an error occurred. byte_order - byte order to use for messages vr_encoding - VR encoding to use for messages maximum_length - maximum length of output PDU's @RETURNS : output_message @DESCRIPTION: Responds to an associate request message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message associate_reply(Acr_Message input_message, char **project_name, int *pres_context_id, Acr_byte_order *byte_order, Acr_VR_encoding_type *vr_encoding, long *maximum_length) { Acr_Group group, input_group; Acr_Element element, item, subitem, sublist; Acr_Element out_item, out_subitem, out_sublist, out_list; int found_best; int best_pres_context_id, cur_pres_context_id; int best_transfer_syntax_priority; int use_implicit_little_endian; char *best_abstract_syntax, *best_transfer_syntax, *cur_syntax; int impuid; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived associate request message:\n"); acr_dump_message(stderr, input_message); } /* Set default presentation context id to flag error */ *pres_context_id = -1; /* Free project_name string if needed */ if (*project_name != NULL) FREE(*project_name); /* Get the group list */ input_group = acr_get_message_group_list(input_message); /* Get the project name from the DICOM application name */ *project_name = strdup(acr_find_string(input_group, DCM_PDU_Called_Ap_title, "")); /* Check that the project file is okay. If it is not found, try the host name */ if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)) { FREE(*project_name); *project_name = NULL; if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)) { return associate_reply_reject(input_message, ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC); } } /* Check for Siemens Vision implementation that lies about its transfer syntaxes */ use_implicit_little_endian = FALSE; for (impuid=0; SPI_Vision_Implementation_UIDs[impuid] != NULL; impuid++) { if (uid_equal(acr_find_string(input_group, DCM_PDU_Implementation_class_uid, ""), SPI_Vision_Implementation_UIDs[impuid])) { use_implicit_little_endian = TRUE; SPI_Vision_version_pre33A = (impuid < SPI_VISION_VB33A_INDEX); break; } } /* Get maximum length */ *maximum_length = acr_find_long(input_group, DCM_PDU_Maximum_length, 0L); /* Get presentation context list */ best_pres_context_id = -1; best_abstract_syntax = NULL; element = acr_find_group_element(input_group, DCM_PDU_Presentation_context_list); if ((element == NULL) || !acr_element_is_sequence(element)) { (void) fprintf(stderr, "No presentation context list found\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Loop over presentation contexts */ found_best = FALSE; for (item = (Acr_Element) acr_get_element_data(element); (item != NULL) && acr_element_is_sequence(item) && !found_best; item = acr_get_element_next(item)) { /* Get presentation context info */ sublist = (Acr_Element) acr_get_element_data(item); /* Get abstract syntax */ subitem = acr_find_element_id(sublist, DCM_PDU_Abstract_syntax); if (subitem == NULL) continue; cur_syntax = acr_get_element_string(subitem); /* Check whether this is either MR abstract syntax or we have already found one (we take the first one if we cannot find the one that we want) */ if (uid_equal(cur_syntax, FAVORITE_ABSTRACT_SYNTAX)) found_best = TRUE; else if (best_abstract_syntax != NULL) continue; /* Save the abstract syntax */ best_abstract_syntax = cur_syntax; /* Get presentation context id */ subitem = acr_find_element_id(sublist, DCM_PDU_Presentation_context_id); if (subitem != NULL) best_pres_context_id = acr_get_element_short(subitem); else { (void) fprintf(stderr, "No presentation context - internal error\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Look for an appropriate transfer syntax */ best_transfer_syntax = NULL; best_transfer_syntax_priority = 0; for (subitem = sublist; subitem != NULL; subitem=acr_find_element_id(acr_get_element_next(subitem), DCM_PDU_Transfer_syntax)) { /* Check for syntaxes in descending order of preference */ cur_syntax = acr_get_element_string(subitem); if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { if (best_transfer_syntax_priority < 3) { best_transfer_syntax_priority = 3; best_transfer_syntax = cur_syntax; } } else if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { if (best_transfer_syntax_priority < 2) { best_transfer_syntax_priority = 2; best_transfer_syntax = cur_syntax; } } else if (uid_equal(cur_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { if (best_transfer_syntax_priority < 1) { best_transfer_syntax_priority = 1; best_transfer_syntax = cur_syntax; } } } /* Loop over transfer syntaxes */ } /* Loop over presentation contexts */ /* Check for machines that lie about their ability to do different transfer syntaxes */ if (use_implicit_little_endian) { best_transfer_syntax = ACR_IMPLICIT_VR_LITTLE_END_UID; } /* Check that we found something useful */ if ((best_pres_context_id < 0) || (best_abstract_syntax == NULL) || (best_transfer_syntax == NULL)) { (void) fprintf(stderr, "Did not find understandable presentation context\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Set the transfer syntax information */ if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { *byte_order = ACR_BIG_ENDIAN; *vr_encoding = ACR_EXPLICIT_VR; } else if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { *byte_order = ACR_LITTLE_ENDIAN; *vr_encoding = ACR_EXPLICIT_VR; } else if (uid_equal(best_transfer_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { *byte_order = ACR_LITTLE_ENDIAN; *vr_encoding = ACR_IMPLICIT_VR; } else { (void) fprintf(stderr, "Did not understand transfer syntax.\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_AC); /* Save the caller and calling AE titles */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Called_Ap_title, acr_find_string(input_group, DCM_PDU_Called_Ap_title, ""))); acr_group_add_element(group, acr_create_element_string(DCM_PDU_Calling_Ap_title, acr_find_string(input_group, DCM_PDU_Calling_Ap_title, ""))); /* Add the application context name */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Application_context, acr_find_string(input_group, DCM_PDU_Application_context, ACR_APPLICATION_CONTEXT_UID))); /* Loop over presentation contexts */ item = (Acr_Element) acr_get_element_data (acr_find_group_element(input_group, DCM_PDU_Presentation_context_list)); out_list = NULL; for (;(item != NULL); item = acr_get_element_next(item)) { if (!acr_element_is_sequence(item)) continue; /* Get presentation context info */ sublist = (Acr_Element) acr_get_element_data(item); /* Save the id */ subitem = acr_find_element_id(sublist, DCM_PDU_Presentation_context_id); if (subitem == NULL) continue; cur_pres_context_id = acr_get_element_short(subitem); /* Create the presentation context */ out_sublist = NULL; out_subitem = acr_create_element_short(DCM_PDU_Presentation_context_id, cur_pres_context_id); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Accept or reject */ out_subitem = acr_create_element_short(DCM_PDU_Result, ((cur_pres_context_id == best_pres_context_id) ? ACR_ASSOC_PR_CN_ACCEPT : ACR_ASSOC_PR_CN_REJECT)); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Add the transfer syntax */ out_subitem = acr_create_element_string(DCM_PDU_Transfer_syntax, best_transfer_syntax); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Add this context to the list */ out_item = acr_create_element_sequence(DCM_PDU_Presentation_context_reply, out_sublist); out_list = acr_element_list_add(out_list, out_item); } /* Create the presentation context list element */ element = acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list, out_list); acr_group_add_element(group, element); /* Add the user information */ acr_group_add_element(group, acr_create_element_long(DCM_PDU_Maximum_length, 0L)); /* Set the presentation context id to indicate success */ *pres_context_id = best_pres_context_id; return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : associate_reply_reject @INPUT : input_message @OUTPUT : reason @RETURNS : output_message @DESCRIPTION: Responds to an associate request message with a rejection @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public Acr_Message associate_reply_reject(Acr_Message input_message, int reason) { Acr_Group group; /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_RJ); /* Give the result, source and reason */ SAVE_SHORT(group, DCM_PDU_Result, ACR_ASSOC_RJ_PERM); SAVE_SHORT(group, DCM_PDU_Source, ACR_ASSOC_RJ_USER); SAVE_SHORT(group, DCM_PDU_Reason, reason); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : release_reply @INPUT : input_message @OUTPUT : (nothing) @RETURNS : output_message @DESCRIPTION: Responds to READYq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message release_reply(Acr_Message input_message) { Acr_Group group; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived release request message:\n"); acr_dump_message(stderr, input_message); } /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_REL_RP); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : abort_reply @INPUT : input_message @OUTPUT : (nothing) @RETURNS : output_message @DESCRIPTION: Responds to GCENDq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message abort_reply(Acr_Message input_message) { Acr_Group group; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived abort message:\n"); acr_dump_message(stderr, input_message); } /* Create the reply */ group = acr_copy_group_list(acr_get_message_group_list(input_message)); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : data_reply @INPUT : input_message file_prefix @OUTPUT : new_file_name (must be freed by caller) @RETURNS : output_message @DESCRIPTION: Responds to SENDq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message data_reply(Acr_Message input_message) { Acr_Group group, input_group; int reply_command; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived data message:\n"); acr_dump_message(stderr, input_message); } /* Get the input group list */ input_group = acr_get_message_group_list(input_message); /* Figure out the reply that we need */ switch (acr_find_short(input_group, ACR_Command, -1)) { case ACR_C_STORE_RQ: reply_command = ACR_C_STORE_RSP; break; case ACR_C_ECHO_RQ: reply_command = ACR_C_ECHO_RSP; break; default: reply_command = ACR_C_ECHO_RSP; break; } /* Create the reply */ group = acr_create_group(ACR_MESSAGE_GID); /* Save appropriate stuff */ acr_group_add_element(group, acr_create_element_string(ACR_Affected_SOP_class_UID, acr_find_string(input_group, ACR_Affected_SOP_class_UID, ""))); SAVE_SHORT(group, ACR_Command, reply_command); SAVE_SHORT(group, ACR_Message_id_brt, acr_find_short(input_group, ACR_Message_id, 0)); SAVE_SHORT(group, ACR_Dataset_type, ACR_NULL_DATASET); SAVE_SHORT(group, ACR_Status, ACR_SUCCESS); if (reply_command == ACR_C_STORE_RSP) { acr_group_add_element(group, acr_create_element_string(ACR_Affected_SOP_instance_UID, acr_find_string(input_group, ACR_Affected_SOP_instance_UID, ""))); } return make_message(group); } minc-tools-2.3.00+dfsg/conversion/dicomserver/siemens_dicom_to_minc.h0000644000175000000620000001133312574624760025031 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc.h @DESCRIPTION: Header file for siemens_dicom_to_minc.h @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_to_minc.h,v $ * Revision 6.1 1999-10-29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* General constants */ #define SECONDS_PER_MINUTE 60 #define MINUTES_PER_HOUR 60 #define SECONDS_PER_HOUR (MINUTES_PER_HOUR*SECONDS_PER_MINUTE) #define HOURS_PER_DAY 24 #define SECONDS_PER_DAY (HOURS_PER_DAY*SECONDS_PER_HOUR) #define MS_PER_SECOND 1000 #define COORDINATE_EPSILON (100.0*FLT_EPSILON) /* Default value for ncopts */ #define NCOPTS_DEFAULT NC_VERBOSE /* MINC variable for dicom elements */ #define DICOM_ROOT_VAR "dicom_groups" /* Possible MRI dimensions */ typedef enum { SLICE = 0, ECHO, TIME, PHASE, CHEM_SHIFT, MRI_NDIMS } Mri_Index; /* World dimensions */ typedef enum { XCOORD = 0, YCOORD, ZCOORD, WORLD_NDIMS } World_Index; /* Volume dimensions */ typedef enum { VSLICE = 0, VROW, VCOLUMN, VOL_NDIMS } Volume_Index; /* Orientations */ typedef enum {TRANSVERSE = 0, SAGITTAL, CORONAL, NUM_ORIENTATIONS} Orientation; /* String type */ typedef char Cstring[256]; /* Structure for general info about files */ typedef struct { int initialized; int study_id; int acq_id; /* Time of scan */ int rec_num; int image_type; Cstring image_type_string; int nrows; int ncolumns; int default_index[MRI_NDIMS]; /* Index for dimensions with size == 1 */ int size[MRI_NDIMS]; /* Size of dimension across these files */ int total_size[MRI_NDIMS]; /* Size of dimension across acquisition */ int *indices[MRI_NDIMS]; /* List of indices found for each dimension. Only allocated when size > 1 */ int search_start[MRI_NDIMS]; /* Indices into lists for starting searches */ double *coordinates[MRI_NDIMS]; /* Array indicating coordinate of each index in indices array */ int image_index[MRI_NDIMS]; /* Mapping from MRI dim to output image dim */ World_Index slice_world; World_Index row_world; World_Index column_world; double step[WORLD_NDIMS]; double start[WORLD_NDIMS]; double dircos[WORLD_NDIMS][WORLD_NDIMS]; nc_type datatype; int is_signed; double pixel_min; double pixel_max; Cstring units; double window_min; double window_max; struct { Cstring name; Cstring identification; Cstring birth_date; Cstring sex; double weight; } patient; struct { Cstring start_time; Cstring modality; Cstring manufacturer; Cstring model; Cstring institution; Cstring station_id; Cstring ref_physician; Cstring procedure; Cstring study_id; Cstring acquisition_id; } study; struct { Cstring scan_seq; double rep_time; double echo_time; double inv_time; double flip_angle; double num_avg; double imaging_freq; Cstring imaged_nucl; Cstring comments; } acq; Acr_Group group_list; } General_Info; /* Structure for file-specific info */ typedef struct { int valid; int bits_alloc; int bits_stored; int index[MRI_NDIMS]; double pixel_max; double pixel_min; double slice_max; double slice_min; double window_max; double window_min; double coordinate[MRI_NDIMS]; } File_Info; /* Structure for image data */ typedef struct { int free; int nrows; int ncolumns; unsigned short *data; } Image_Data; /* Structure for sorting dimensions */ typedef struct { int identifier; int original_index; double value; } Sort_Element; minc-tools-2.3.00+dfsg/conversion/dicomserver/dicom_element_defs.c0000644000175000000620000000201112574624760024274 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_element_defs.c @DESCRIPTION: Element definitions for DICOM @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ #define GLOBAL_ELEMENT_DEFINITION #include minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/0002755000175000000620000000000012601667642021675 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/open_connection.c0000644000175000000620000001537012574624760025230 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : open_connection.c @DESCRIPTION: File containing routines to open a decnet connection. @GLOBALS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : * $Log: open_connection.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:56 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * * Revision 3.0 1995/05/15 19:31:44 neelin * Release of minc version 0.3 * * Revision 2.5 1995/02/14 18:12:26 neelin * Added project names and defaults files (using volume name). * Added process id to log file name. * Moved temporary files to subdirectory. * * Revision 2.4 1995/02/09 13:51:26 neelin * Mods for irix 5 lint. * * Revision 2.3 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 2.2 1994/12/07 09:45:59 neelin * Fixed called to ioctl to get rid of type mismatch warning messages. * * Revision 2.1 94/12/07 08:20:10 neelin * Added support for irix 5 decnet. * * Revision 2.0 94/09/28 10:35:32 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:34:50 neelin * Pre-release * * Revision 1.4 94/01/18 14:23:41 neelin * Changed bzero to memset. * * Revision 1.3 93/11/30 14:42:13 neelin * Copies to minc format. * * Revision 1.2 93/11/25 13:26:55 neelin * Working version. * * Revision 1.1 93/11/23 14:11:54 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 #include #include #include #include #include #include #include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : connection_okay @INPUT : sockfd - input file descriptor which might be a socket @OUTPUT : (none) @RETURNS : TRUE if connection is okay, FALSE otherwise @DESCRIPTION: Checks whether the connection is allowed. Looks at sockfd to find out if remote host is allowed to connect. If sockfd is a file and not a socket, then the connection is allowed. @METHOD : @GLOBALS : @CALLS : @CREATED : February 20, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private int connection_okay(int sockfd) { struct sockaddr_in us, them; int status; int namelen; extern int Do_logging; /* Get our own id. If sockfd is a file, then its okay. Check that we have an internet connection. */ namelen = sizeof(us); if (getsockname(sockfd, &us, &namelen) != 0) { if (errno == ENOTSOCK) return TRUE; else { (void) fprintf(stderr, "Unable to get our own host address.\n"); return FALSE; } } else if (us.sin_family != AF_INET) { (void) fprintf(stderr, "Connection is not from network.\n"); return FALSE; } /* Try to get id of host at other end of connection */ namelen = sizeof(us); status = getpeername(sockfd, &them, &namelen); if (status != 0) { (void) fprintf(stderr, "Unable to check connection source.\n"); return FALSE; } /* */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Connection from %s ", inet_ntoa(them.sin_addr)); } /* modified by rhoge to relax network restriction from class C to B */ /* Compare the addresses. Make sure that we have the same IP domain assuming class B structure. */ if ((us.sin_addr.s_addr & IN_CLASSB_NET) != (them.sin_addr.s_addr & IN_CLASSB_NET)) { if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr,"Request not from same IP domain (class B)\n"); (void) fprintf(stderr,"Our ip address: %d\n",us.sin_addr.s_addr); (void) fprintf(stderr,"Their ip address: %d\n",them.sin_addr.s_addr); (void) fprintf(stderr,"CLASSB mask: %d\n",IN_CLASSB_NET); (void) fprintf(stderr, "Connection from %s ", inet_ntoa(them.sin_addr)); (void) fprintf(stderr, "refused.\n"); } /* return FALSE; */ } /* Log a warning if hosts not from same class C network */ if ((us.sin_addr.s_addr & IN_CLASSC_NET) != (them.sin_addr.s_addr & IN_CLASSC_NET)) { if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr,"Request not from same IP domain (class C)\n"); (void) fprintf(stderr, "Connection from %s ", inet_ntoa(them.sin_addr)); } } if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "accepted.\n"); } return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_connection @INPUT : argc - number of command-line arguments argv - array of command-line arguments @OUTPUT : afpin - Acr file pointer for input afpout - Acr file pointer for output @RETURNS : (nothing) @DESCRIPTION: Opens the connection for reading writing dicom messages. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void open_connection(int argc, char *argv[], Acr_File **afpin, Acr_File **afpout) { /* Set default file pointers */ *afpin = *afpout = NULL; /* Check for a valid connection */ if (!connection_okay(fileno(stdin))) return; /* Open the connection */ *afpin=acr_initialize_dicom_input(stdin, 0, acr_stdio_read); *afpout=acr_initialize_dicom_output(stdout, 0, acr_stdio_write); /* Ignore SIGPIPE errors in case connection gets closed when we are doing output */ (void) signal(SIGPIPE, SIG_IGN); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_dicom_read.c0000644000175000000620000012724512574624760025666 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_read.c @DESCRIPTION: Code to read siemens dicom files and get info from them. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_read.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.12 2002/05/01 21:29:34 rhoge * removed MrProt from minc header - encountered files with large strings, * causing seg faults * * Revision 1.11 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.10 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.9 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.8 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.7 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.6 2000/12/14 21:33:13 rhoge * code modifications to restore measurement loop support that was broken * by changes to support acqusition loop scanning * * Revision 1.5 2000/12/13 13:25:36 rhoge * removed dummy printf from convert_time_to_seconds - turns out that * buggy behaviour was an optimization problem * * Revision 1.4 2000/12/12 19:27:52 rhoge * changed atof(acr_find_string) back to acr_find_double after realizing * that these functions assume string representation * * Revision 1.3 2000/12/12 14:43:22 rhoge * fixed syntax error (dangling comment symbol) from removal of debug * printfs - compiles now * * Revision 1.2 2000/12/11 20:01:44 rhoge * fixed code for frame time computation - ACR vals are strings. Also * inserted dummy fprintf statement in convert_time_to_seconds as this * seems to salvage Linux problems * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * -now always use ACR_Series for run number (seemed to be * problems with test introduced in 6.1) * -added code to detect use of acquisition loop and also to handle * `corrected' siemens acq loop scans * -changed code to use registration time instead of scan time for * session id * -got rid of extraneous acquisition id digit * -added technical information about data acquisition * * Revision 6.2 1999/10/29 17:51:58 neelin * Fixed Log keyword * * Revision 6.1 1999/08/05 20:00:34 neelin * Get acquisition id from series or study element, depending on the * version of the Siemens software. * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.1 1997/09/10 19:36:13 neelin * Small fix to set default direction cosines when they are absent from the * dicom data. * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.1 1997/06/13 12:51:21 neelin * Changed definition of time index and acquisition id to match change * in Siemens dicom software. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int SPI_Vision_version_pre33A; File_Type file_type; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_info @INPUT : group_list - input data @OUTPUT : file_info - file-specific info general_info - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : Modified Feb. 2000 by Rick Hoge to handle Acquisition loop mode : if assume_acq_loop is FALSE the other added variables don't : matter ---------------------------------------------------------------------------- */ public void get_file_info(Acr_Group group_list, File_Info *file_info, General_Info *general_info) { Mri_Index imri; World_Index iworld, jworld; Volume_Index ivolume; int nrows, ncolumns, spatial_sizes[VOL_NDIMS]; double study_id; int acq_id, rec_num; int cur_index; int index; int number_of_3D_partitions; Orientation orientation; World_Index volume_to_world[VOL_NDIMS]; double coordinate[WORLD_NDIMS], dircos[VOL_NDIMS][WORLD_NDIMS]; double steps[VOL_NDIMS], starts[VOL_NDIMS], slice_index; Acr_Element_Id mri_index_list[MRI_NDIMS]; Acr_Element_Id mri_total_list[MRI_NDIMS]; // Array of elements for mri dimensions mri_index_list[SLICE] = SPI_Current_slice_number; mri_index_list[ECHO] = ACR_Echo_number; // dicom time element may be different on old systems mri_index_list[TIME] = ACR_Acquisition; mri_index_list[PHASE] = NULL; mri_index_list[CHEM_SHIFT] = NULL; mri_total_list[SLICE] = SPI_Number_of_slices_nominal; mri_total_list[ECHO] = SPI_Number_of_echoes; mri_total_list[TIME] = ACR_Acquisitions_in_series; mri_total_list[PHASE] = NULL; mri_total_list[CHEM_SHIFT] = NULL; // Get image dimensions nrows = acr_find_short(group_list, ACR_Rows, 0); ncolumns = acr_find_short(group_list, ACR_Columns, 0); spatial_sizes[VROW] = nrows; spatial_sizes[VCOLUMN] = ncolumns; spatial_sizes[VSLICE] = 1; // Get intensity information get_intensity_info(group_list, file_info); // Check for necessary values not found if ((nrows <= 0) || (ncolumns <= 0) || (file_info->bits_stored <= 0) || (file_info->bits_alloc <= 0)) { file_info->valid = FALSE; return; } // Get study, acq, rec, image type id's get_identification_info(group_list, &study_id, &acq_id, &rec_num, NULL); // Get number of 3D partitions for working out number of slices number_of_3D_partitions = acr_find_int(group_list, SPI_Number_of_3D_raw_partitions_nominal, 1); if (number_of_3D_partitions < 1) number_of_3D_partitions = 1; // Get indices for image in current file for (imri=0; imri < MRI_NDIMS; imri++) { if (mri_index_list[imri] != NULL) { file_info->index[imri] = /* note that for TIME this will use ACR_Acqusition, which does not work for measurement loop scans */ acr_find_int(group_list, mri_index_list[imri], 1); } else { file_info->index[imri] = 1; } } // Get coordinate information get_coordinate_info(group_list, file_info, &orientation, volume_to_world, spatial_sizes, dircos, steps, starts, coordinate); // Replace slice index with slice position in hundredths of millimetres if // we have more than one partition if (number_of_3D_partitions > 1 | 1) { // ALWAYS USE POSITION (rhoge) slice_index = file_info->coordinate[SLICE] * 100.0; if (slice_index >= 0.0) { slice_index += 0.5; } else { slice_index -= 0.5; } slice_index = (int) slice_index; file_info->index[SLICE] = slice_index; } // Set up general info on first pass if (!general_info->initialized) { // Get row and columns sizes general_info->nrows = nrows; general_info->ncolumns = ncolumns; // Save the study, acquisition, reconstruction and image type // identifiers general_info->study_id = study_id; general_info->acq_id = acq_id; general_info->rec_num = rec_num; // No image type is available // (rhoge: no longer true?) general_info->image_type_string[0] = '\0'; /* Get dimension information */ for (imri=0; imri < MRI_NDIMS; imri++) { /* Gets sizes */ general_info->size[imri] = 1; if (mri_total_list[imri] != NULL) { general_info->total_size[imri] = acr_find_int(group_list, mri_total_list[imri], 1); } else { general_info->total_size[imri] = 1; } if (general_info->total_size[imri] < 1) { general_info->total_size[imri] = 1; } /* Check for 3D partitions for slice dimensions */ if (imri == SLICE) { general_info->total_size[imri] *= number_of_3D_partitions; } /* Set initial values */ general_info->default_index[imri] = file_info->index[imri]; general_info->image_index[imri] = -1; /* Allocate space for index and coordinate arrays if total_size > 1. Set the first values. */ if (general_info->total_size[imri] > 1) { general_info->indices[imri] = MALLOC(general_info->total_size[imri] * sizeof(int)); general_info->coordinates[imri] = MALLOC(general_info->total_size[imri] * sizeof(double)); for (index=0; index < general_info->total_size[imri]; index++) { general_info->indices[imri][index] = -1; general_info->coordinates[imri][index] = 0; } general_info->search_start[imri] = 0; general_info->indices[imri][0] = file_info->index[imri]; general_info->coordinates[imri][0] = file_info->coordinate[imri]; } } /* Loop over dimensions */ /* Get spatial coordinate information */ general_info->slice_world = volume_to_world[VSLICE]; general_info->row_world = volume_to_world[VROW]; general_info->column_world = volume_to_world[VCOLUMN]; for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; general_info->step[iworld] = steps[ivolume]; general_info->start[iworld] = starts[ivolume]; for (jworld=0; jworld < WORLD_NDIMS; jworld++) { general_info->dircos[volume_to_world[ivolume]][jworld] = dircos[ivolume][jworld]; } } /* Set data type and range */ if (file_info->bits_alloc <= 8) general_info->datatype = NC_BYTE; else general_info->datatype = NC_SHORT; general_info->is_signed = ((general_info->datatype == NC_SHORT) && (file_info->bits_stored < 16)); general_info->pixel_min = file_info->pixel_min; general_info->pixel_max = file_info->pixel_max; /* Save display window info */ general_info->window_min = file_info->window_min; general_info->window_max = file_info->window_max; /* Get the rest of the header information */ get_general_header_info(group_list, general_info); /* Copy the group list */ general_info->group_list = acr_copy_group_list(group_list); /* Set initialized flag */ general_info->initialized = TRUE; } /* Set up file info */ /* Update general info and validate file on later passes */ else { //-- /* Check for consistent data type */ if (((general_info->datatype == NC_BYTE) && (file_info->bits_alloc > 8)) || ((general_info->datatype == NC_SHORT) && (file_info->bits_alloc <= 8))) { file_info->valid = FALSE; return; } /* Check row and columns sizes */ if ((nrows != general_info->nrows) && (ncolumns != general_info->ncolumns)) { file_info->valid = FALSE; return; } /* Check study and acquisition id's */ if ((general_info->study_id != study_id) || (general_info->acq_id != acq_id)) { file_info->valid = FALSE; return; } /* Look to see if indices have changed */ for (imri=0; imri < MRI_NDIMS; imri++) { /* Get current index */ cur_index = file_info->index[imri]; /* Check whether this index is in the list */ if (general_info->size[imri] == 1) { index = ((cur_index == general_info->default_index[imri]) ? 0 : -1); } else { index = search_list(cur_index, general_info->indices[imri], general_info->size[imri], general_info->search_start[imri]); } /* If it is not, then add it */ if (index < 0) { /* Check whether we can add a new index */ if (general_info->size[imri] >= general_info->total_size[imri]) { file_info->valid = FALSE; return; } /* Add the index and coordinate to the lists */ index = general_info->size[imri]; general_info->search_start[imri] = index; general_info->indices[imri][index] = cur_index; general_info->coordinates[imri][index] = file_info->coordinate[imri]; general_info->size[imri]++; } } /* Loop over Mri_Index */ // note that number of slices will be wrong here for mosaics // we add some other mosaic-relevant fields... general_info->num_mosaic_rows = acr_find_int(group_list,EXT_Mosaic_rows,1); general_info->num_mosaic_cols = acr_find_int(group_list,EXT_Mosaic_columns,1); general_info->num_slices_in_file = acr_find_int(group_list,EXT_Slices_in_file,1); general_info->sub_image_rows = acr_find_short(group_list,EXT_Sub_image_rows, acr_find_short(group_list, ACR_Rows, 0)); general_info->sub_image_columns = acr_find_short(group_list,EXT_Sub_image_columns, acr_find_short(group_list, ACR_Rows, 0)); // Update display window info if (general_info->window_min > file_info->window_min) general_info->window_min = file_info->window_min; if (general_info->window_max < file_info->window_max) general_info->window_max = file_info->window_max; } // Update general info for this file // If we get to here, then we have a valid file file_info->valid = TRUE; return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_identification_info @INPUT : group_list - input data @OUTPUT : study_id acq_id rec_num image_type @RETURNS : (nothing) @DESCRIPTION: Routine to get image identification information. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_identification_info(Acr_Group group_list, double *study_id, int *acq_id, int *rec_num, int *image_type) { int number_of_frames; int number_of_averages; if (study_id != NULL) { // generate a study ID number from date & time: yyyymmdd.hhmmss // (should be unique enough for our application) *study_id = (((float)acr_find_int(group_list, ACR_Study_date, 0)) + ((float)acr_find_int(group_list, ACR_Study_time, 0))/1e6); } if (acq_id != NULL) { *acq_id = acr_find_int(group_list, ACR_Series, 0); number_of_frames = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); number_of_averages = acr_find_int(group_list, ACR_Nr_of_averages, 1); /* Determine if measurement loop was used (rhoge) - if so, we replace the different series numbers with ACR_Study_time, which is the same for all frames in a run. This will aid in grouping the files later on. Criteria used for identification of meast loop: 1) more than one dynamic scan 2) number of dynamic scans NOT equal to nominal number of signal averages (if they're equal, we assume acquisition loop scan) WARNING: it is possible that someone might use the measurement loop do serial scans which each have multiple signal averages. If NSA = the number of measts. in this case, then the scan will not be recognized as a Meast loop scan and the different frames will be placed in different series. To fix this, we'd really need to look at the series numbers of future and past files. It's also unlikely to happen but I'm sure it will... This also means we should NOT correct the number of signal averages on the sending side */ /* if ((number_of_frames > 1) || (*acq_id == 0)) { (orig test) */ if ( (file_type == N3DCM) && (number_of_frames > 1) && (number_of_frames != number_of_averages)) { *acq_id = acr_find_int(group_list, ACR_Study_time, 0); } } if (rec_num != NULL) *rec_num = 0; if (image_type != NULL) *image_type = 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_intensity_info @INPUT : group_list - input data @OUTPUT : file_info - file-specific info @RETURNS : (nothing) @DESCRIPTION: Routine to get intensity information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_intensity_info(Acr_Group group_list, File_Info *file_info) { double window_centre, window_width; /* Get pixel storage information */ file_info->bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); file_info->bits_stored = acr_find_short(group_list, ACR_Bits_stored, 0); #ifdef USE_DICOM_PIXEL_MIN_MAX // Get pixel value information // I think this might wrongly assume that the ACR values min/max // apply to the whole volume - it actually appears that they apply // to the current slice. file_info->pixel_min = acr_find_short(group_list, ACR_Smallest_pixel_value, 0); file_info->pixel_max = acr_find_short(group_list,ACR_Largest_pixel_value, (1 << file_info->bits_stored) - 1); #else // for now, use bits_stored to determine dynamic range // DICOM info on largest pixel applies to first slice, // not whole volume - this caused problems (roundoff?) // in Siemens Numaris 4 scans file_info->pixel_min = 0; file_info->pixel_max = (1 << file_info->bits_stored) - 1; #endif file_info->slice_min = file_info->pixel_min; file_info->slice_max = file_info->pixel_max; /* Get window min and max */ window_centre = (file_info->slice_max + file_info->slice_min) / 2.0; window_width = file_info->slice_max - file_info->slice_min; window_centre = acr_find_double(group_list, ACR_Window_centre, window_centre); window_width = acr_find_double(group_list, ACR_Window_width, window_width); file_info->window_min = window_centre - window_width / 2.0; file_info->window_max = window_centre + window_width / 2.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_coordinate_info @INPUT : group_list - input data sizes - size of each spatial dimension @OUTPUT : file_info - file-specific info volume_to_world - volume index to world coordinate index mapping dircos - direction cosines for spatial dimensions steps - step sizes for spatial dimensions starts - start positions for spatial dimensions (for a slice) coordinate - coordinate of centre of slice @RETURNS : (nothing) @DESCRIPTION: Routine to get coordinate information for a slice from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]) { Volume_Index ivolume; World_Index iworld; Acr_Element_Id dircos_elid[VOL_NDIMS]; Acr_Element element; int found_dircos[VOL_NDIMS], found_coordinate; double frame_time, start_time; double magnitude, largest, centre; double darray[2]; static Orientation orientation_list[WORLD_NDIMS] = {SAGITTAL, CORONAL, TRANSVERSE}; // row/column unit vectors in public dicom element double RowColVec[6]; if (file_type == N3DCM || file_type == IMA) { /* Set direction cosine element ids. Note that the reversal of rows and columns is intentional - their idea of the meaning of theses labels is different from ours. (Their row vector points along the row and not along the row dimension.) */ dircos_elid[VSLICE] = SPI_Image_normal; dircos_elid[VROW] = SPI_Image_column; dircos_elid[VCOLUMN] = SPI_Image_row; // Get direction cosines for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { found_dircos[ivolume] = FALSE; element = acr_find_group_element(group_list, dircos_elid[ivolume]); if (element == NULL) continue; if (acr_get_element_numeric_array(element, WORLD_NDIMS, dircos[ivolume]) != WORLD_NDIMS) continue; // negate the X and Z coordinates convert_numa3_coordinate(dircos[ivolume]); found_dircos[ivolume] = TRUE; } } else { // in normal dicom file, you just have row and column unit vectors // read in row/col vectors: element = acr_find_group_element(group_list, ACR_Image_orientation_patient); acr_get_element_numeric_array(element, 6, RowColVec); memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); found_dircos[VCOLUMN] = TRUE; found_dircos[VROW] = TRUE; convert_dicom_coordinate(dircos[VROW]); convert_dicom_coordinate(dircos[VCOLUMN]); // slice direction unit vector is cross product of row, col vectors: dircos[VSLICE][0] = dircos[VCOLUMN][1] * dircos[VROW][2] - dircos[VCOLUMN][2] * dircos[VROW][1]; dircos[VSLICE][1] = dircos[VCOLUMN][2] * dircos[VROW][0] - dircos[VCOLUMN][0] * dircos[VROW][2]; dircos[VSLICE][2] = dircos[VCOLUMN][0] * dircos[VROW][1] - dircos[VCOLUMN][1] * dircos[VROW][0]; found_dircos[VSLICE] = TRUE; // slice does not need to be converted because // it was computed as the cross product of two // corrected vectors // convert_dicom_coordinate(dircos[VSLICE]); } // Normalize the direction cosines for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { magnitude = 0.0; for (iworld=0; iworld < WORLD_NDIMS; iworld++) { magnitude += dircos[ivolume][iworld] * dircos[ivolume][iworld]; } if (magnitude <= 0) { found_dircos[ivolume] = FALSE; continue; } magnitude = sqrt(magnitude); for (iworld=0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] /= magnitude; } } // If we don't find direction cosines, then assume transverse volume if (!found_dircos[VSLICE] || !found_dircos[VROW] || !found_dircos[VCOLUMN]) { for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { for (iworld=0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] = ((ivolume == (WORLD_NDIMS-iworld-1)) ? -1.0 : 0.0); } } } // Figure out volume index to world index mapping and sign of direction // cosines - the code below figures out the primary direction in x,y,z // of each volume coordinate (row,col,slice) for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { largest = -1.0; for (iworld=0; iworld < WORLD_NDIMS; iworld++) { magnitude = dircos[ivolume][iworld]; if (magnitude < 0.0) magnitude = -magnitude; if (magnitude > largest) { largest = magnitude; volume_to_world[ivolume] = iworld; } } } // Get orientation (depends on primary direction of slice normal) *orientation = orientation_list[volume_to_world[VSLICE]]; // Get step information for (ivolume=0; ivolume < sizeof(darray)/sizeof(darray[0]); ivolume++) darray[ivolume] = -DBL_MAX; // note ACR_Pixel_size should now be called Pixel_spacing element = acr_find_group_element(group_list, ACR_Pixel_size); if (element != NULL) (void) acr_get_element_numeric_array(element, sizeof(darray)/sizeof(darray[0]), darray); if (darray[0] == -DBL_MAX) darray[0] = 1.0; if (darray[1] == -DBL_MAX) darray[1] = darray[0]; steps[VCOLUMN] = darray[0]; steps[VROW] = darray[1]; // anisotropic resolution? // steps[VSLICE] = acr_find_double(group_list, ACR_Slice_thickness, 1.0); steps[VSLICE] = acr_find_double(group_list,ACR_Spacing_between_slices,1.0); /* Make sure that direction cosines point the right way (dot product of direction cosine and axis is positive) and that step has proper sign */ for (ivolume = 0; ivolume < VOL_NDIMS; ivolume++) { iworld = volume_to_world[ivolume]; if (dircos[ivolume][iworld] < 0.0) { steps[ivolume] *= -1.0; for (iworld = 0; iworld < WORLD_NDIMS; iworld++) { dircos[ivolume][iworld] *= -1.0; } } } if (file_type == N3DCM || file_type == IMA) { // Find 3D coordinate of slice - SPI_Image_position gives // the *centre* of the slice! element = acr_find_group_element(group_list, SPI_Image_position); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) != WORLD_NDIMS)) { found_coordinate = FALSE; for (iworld=0; iworld < WORLD_NDIMS; iworld++) coordinate[iworld] = 0.0; } else { found_coordinate = TRUE; } convert_numa3_coordinate(coordinate); // Work out start positions in volume coordinates for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && found_dircos[VCOLUMN]) { centre = coordinate[XCOORD] * dircos[ivolume][XCOORD] + coordinate[YCOORD] * dircos[ivolume][YCOORD] + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; } else { centre = 0.0; } starts[ivolume] = centre - (steps[ivolume] * (sizes[ivolume] - 1.0) / 2.0); } } else { // normal dicom // Find 3D coordinate of slice - ACR_Image_position_patient gives // the *corner* of the slice! element = acr_find_group_element(group_list, ACR_Image_position_patient); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, coordinate) != WORLD_NDIMS)) { found_coordinate = FALSE; for (iworld=0; iworld < WORLD_NDIMS; iworld++) coordinate[iworld] = 0.0; } else { found_coordinate = TRUE; } convert_dicom_coordinate(coordinate); // Work out start positions in volume coordinates for (ivolume=0; ivolume < VOL_NDIMS; ivolume++) { if (found_coordinate && found_dircos[VSLICE] && found_dircos[VROW] && found_dircos[VCOLUMN]) { starts[ivolume] = coordinate[XCOORD] * dircos[ivolume][XCOORD] + coordinate[YCOORD] * dircos[ivolume][YCOORD] + coordinate[ZCOORD] * dircos[ivolume][ZCOORD]; } else { starts[ivolume] = 0.0; } } } // Find position along each dimension file_info->coordinate[SLICE] = starts[VSLICE]; file_info->coordinate[ECHO] = acr_find_double(group_list, ACR_Echo_time, 0.0) / (double) MS_PER_SECOND; // time section (rhoge) // now assume that time has been fixed when file was read start_time = acr_find_double(group_list,ACR_Series_time, 0.0); frame_time = acr_find_double(group_list, ACR_Acquisition_time, 0.0); start_time = convert_time_to_seconds(start_time); frame_time = convert_time_to_seconds(frame_time) - start_time; // check for case where scan starts right before midnight, // but frame is after midnight if (frame_time < 0.0) frame_time += (double) SECONDS_PER_DAY; file_info->coordinate[TIME] = frame_time; /* end of time section */ file_info->coordinate[PHASE] = 0.0; file_info->coordinate[CHEM_SHIFT] = 0.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_numa3_coordinate @INPUT : coordinate @OUTPUT : coordinate @RETURNS : (nothing) @DESCRIPTION: Routine to convert a coordinate to the correct orientation @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : made version specific to Numaris 3 SPI data (rhoge) ---------------------------------------------------------------------------- */ public void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]) { coordinate[XCOORD] = -coordinate[XCOORD]; coordinate[ZCOORD] = -coordinate[ZCOORD]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_dicom_coordinate @INPUT : coordinate @OUTPUT : coordinate @RETURNS : (nothing) @DESCRIPTION: Routine to convert a coordinate to the correct orientation @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : made new dicom version (rhoge) ---------------------------------------------------------------------------- */ public void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]) { coordinate[XCOORD] = -coordinate[XCOORD]; coordinate[YCOORD] = -coordinate[YCOORD]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_general_header_info @INPUT : group_list - input data @OUTPUT : general_info - general information about files @RETURNS : (nothing) @DESCRIPTION: Routine to extract general header information from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_general_header_info(Acr_Group group_list, General_Info *general_info) { int maxlen, length; char *string, *ptr; /* Maximum length for strings */ maxlen = sizeof(Cstring) - 1; /* Get intensity units */ (void) strncpy(general_info->units, "", maxlen); /* Get patient info */ (void) strncpy(general_info->patient.name, acr_find_string(group_list, ACR_Patient_name, ""), maxlen); (void) strncpy(general_info->patient.identification, acr_find_string(group_list, ACR_Patient_identification, ""), maxlen); (void) strncpy(general_info->patient.birth_date, acr_find_string(group_list, ACR_Patient_birth_date, ""), maxlen); (void) strncpy(general_info->patient.age, acr_find_string(group_list, ACR_Patient_age, ""), maxlen); string = acr_find_string(group_list, ACR_Patient_sex, ""); if (*string == 'M') (void) strncpy(general_info->patient.sex, MI_MALE, maxlen); else if (*string == 'F') (void) strncpy(general_info->patient.sex, MI_FEMALE, maxlen); else if (*string == 'O') (void) strncpy(general_info->patient.sex, MI_OTHER, maxlen); else (void) strncpy(general_info->patient.sex, "", maxlen); general_info->patient.weight = acr_find_double(group_list, ACR_Patient_weight, -DBL_MAX); /* added by rhoge - registration timing info */ (void) strncpy(general_info->patient.reg_date, acr_find_string(group_list, ACR_Study_date, ""), maxlen); (void) strncpy(general_info->patient.reg_time, acr_find_string(group_list, ACR_Study_time, ""), maxlen); /* Get study info */ (void) strncpy(general_info->study.start_time, acr_find_string(group_list, ACR_Study_date, ""), maxlen - 1); length = strlen(general_info->study.start_time); general_info->study.start_time[length] = ' '; length++; (void) strncpy(&general_info->study.start_time[length], acr_find_string(group_list, ACR_Study_time, ""), maxlen - length); string = acr_find_string(group_list, ACR_Modality, ""); if (strcmp(string, ACR_MODALITY_MR) == 0) (void) strncpy(general_info->study.modality, MI_MRI, maxlen); (void) strncpy(general_info->study.manufacturer, acr_find_string(group_list, ACR_Manufacturer, ""), maxlen); (void) strncpy(general_info->study.model, acr_find_string(group_list, ACR_Manufacturer_model, ""), maxlen); general_info->study.field_value = acr_find_double(group_list, ACR_Magnetic_field_strength, -DBL_MAX); (void) strncpy(general_info->study.software_version, acr_find_string(group_list, ACR_Software_versions, ""), maxlen); (void) strncpy(general_info->study.serial_no, acr_find_string(group_list, ACR_Device_serial_number, ""), maxlen); (void) strncpy(general_info->study.calibration_date, acr_find_string(group_list, SPI_Calibration_date, ""), maxlen); (void) strncpy(general_info->study.institution, acr_find_string(group_list, ACR_Institution_id, ""), maxlen); (void) strncpy(general_info->study.station_id, acr_find_string(group_list, ACR_Station_id, ""), maxlen); (void) strncpy(general_info->study.referring_physician, acr_find_string(group_list, ACR_Referring_physician, ""), maxlen); (void) strncpy(general_info->study.performing_physician, acr_find_string(group_list, ACR_Performing_physician, ""), maxlen); (void) strncpy(general_info->study.operator, acr_find_string(group_list, ACR_Operators_name, ""), maxlen); (void) strncpy(general_info->study.procedure, acr_find_string(group_list, ACR_Procedure_description, ""), maxlen); (void) sprintf(general_info->study.study_id, "%.6f",general_info->study_id); /* acquisition id modified by rhoge to get rid of first digit that is not required for identification of run */ /* (void) sprintf(general_info->study.acquisition_id, "%d_%d", acr_find_int(group_list, ACR_Series, 0), general_info->acq_id); */ (void) sprintf(general_info->study.acquisition_id, "%d", general_info->acq_id); /* Get acquisition information */ /* string = acr_find_string(group_list, SPI_Sequence_file_name, ""); ptr = string + strlen(string) - 1; while ((ptr > string) && (*ptr != '/')) {ptr--;} if ((ptr >= string) && (*ptr == '/')) string = ptr + 1; (void) strncpy(general_info->acq.scan_seq, string, maxlen); ptr = general_info->acq.scan_seq; while (*ptr != '\0') { if (*ptr == '.') *ptr = '\0'; ptr++; } */ (void) strncpy(general_info->acq.scan_seq, acr_find_string(group_list, ACR_Sequence_name, ""), maxlen); (void) strncpy(general_info->acq.seq_owner, acr_find_string(group_list, SPI_Sequence_file_owner, ""), maxlen); (void) strncpy(general_info->acq.seq_descr, acr_find_string(group_list, SPI_Sequence_description, ""), maxlen); (void) strncpy(general_info->acq.protocol_name, acr_find_string(group_list, ACR_Protocol_name, ""), maxlen); (void) strncpy(general_info->acq.receive_coil, acr_find_string(group_list, ACR_Receiving_coil, ""), maxlen); (void) strncpy(general_info->acq.transmit_coil, acr_find_string(group_list, ACR_Transmitting_coil, ""), maxlen); general_info->acq.rep_time = acr_find_double(group_list, ACR_Repetition_time, -DBL_MAX); if (general_info->acq.rep_time != -DBL_MAX) general_info->acq.rep_time /= 1000.0; general_info->acq.echo_time = acr_find_double(group_list, ACR_Echo_time, -DBL_MAX); if (general_info->acq.echo_time != -DBL_MAX) general_info->acq.echo_time /= 1000.0; general_info->acq.echo_number = acr_find_double(group_list, ACR_Echo_number, -DBL_MAX); general_info->acq.inv_time = acr_find_double(group_list, ACR_Inversion_time, -DBL_MAX); if (general_info->acq.inv_time != -DBL_MAX) general_info->acq.inv_time /= 1000.0; general_info->acq.b_value = acr_find_double(group_list, EXT_Diffusion_b_value, -DBL_MAX); general_info->acq.flip_angle = acr_find_double(group_list, ACR_Flip_angle, -DBL_MAX); general_info->acq.slice_thickness = acr_find_double(group_list, ACR_Slice_thickness, -DBL_MAX); general_info->acq.num_slices = acr_find_double(group_list, SPI_Number_of_slices_nominal, -DBL_MAX); general_info->acq.num_dyn_scans = acr_find_double(group_list, ACR_Acquisitions_in_series, -DBL_MAX); general_info->acq.num_avg = acr_find_double(group_list, ACR_Nr_of_averages, -DBL_MAX); general_info->acq.scan_dur = acr_find_double(group_list, SPI_Total_measurement_time_cur, -DBL_MAX); general_info->acq.ky_lines = acr_find_double(group_list, SPI_Number_of_fourier_lines_current, -DBL_MAX); general_info->acq.kymax_ix = acr_find_double(group_list, SPI_Number_of_fourier_lines_after_zero, -DBL_MAX); general_info->acq.kymin_ix = acr_find_double(group_list, SPI_First_measured_fourier_line, -DBL_MAX); general_info->acq.kz_lines = acr_find_double(group_list, SPI_Number_of_3d_raw_part_cur, -DBL_MAX); general_info->acq.dummy_scans = acr_find_double(group_list, SPI_Number_of_prescans, -DBL_MAX); general_info->acq.imaging_freq = acr_find_double(group_list, ACR_Imaging_frequency, -DBL_MAX); if (general_info->acq.imaging_freq != -DBL_MAX) general_info->acq.imaging_freq *= 1e6; (void) strncpy(general_info->acq.imaged_nucl, acr_find_string(group_list, ACR_Imaged_nucleus, ""), maxlen); general_info->acq.adc_voltage = acr_find_double(group_list, SPI_ADC_voltage, -DBL_MAX); general_info->acq.adc_offset = acr_find_double(group_list, SPI_ADC_offset, -DBL_MAX); general_info->acq.transmit_ampl = acr_find_double(group_list, SPI_Transmitter_amplitude, -DBL_MAX); general_info->acq.rec_amp_gain = acr_find_double(group_list, SPI_Receiver_amplifier_gain, -DBL_MAX); general_info->acq.rec_preamp_gain = acr_find_double(group_list, SPI_Receiver_preamplifier_gain, -DBL_MAX); general_info->acq.win_center = acr_find_double(group_list, SPI_Window_center, -DBL_MAX); general_info->acq.win_width = acr_find_double(group_list, SPI_Window_width, -DBL_MAX); general_info->acq.gy_ampl = acr_find_double(group_list, SPI_Phase_gradient_amplitude, -DBL_MAX); general_info->acq.gx_ampl = acr_find_double(group_list, SPI_Readout_gradient_amplitude, -DBL_MAX); general_info->acq.gz_ampl = acr_find_double(group_list, SPI_Selection_gradient_amplitude, -DBL_MAX); general_info->acq.num_phase_enc_steps = acr_find_double(group_list, ACR_Number_of_phase_encoding_steps, -DBL_MAX); general_info->acq.percent_sampling = 100 * acr_find_double(group_list, ACR_Percent_sampling, -DBL_MAX); general_info->acq.percent_phase_fov = 100 * acr_find_double(group_list, ACR_Percent_phase_field_of_view, -DBL_MAX); general_info->acq.pixel_bandwidth = acr_find_double(group_list, ACR_Pixel_bandwidth, -DBL_MAX); general_info->acq.sar = acr_find_double(group_list, ACR_SAR, -DBL_MAX); strncpy(general_info->acq.mr_acq_type, acr_find_string(group_list,ACR_MR_acquisition_type,""),4); strncpy(general_info->acq.image_type, acr_find_string(group_list,ACR_Image_type,""),128); strncpy(general_info->acq.phase_enc_dir, acr_find_string(group_list,ACR_Phase_encoding_direction,""),3); (void) strncpy(general_info->acq.comments, "", maxlen); // Siemens Numaris 4 specific! if (0) { // this causes problems with large MrProt fields // printf("allocating %d bytes for MrProt...\n", // strlen(acr_find_string(group_list, EXT_MrProt_dump, ""))); general_info->acq.MrProt = MALLOC(strlen(acr_find_string(group_list, EXT_MrProt_dump, "")) * sizeof(char)); (void) strcpy(general_info->acq.MrProt, acr_find_string(group_list, EXT_MrProt_dump, "")); } else { general_info->acq.MrProt = MALLOC(strlen("disabled")*sizeof(char)); (void) strcpy(general_info->acq.MrProt, "disabled"); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_time_to_seconds @INPUT : dicom_time @OUTPUT : (none) @RETURNS : real time in seconds from beginning of day @DESCRIPTION: Routine to convert dicom seconds (decimal hhmmss.xxxxx) to real seconds since the start of day. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public double convert_time_to_seconds(double dicom_time) { /* Constants */ #define DICOM_SECONDS_PER_HOUR 10000.0 #define DICOM_SECONDS_PER_MINUTE 100.0 /* Variables */ double real_time, Hours, Minutes, Seconds; /* Get the components of the time */ Hours = (int) (dicom_time / (double) DICOM_SECONDS_PER_HOUR); dicom_time -= Hours * DICOM_SECONDS_PER_HOUR; Minutes = (int) (dicom_time / (double) DICOM_SECONDS_PER_MINUTE); dicom_time -= Minutes * (double) DICOM_SECONDS_PER_MINUTE; Seconds = dicom_time; /* Work out the number of seconds */ real_time = (Hours * 3600.0) + (Minutes * 60.0) + Seconds; return real_time; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_siemens_dicom_image @INPUT : group_list - input data @OUTPUT : image - image data structure (user must free data) @RETURNS : (nothing) @DESCRIPTION: Routine to get an image from a group list @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image) { /* Variables */ Acr_Element element; int nrows, ncolumns; int bits_alloc; int bits_stored; int image_group; void *data = NULL; long imagepix, ipix; struct Acr_Element_Id elid; nc_type datatype; /* Get the image information */ bits_alloc = acr_find_short(group_list, ACR_Bits_allocated, 0); bits_stored = acr_find_short(group_list, ACR_Bits_stored, bits_alloc); nrows = acr_find_short(group_list, ACR_Rows, 0); ncolumns = acr_find_short(group_list, ACR_Columns, 0); image_group = acr_find_short(group_list, ACR_Image_location, ACR_ACTUAL_IMAGE_GID); /* Figure out type */ if (bits_alloc > CHAR_BIT) datatype = NC_SHORT; else datatype = NC_BYTE; /* Set image info */ image->nrows = nrows; image->ncolumns = ncolumns; imagepix = nrows * ncolumns; image->data = (unsigned short *) MALLOC(imagepix * sizeof(short)); image->free = TRUE; /* Get image pointer */ elid.group_id = image_group; elid.element_id = SPI_IMAGE_ELEMENT; element = acr_find_group_element(group_list, &elid); if (element == NULL) { (void) memset(image->data, 0, imagepix * sizeof(short)); return; } data = acr_get_element_data(element); /* Convert the data according to type */ /* Look for byte data */ if (datatype == NC_BYTE) { for (ipix=0; ipix < imagepix; ipix++) { image->data[ipix] = *((unsigned char *) data + ipix); } } else { /* Look for unpacked short data */ if (bits_alloc == nctypelen(datatype) * CHAR_BIT) { acr_get_short(acr_get_element_byte_order(element), nrows*ncolumns, data, image->data); } /* Fill with zeros in any other case */ else { (void) memset(image->data, 0, imagepix * sizeof(short)); } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/string_to_filename.c0000644000175000000620000001724412574624760025722 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename.c @DESCRIPTION: Code to convert a string to something that can be used in a file name. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1997 (Peter Neelin) @MODIFIED : * $Log: string_to_filename.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.2 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #define SEPARATOR '_' /* ----------------------------- MNI Header ----------------------------------- @NAME : string_to_filename @INPUT : string - string to convert maxlen - maximum length of output string (including terminating '\0') @OUTPUT : filename - output string @RETURNS : (nothing) @DESCRIPTION: Routine to convert a string to something that can be used in a filename @METHOD : @GLOBALS : @CALLS : @CREATED : December 10, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void string_to_filename(char *string, char *filename, int maxlen) { int length, isrc, idst; int ch; int found_first, need_separator; /* Get string length */ length = strlen(string); if (length > maxlen-1) length = maxlen - 1; /* Loop through characters */ idst = 0; found_first = FALSE; need_separator = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalnum(ch)) { found_first = TRUE; if (need_separator) { filename[idst++] = SEPARATOR; need_separator = FALSE; } filename[idst++] = tolower(ch); } else if (found_first) { need_separator = TRUE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; return; } public void string_to_initials(char *string, char *filename, int maxlen) { /* function added by R. Hoge to convert name-like strings to initials in environment where confidentiality policy prohibits use of names in file-names */ int length, isrc, idst; int ch; int first_found, sep_found, multi_word, comma_found, comma_found2, in_word; /* Get string length */ length = strlen(string); if (length > maxlen-1) length = maxlen - 1; /* do first pass to look for multi-words, commas */ first_found = FALSE; sep_found = FALSE; multi_word = FALSE; comma_found = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; /* if we hit a separator after finding a first alphanum, treat as multi words */ /* alphanumeric expressions (including underscores, hyphens) are treated as discrete words - note that we won't print hyphens */ /* examples of single words: test5 test-5 test_5 examples of multi words: test 5 snr_test 5 test,5 snr-test 5 */ if (sep_found && isalnum(ch)) { multi_word = TRUE; } if (first_found && !(isalnum(ch)||ch=='-'||ch=='_')) { sep_found = TRUE; } if (isalnum(ch)) { first_found = TRUE; } // Numaris 4 used caret (^) to separate names (Last^first) if (ch == ',' || ch == '^') { comma_found = TRUE; } } /* if Patient name is only a single word, then just strip out non-alphanumeric characters examples: snrtest1 -> snrtest1 snrtest-1 -> snrtest1 snr_test-2 -> snr_test2 note that hyphens are omitted, because these are used as delimiters in filename */ if (!multi_word) { idst = 0; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalnum(ch) || ch=='_') { filename[idst++] = tolower(ch); } } /* Add terminating '\0' */ filename[idst++] = '\0'; } else { /* multiple words */ if (!comma_found) { /* examples of multi-word no comma: john doe -> jd john edward doe -> jed john doe-smith -> jds my snr_test -> mst john doe 12 -> jd12 john12 smith -> j12s 12john smith -> 12js john doe test2b -> jst2b note that underscores are treated as separators here, contiguous digits are all printed, and digits are treated as printable separators */ /* Loop through characters */ idst = 0; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; } else { /* multiple words with comma separation */ /* examples of multi-word with comma: doe, john -> jd doe,john -> jd doe-smith, john -> jds note that we treat stuff before the comma as the LAST name */ /* we do two passes: all the stuff after the comma THEN all the stuff before the comma */ idst = 0; /* Loop through characters, writing those after comma*/ comma_found2 = FALSE; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; if (comma_found2) filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; if (comma_found2) filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } // numaris 4 uses Last^First if (ch == ',' || ch == '^') { comma_found2 = TRUE; } } /* now write characters/initials before the comma*/ comma_found2 = FALSE; in_word = FALSE; for (isrc=0; isrc < length; isrc++) { ch = string[isrc]; if (isalpha(ch) && !in_word) { in_word = TRUE; if (!comma_found2) filename[idst++] = tolower(ch); } else if (isdigit(ch)) { in_word = FALSE; if (!comma_found2) filename[idst++] = ch; } else if (!isalnum(ch)) { in_word = FALSE; } // Numaris 4 uses Last^First if (ch == ',' || ch == '^') { comma_found2 = TRUE; } } /* Add terminating '\0' */ filename[idst++] = '\0'; } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/modify_group_list.c0000644000175000000620000001254712574624760025611 0ustar stevestaffpublic void multi_image_modify_group_list(Acr_Group group_list, Acr_Element *big_image,Acr_Element *small_image,int iimage) { int irow, ibyte, idim, nbyte; int isub, jsub; char *new, *old; long old_offset, new_offset; double position[3], distance; double old_position[3], old_step[3], normal[3]; char string[256]; int slices_in_file; int num_mosaic_rows; int num_mosaic_cols; int pixel_size; long new_image_size; Acr_Element element; int big_cols, big_rows; int small_cols, small_rows; void *data; double RowColVec[6]; double dircos[VOL_NDIMS][WORLD_NDIMS]; // get info about file: slices_in_file=acr_find_int(group_list, EXT_Slices_in_file, 999); num_mosaic_rows=acr_find_int(group_list,EXT_Mosaic_rows, 999); num_mosaic_cols = acr_find_int(group_list,EXT_Mosaic_columns,999); // Check the image number if ((iimage < 0) || (iimage > slices_in_file)) { (void) fprintf(stderr, "Invalid image number to send: %d of %d\n",iimage, slices_in_file); exit(EXIT_FAILURE); } // Figure out the sub-image indices isub = iimage % num_mosaic_rows; jsub = iimage / num_mosaic_cols; // Get pointers: old = acr_get_element_data(*big_image); new = acr_get_element_data(*small_image); /* Copy the image */ nbyte = small_cols * pixel_size; for (irow=0; irow < small_rows; irow++) { old_offset = isub * small_cols +(jsub * small_rows + irow) * big_rows; old_offset *= pixel_size; new_offset = (irow * small_cols) * pixel_size; for (ibyte=0; ibyte < nbyte; ibyte++) { new[new_offset + ibyte] = old[old_offset + ibyte]; } } /* Reset the byte order and VR encoding. This will be modified on each send according to what the connection needs. */ acr_set_element_byte_order(*small_image,acr_get_element_byte_order(*big_image)); acr_set_element_vr_encoding(*small_image,acr_get_element_vr_encoding(*big_image)); // Update the slice index acr_insert_numeric(&group_list, SPI_Current_slice_number,(double) (iimage + 1)); if (file_type == N3DCM || file_type == IMA) { // get the image normals element = acr_find_group_element(group_list, SPI_Image_normal); acr_get_element_numeric_array(element, 3, normal); // get the old image position element = acr_find_group_element(group_list, SPI_Image_position); acr_get_element_numeric_array(element, 3, old_position); } else { element = acr_find_group_element(group_list,ACR_Image_orientation_patient); acr_get_element_numeric_array(element, 6, RowColVec); memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); convert_dicom_coordinate(dircos[VROW]); convert_dicom_coordinate(dircos[VCOLUMN]); // dircos[VSLICE][0] = normal[0] = dircos[VCOLUMN][1] * dircos[VROW][2] - dircos[VCOLUMN][2] * dircos[VROW][1]; // dircos[VSLICE][1] = normal[1] = dircos[VCOLUMN][2] * dircos[VROW][0] - dircos[VCOLUMN][0] * dircos[VROW][2]; // dircos[VSLICE][2] = normal[2] = dircos[VCOLUMN][0] * dircos[VROW][1] - dircos[VCOLUMN][1] * dircos[VROW][0]; element = acr_find_group_element(group_list, ACR_Image_position_patient); acr_get_element_numeric_array(element, WORLD_NDIMS, old_position); convert_dicom_coordinate(old_position); } printf("here1\n"); /* Update the position */ distance = 0.0; for (idim=0; idim < 3; idim++) { position[idim] = old_position[idim] + (double) iimage * old_step[idim]; distance += position[idim] * normal[idim]; } (void) sprintf(string, "%.15g\\%.15g\\%.15g",position[0], position[1], position[2]); acr_insert_string(&group_list, SPI_Image_position, string); // call function to fix DICOM header to match Siemens header update_coordinate_info(group_list); } public int multi_image_init(Acr_Group group_list, Acr_Element *big_image, Acr_Element *small_image) { int big_cols, big_rows; int small_cols, small_rows; int num_mosaic_rows; int num_mosaic_cols; int pixel_size; long new_image_size; void *data; int group_id, element_id; // Steal the image element from the group list *big_image = acr_find_group_element(group_list, ACR_Image); group_id = acr_get_element_group(*big_image); element_id = acr_get_element_element(*big_image); acr_group_steal_element(acr_find_group(group_list, group_id),*big_image); // Add a small image, if needed big_cols = acr_find_int(group_list, ACR_Columns, 1) * num_mosaic_cols; big_rows = acr_find_int(group_list, ACR_Rows, 1) * num_mosaic_rows; small_cols = acr_find_int(group_list, ACR_Columns, 1); small_rows = acr_find_int(group_list, ACR_Rows, 1); pixel_size = (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; new_image_size = acr_find_int(group_list, ACR_Columns, 1) * acr_find_int(group_list, ACR_Rows, 1) * pixel_size; data = malloc((size_t) new_image_size); *small_image = acr_create_element(group_id, element_id,acr_get_element_vr(*big_image),new_image_size, data); acr_set_element_vr(*small_image,acr_get_element_vr(*big_image)); acr_set_element_byte_order(*small_image,acr_get_element_byte_order(*big_image)); acr_set_element_vr_encoding(*small_image,acr_get_element_vr_encoding(*big_image)); acr_insert_element_into_group_list(&group_list, *small_image); return 0; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicomserver-nondebug.c0000644000175000000620000007144212574624760026173 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomserver.c @DESCRIPTION: Program to receive images from Siemens Vision. @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : Modified by R. Hoge Feb. 2000 to handle acquisition loop dynamic scans on Siemens Sonata system * $Log: dicomserver-nondebug.c,v $ * Revision 1.3 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.9 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.8 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.7 2001/08/29 20:55:53 rhoge * added -help option * * Revision 1.6 2001/07/19 17:43:46 rhoge * added -nofork command line option to prevent forking * * Revision 1.5 2001/02/26 13:37:59 rhoge * redirect sderr to /dev/null if no logging - prevents hangups due to * printfs dumping text into connection stream? * * Revision 1.4 2001/02/26 06:17:42 rhoge * changed tmp dir assignment to be consistent, always uses stdio.h stuff. * also added command-line flags for destination dir, logging level, and * retention of dicom files (are put in dest dir - must be specified) * * Revision 1.3 2000/12/14 21:14:58 rhoge * added macro (NO_FORK) to prevent forking if uncommented * * Revision 1.2 2000/12/13 13:22:18 rhoge * experimental changes to forking code for minc file creation - should * be no change * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.2 1997/07/10 17:35:35 neelin * Changed error handling and fixed message deletion. * * Revision 4.1 1997/07/08 23:15:09 neelin * Added support for C_ECHO command. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include /* Global for minc history */ extern char *minc_history; /* State of server. Note that DISCONNECTING is used for a high-level protocol error and TERMINATING is used for a low-level error or end of input */ typedef enum { WAITING_FOR_ASSOCIATION, WAITING_FOR_DATA, DISCONNECTING, TERMINATING } Server_state; /* stuff added by rhoge */ #define EXTREME_LOGGING 10 /* rhoge */ /* Do we do logging? */ /********************/ //int Do_logging = 0; /* Belong to rhoge, commented out by leili*/ /*added by Leili:*/ int Do_logging = #ifndef DO_HIGH_LOGGING LOW_LOGGING; # else HIGH_LOGGING; # endif /******************/ /* Changed by leili from False to True */ int Fork = TRUE; /*****************/ /* Do we keep files or are they temporary? */ static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif /******************/ /* added by Leili */ /* In what directory do we run? */ static char *run_dir = "/data/fmri/transfer/tmp"; /******************/ /* Globals for handling connection timeouts */ int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; File_Type file_type = N4DCM ; /* type of input files */ int N4_OFFSET; char *pname; int main(int argc, char *argv[]) { char *pname; Acr_File *afpin, *afpout; Acr_Status status; Server_state state; int acr_command; Acr_Group group_list; Acr_Message input_message, output_message; int exit_status; char exit_string[256]; char **file_list; Data_Object_Info **file_info_list; int num_files, num_files_alloc; static char file_prefix_string[L_tmpnam+1] = "dicomserver"; char *file_prefix = file_prefix_string; //Added by Leili char *file_prefix2 = "/usr/local/dicom_keep/"; char *temp_dir; int continue_looping; FILE *fptemp; char last_file_name[256]; char *project_name = NULL; char logfilename[256]; int pdu_type; int process_files, have_extra_file; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; int pres_context_id; long maximum_length; pid_t server_pid, child_pid; int statptr; /* added by rhoge */ int ix; int UseArgDir = 0; char OutDir[128]; /* Get server process id */ server_pid = getpid(); /*************************/ /* Change to tmp directory - note that this will be default file destination if no aetitle or command-line specification are received (rhoge) */ /* commented out by leili */ //(void) chdir(P_tmpdir); /* added by leili */ /* change to tmp directory */ if (run_dir != NULL) { (void) chdir(run_dir); } /*****************************/ /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* read in all the input pars and file names */ for (ix = 1; ix]\nOptions:\n\t-log \n\t-fork\n\t-keep_dcm\n\t-help\n"); exit(EXIT_SUCCESS); } else { (void) fprintf(stderr,"Usage: dicomserver []\nOptions:\n\t-log \n\t-fork\n\t-keep_dcm\n\t-help\n"); exit(EXIT_FAILURE); } } /* Re-open stderr if we are logging */ if (Do_logging > NO_LOGGING) { (void) sprintf(logfilename, "/data/fmri/transfer/logs/dicomserver-%d.log", (int) getpid()); (void) freopen(logfilename, "w", stderr); setbuf(stderr, NULL); } else { (void) sprintf(logfilename, "/dev/null"); (void) freopen(logfilename, "w", stderr); setbuf(stderr, NULL); } // set up type of connection (Syngo?) file_type = N4DCM; // default N4_OFFSET = 0; /* Print message at start */ pname = argv[0]; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "%s: Started dicom server.\n", pname); } /* Make connection */ open_connection(argc, argv, &afpin, &afpout); /* Check that the connection was made */ if ((afpin == NULL) || (afpout == NULL)) { (void) fprintf(stderr, "%s: Error opening connection.\n", pname); exit(EXIT_FAILURE); } /* Print connection message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "%s: Connection accepted.\n", pname); } #ifdef DO_INPUT_TRACING /* Enable input tracing */ acr_dicom_enable_trace(afpin); acr_dicom_enable_trace(afpout); #endif /* Create file prefix. Create the temporary file to avoid file name clashes */ /*****************************/ /* Commented out by leili */ //temp_dir = NULL; //if ( !Keep_files) { // (void) tmpnam(file_prefix); // if (mkdir(file_prefix, (mode_t) 0777)) { // (void) fprintf(stderr, // "%s: Unable to create directory for temporary files.\n", // pname); // perror(pname); // exit(EXIT_FAILURE); // } // temp_dir = strdup(file_prefix); // (void) strcat(file_prefix, "/dicom"); //} // else { // file_prefix=strdup(OutDir); // if (mkdir(file_prefix, (mode_t) 0777)) { // (void) fprintf(stderr, // "Directory %s exists...\n",file_prefix); // } // temp_dir = strdup(file_prefix); // (void) strcat(file_prefix, "/dicom"); // } /****************************/ /* Added by Leili */ temp_dir = NULL; if (! Keep_files){ //temp_dir = tempnam(run_dir, NULL); temp_dir = tempnam(run_dir, NULL); if(mkdir(temp_dir, (mode_t) 0777)){ (void) fprintf(stderr, "%s: Unable to create directory for temporary files.\n", pname); perror(pname); exit(EXIT_FAILURE); } (void)strcpy(file_prefix, temp_dir); (void) strcat(file_prefix, "/"); //(void) strcat(file_prefix, "/dicom"); } /*****************************/ (void) fprintf(stderr, "About to begin the space allocation \n"); /* Leili */ /* Get space for file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; file_list = MALLOC((size_t) num_files_alloc * sizeof(*file_list)); file_info_list = MALLOC(num_files_alloc * sizeof(*file_info_list)); /* Loop while reading messages */ state = WAITING_FOR_ASSOCIATION; continue_looping = TRUE; num_files = 0; (void) fprintf(stderr, "About to start reading a message \n"); /* Leili */ while (continue_looping) { /* Wait for any children that have finished */ while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} /* If there are children, slow down the processing */ (void) fprintf(stderr, "If we have children, slow down \n"); /* Leili */ if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } (void) fprintf(stderr, "Start reading the message\n"); /* Leili */ /* Read in the message */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); if (Do_logging > HIGH_LOGGING) fprintf(stderr,"\nWaiting for dicom message...\n"); /* rhoge */ /* chokes here at end of asynchronous transfer */ status=acr_input_dicom_message(afpin, &input_message); (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr,"\nGot message.\n"); fprintf(stderr,"\nCalling `alarm'...\n"); } (void) alarm(0); if (Do_logging > HIGH_LOGGING) fprintf(stderr,"\nAlarm called.\n"); /* rhoge */ (void) fprintf(stderr,"Checking for an error \n"); /* Leili */ /* Check for error */ if (status != ACR_OK) { continue_looping = FALSE; state = TERMINATING; break; } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Get group list */ group_list = acr_get_message_group_list(input_message); /* Get PDU type. Default is data transfer */ pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); (void) fprintf(stderr,"We got the pdu and it is: %d\n", pdu_type); /* Leili */ /* Deal with PDU state */ switch (pdu_type) { /* Associate request */ case ACR_PDU_ASSOC_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\n Associate request is ACR_PDU_ASSOC_RQ\n"); if (state != WAITING_FOR_ASSOCIATION) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Work out reply and get connection info */ (void) fprintf(stderr,"About to go to reply program\n"); /* Leili */ output_message = associate_reply(input_message, &project_name, &pres_context_id, &byte_order, &vr_encoding, &maximum_length); (void) fprintf(stderr,"We finished with the reply program\n"); /* Leili */ /* Modify the input and output streams according to the connection info */ acr_set_byte_order(afpin, byte_order); acr_set_vr_encoding(afpin, vr_encoding); acr_set_byte_order(afpout, byte_order); acr_set_vr_encoding(afpout, vr_encoding); acr_set_dicom_pres_context_id(afpout, pres_context_id); acr_set_dicom_maximum_length(afpout, maximum_length); /* Get ready for files */ num_files = 0; state = WAITING_FOR_DATA; break; /* Release */ case ACR_PDU_REL_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_REL_RQ\n"); if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } output_message = release_reply(input_message); state = TERMINATING; process_files = TRUE; if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nReceived release request; process flag set.\n"); break; /* Abort */ case ACR_PDU_ABORT_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_ABORT_RQ\n"); output_message = abort_reply(input_message); state = TERMINATING; break; /* Data transfer */ case ACR_PDU_DATA_TF: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_DATA_TF\n"); /* Check state */ if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Check command and compose a reply */ acr_command = acr_find_short(group_list, ACR_Command, -1); switch (acr_command) { case ACR_C_STORE_RQ: case ACR_C_ECHO_RQ: output_message = data_reply(input_message); break; default: status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Carry on only if we have a store command */ if (acr_command != ACR_C_STORE_RQ) break; /* Get rid of the command groups */ group_list = skip_command_groups(group_list); /* Was the data attached to the command? If not, read in the next message - it should contain the data */ if (group_list == NULL) { /* Delete the previous message */ if (input_message != NULL) acr_delete_message(input_message); /* Read the data and check the status */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status=acr_input_dicom_message(afpin, &input_message); (void) alarm(0); if (status != ACR_OK) { state = DISCONNECTING; break; } /* Check that we have a data PDU */ group_list = acr_get_message_group_list(input_message); if (acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF) != ACR_PDU_DATA_TF) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Skip command groups and check for no data */ group_list = skip_command_groups(group_list); if (group_list == NULL) break; } (void) fprintf(stderr,"End of while loop, we are out of the loop \n"); /* Leili */ /* Extend file list if necessary */ if (num_files >= num_files_alloc) { num_files_alloc = num_files + FILE_ALLOC_INCREMENT; file_list = REALLOC(file_list, num_files_alloc * sizeof(*file_list)); file_info_list = REALLOC(file_info_list, num_files_alloc * sizeof(*file_info_list)); } file_list[num_files] = NULL; file_info_list[num_files] = MALLOC(sizeof(*file_info_list[num_files])); /* Save the object */ //save_transferred_object(group_list, // file_prefix, &file_list[num_files], // file_info_list[num_files]); /* Added by leili */ save_transferred_object(group_list, file_prefix2, &file_list[num_files], file_info_list[num_files]); (void) fprintf(stderr,"Finished saving transfered object number %d \n", (num_files)+1); /* Leili */ num_files++; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, " Copied %s\n", file_list[num_files-1]); } (void) fprintf(stderr,"Number of files are: %d \n", num_files); /* Leili */ if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr, "About to check if we're done file group\n"); fprintf(stderr,"\nnum_files: \t%d",num_files); fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", file_info_list[num_files-1]->study_id); fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", file_info_list[0]->study_id); fprintf(stderr,"\nfile_info_list[num_files-1]->acq_id: \t%d", file_info_list[num_files-1]->acq_id); fprintf(stderr,"\nfile_info_list[0]->acq_id: \t%d", file_info_list[0]->acq_id); } /* Check whether we have reached the end of a group of files */ if (num_files > 1) { if ((file_info_list[num_files-1]->study_id != file_info_list[0]->study_id) || (file_info_list[num_files-1]->acq_id != file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nend of file group detected, process flag set.\n"); } } if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\ndone checking for end of file group\n"); break; /* Unknown command */ default: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is default (an error)\n"); status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* End of switch on pdu_type */ (void) fprintf(stderr,"We break and came out since we didn't have the store command\n"); /* Leili */ /* Delete input message */ if (input_message != NULL) acr_delete_message(input_message); /* Use the files if we have a complete acquisition */ if (process_files) { if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nEntered the `process files' block...\n"); /* Log the fact */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\nCopied one acquisition.\n"); } /* Check for file from next acquisition */ if (have_extra_file) num_files--; /* uncomment to prevent forking */ /* #define NO_FORK */ if (Fork) { /* Fork child to process the files */ child_pid = fork(); } else { child_pid = 0; } if (child_pid > 0) { /* Parent process */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Forked process to create minc files.\n"); } } /* Error forking */ else if (child_pid < 0) { (void) fprintf(stderr, "Error forking child to create minc file\n"); return; } else { /* Child process */ /*******************************************/ /* Added by Leili */ /* Close file descriptors to avoid buffering problems. STDERR is sometimes left open, therefore this command maybe needed */ if(Fork){ int fd; for(fd=getdtablesize()-1; fd>= 0; fd--) { if(fd != 2){ (void) close(fd); } } } /******************************************/ /* Do something with the files */ (void) fprintf(stderr, "We are about to do something with the files.\n"); /* Leili */ (void) fprintf(stderr, "The project name is:%s\n", project_name); /* Leili */ (void) fprintf(stderr, "The pname is:%s\n", pname); /* Leili */ use_the_files(project_name, num_files, file_list, file_info_list,UseArgDir,OutDir); /* Remove the temporary files */ cleanup_files(num_files, file_list); /* Remove the temporary directory if the server has finished */ /* (this does not seem to work all the time) */ if ((temp_dir != NULL) && (kill(server_pid, 0) != 0)) { cleanup_files(1, &temp_dir); } /* Print message about child finishing */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Minc creation process finished.\n"); } if (Fork) { /* Exit from child */ exit(EXIT_SUCCESS); } } /* End of child process */ /* Put blank line in log file */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n"); } /* Reset the lists */ free_list(num_files, file_list, file_info_list); if (have_extra_file) { file_list[0] = file_list[num_files]; file_info_list[0] = file_info_list[num_files]; file_list[num_files] = NULL; file_info_list[num_files] = NULL; } num_files = (have_extra_file ? 1 : 0); } // end of if(process files) if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\n just passed `process files' loop\n"); /* Check for disconnection */ if (state == DISCONNECTING) { continue_looping = FALSE; break; } /* Send reply */ Alarmed_afp = afpout; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status = acr_output_dicom_message(afpout, output_message); (void) alarm(0); /* Delete output message */ if (output_message != NULL) acr_delete_message(output_message); if (status != ACR_OK) { state = TERMINATING; break; } if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nbottom of loop over messages\n"); } /* End of loop over messages */ if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\njust out of loop over messages\n"); /* Free the input and output streams */ acr_close_dicom_file(afpin); acr_close_dicom_file(afpout); /* Save name of first file in last set transferred */ if ((num_files > 0) && (file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name, file_list[0], sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } /* Clean up files, if needed */ if (num_files > 0) { cleanup_files(num_files, file_list); free_list(num_files, file_list, file_info_list); num_files = 0; } FREE(file_list); FREE(file_info_list); /* Remove the file prefix directory (this only happens if it is empty). */ cleanup_files(1, &temp_dir); FREE(temp_dir); /* Check for connection timeout */ if (Connection_timeout) { (void) fprintf(stderr, "Connection timed out.\n"); } /* Print final message */ if ((status == ACR_OK) || (status == ACR_END_OF_INPUT)) { (void) sprintf(exit_string, "Finished transfer."); exit_status = EXIT_SUCCESS; } else { (void) sprintf(exit_string, "%s. Disconnecting.", acr_status_string(status)); exit_status = EXIT_FAILURE; } (void) fprintf(stderr, "The logging set to be %d\n", Do_logging); /* Leili */ (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n%s: %s\n", pname, exit_string); } if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { if (SYSTEM_LOG != NULL) { if ((fptemp = fopen(SYSTEM_LOG, "w")) != NULL) { if ((int) strlen(last_file_name) > 0) { (void) fprintf(fptemp, "%s: File \"%s\"\n", pname, last_file_name); } (void) fprintf(fptemp, "%s: %s\n", pname, exit_string); (void) fclose(fptemp); } } } /* Free the project_name string */ if (project_name != NULL) FREE(project_name); exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : timeout_handler @INPUT : @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to handle connection timeouts. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void timeout_handler(int sig) { Connection_timeout = TRUE; if (Alarmed_afp != NULL) { acr_dicom_set_eof(Alarmed_afp); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : skip_command_groups @INPUT : group_list @OUTPUT : (none) @RETURNS : Pointer to head of group list @DESCRIPTION: Skips over command groups in a group list, returning the rest of the list. @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group skip_command_groups(Acr_Group group_list) { while ((group_list != NULL) && ((acr_get_group_group(group_list) == DCM_PDU_GRPID) || (acr_get_group_group(group_list) == ACR_MESSAGE_GID))) { group_list = acr_get_group_next(group_list); } return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/0002755000175000000620000000000012574624760025046 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/STC_Common_Status.h0000644000175000000620000005237612574624760030536 0ustar stevestaff/*------------------------------------------------------------------------------* * * * File Name : STC_Common_Status.h * * * * Author : M. Rohbrecht CMS / SME 24 Tel. ... 9131 84 4790 * * Author : C. Schaefer CMS / SME 24 Tel. ... 9131 84 4790 * * Author : J.H. Siebert CMS / SME 24 Tel. ... 9131 84 4790 * * * * Description : Based on the definition of the structure of a status * * file, the common (CMS) part can be found in the include * * module `STC_Status.h`. * * * * The contents of this include file is in consistency * * with the definition of the document 'Contents of * * Status-, Configuration,- and Common Files' for the part * * of 'Status File'. * * * * The informations for 'General Installation' and 'Fixed * * Device Conditions' are found inhere. * * * *------------------------------------------------------------------------------* * * * Last Change : July 9th 1990 Creation * * * *------------------------------------------------------------------------------* * * * Last Change : August 6th 1990 Integration of exposure data * * for company S.E.P.P. * * * *------------------------------------------------------------------------------* * * * Last Change : October 22nd 1990 Integration of device data and * * additional camera data. * * * *------------------------------------------------------------------------------* * * * Last Change : December 11th 1990 Integration of modality * * subtype (enum + variable). * * * *------------------------------------------------------------------------------* * * * Last Change : January 17th 1991 Integration of * * - STC_Config_Hostname * * - STC_Config_Consoles * * - STC_Config_Host_Dbhost * * - STC_Config_Host_Dbserver * * - STC_Config_Host_Camera * * * *------------------------------------------------------------------------------* * * * Last Change : January 18th 1991 Integration of * * - STC_Camera_1_Code * * - STC_Camera_2_Code * *------------------------------------------------------------------------------* * * * Last Change : February 15th 1991 by J. H. Siebert * * Integration of * * - Country Code for Japan * * - __STC_Enum_UNDEFINED * * * *------------------------------------------------------------------------------* * * * Last Change : February 22nd 1991 by C. Schaefer * * Integration of * * - STC_Maxicam_Auto_Filmtransport* * * *------------------------------------------------------------------------------* * * * Last Change : March 12th 1991 by C. Schaefer * * Integration of * * - STC_Maxicam_Density * * - STC_Maxicam_LUT * * - STC_Num_Of_Cameras * * * *------------------------------------------------------------------------------* * Last Change : (J.H. Siebert) * * May 16th 1991 STC_PACS_NodeList introduced * * June 25th 1991 STC_PACS_NodeFlag_t is now an int (before: enum), * * __STC_PACS_NodeActiveSUN introduced * * July, 9th 1991 Integration of STC_Customer, STC_City, STC_Destrict * * Aug, 14th 1991 Integration of STC_dsvWinAutoCorr * * * *------------------------------------------------------------------------------* * STC_Device_Net_Installed seems not to be used * *------------------------------------------------------------------------------* * * * Last Change : Sep 9th 1991 by C. Schaefer * * Integration of STC_VCR_Norm * *------------------------------------------------------------------------------* * Last Change : (J.H. Siebert) * * November 4th 1991 New: * * __STC_PACS_Country_Other, __STC_PACS_Country_UK * * STC_DefaultHospitalInstall, * * STC_CustomerHospitalInstall * * STC_MIN_WINAUT_CORR, STC_MAX_WINAUT_CORR * * STC_InstallCountry_t STC_Installation_Country * * * * 30Apr92 CHARM 205149 J.H.Siebert * * Country and PACS code with additional countries * *------------------------------------------------------------------------------*/ #ifndef BASIC_STC_DEFINE #define BASIC_STC_DEFINE TRUE #ifndef __STC_Enum_UNDEFINED #define __STC_Enum_UNDEFINED (-19222) #endif #define CHAR char #define UL unsigned long #define US unsigned short #define SS short #define DO double #define SL long int /*------------------------------------------------------------------------------*/ #define __STC_BASIC_DATA_LEN 8192 /*------------------------------------------------------------------------------* * For those variables of the status file where bitwise definitions are * * needed, no enums are defined, but #define statements. * *------------------------------------------------------------------------------*/ #define __STC_Type_None 0 #define __STC_Type_MINICAM 1 #define __STC_Type_MAXICAM 2 #define __STC_LISA_Present 1 #define __STC_LISA_Not_Present 0 #define __STC_Plotter_Present 1 #define __STC_Plotter_Not_Present 0 #define __STC_Input_Mouse 1 #define __STC_Input_Trackball 2 #define __STC_HostnameLen 8 /*** Ex 16Bytes ?***/ /******************\ * Defines for PACS * * * \******************/ #define __STC_PACS_Country_None "000" #define __STC_PACS_Country_Other "999" #define __STC_PACS_Country_USA "001" /* START CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ #define __STC_PACS_Country_France "033" #define __STC_PACS_Country_Italy "039" /* END CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ #define __STC_PACS_Country_Germany "049" #define __STC_PACS_Country_Japan "081" #define __STC_PACS_Country_UK __STC_PACS_Country_Other /*** NEW May, 16th 1991 J.H. Siebert***/ #define __STC_PACS_NUMOF_NODES 16 #define __STC_PACS_NODE_LOG_LEN __STC_HostnameLen #define __STC_PACS_NODE_PHYS_LEN __STC_PACS_NODE_LOG_LEN /* defines of flags */ typedef int STC_PACS_NodeFlag_t; #define __STC_PACS_NodeNULL 0 #define __STC_PACS_NodeActive 1 #define __STC_PACS_NodeSUN 8 #define __STC_PACS_NodeFlag_UNDEFINED __STC_Enum_UNDEFINED typedef enum { __STC_PACS_NodeOwn = 0, __STC_PACS_NodeRmt1, __STC_PACS_NodeRmt2, __STC_PACS_NodeRmt3, __STC_PACS_NodeRmt4, __STC_PACS_NodeRmt5, __STC_PACS_NodeRmt6, __STC_PACS_NodeRmt7, __STC_PACS_NodeRmt8, __STC_PACS_NodeRmt9, __STC_PACS_NodeRmt10, __STC_PACS_NodeRmt11, __STC_PACS_NodeRmt12, __STC_PACS_NodeRmt13, __STC_PACS_NodeRmt14, __STC_PACS_NodeRmt15, __STC_PACS_Node_UNDEFINED = __STC_Enum_UNDEFINED } STC_PACS_NodeNum_t; typedef struct { STC_PACS_NodeFlag_t flag; /* PACSNET configurationbits */ char log[__STC_PACS_NODE_LOG_LEN+1]; /* logical name */ /*** char log_ext[65-__STC_PACS_NODE_LOG_LEN];***/ /* logical name extension*/ char phys[__STC_PACS_NODE_PHYS_LEN+1]; /* physical name */ /*** char phys_ext[65-__STC_PACS_NODE_PHYS_LEN];***/ /* physical name extension*/ } STC_PACS_node_t; typedef struct { STC_PACS_node_t node[__STC_PACS_NUMOF_NODES]; } STC_PACS_nodelist_t; /*****************************************************************/ #define __STC_Net_Present 1 #define __STC_Net_Not_Present 0 /*------------------------------------------------------------------------------* * * * In case of a defined and limited value range enum definitions are used. * * * *------------------------------------------------------------------------------*/ typedef enum { __STC_Modality_CT = 1, __STC_Modality_MR, __STC_Modality_UNDEFINED = __STC_Enum_UNDEFINED } STC_Modality_t; typedef enum { __STC_Modality_Sub_M2 = 0, __STC_Modality_Sub_P8 = 1, __STC_Modality_Sub_UNDEFINED = __STC_Enum_UNDEFINED } STC_Modality_Sub_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Weight_Kg = 1, __STC_Weight_lbs, __STC_Weight_UNDEFINED = __STC_Enum_UNDEFINED } STC_Weight_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Main_Console = 1, __STC_Satellite_Console, __STC_Console_UNDEFINED = __STC_Enum_UNDEFINED } STC_Console_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Language_English = 1, __STC_Language_German, __STC_Language_UNDEFINED = __STC_Enum_UNDEFINED } STC_Language_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Device_Type_Unknown = 1, __STC_Device_Type_Magnetic_Disk, __STC_Device_Type_Magnetic_Tape, __STC_Device_Type_Optical_Disk, __STC_Device_Type_UNDEFINED = __STC_Enum_UNDEFINED } STC_Device_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Net_Installed = 1, __STC_Net_Not_Installed, __STC_Net_UNDEFINED = __STC_Enum_UNDEFINED } STC_Net_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_Minicam_Matrix_512 = 1, __STC_Minicam_Matrix_484, __STC_Minicam_Matrix_UNDEFINED = __STC_Enum_UNDEFINED } STC_Minicam_Matrix_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_VCR_NORM_PAL = 1, __STC_VCR_NORM_NTSC, __STC_VCR_NORM_UNDEFINED = __STC_Enum_UNDEFINED } STC_VCR_Norm_t; /*------------------------------------------------------------------------------*/ typedef enum { __STC_List_Lokalizer_F = 1, __STC_List_Lokalizer_A, __STC_List_Lokalizer_N, __STC_List_Lokalizer_UNDEFINED = __STC_Enum_UNDEFINED } STC_List_Lokalizer_Default_t; typedef enum { __STC_NULL_VERSION = 0, __STC_91E5_VERSION, __STC_91E7_VERSION, __STC_91E11_VERSION, __STC_Version_UNDEFINED = __STC_Enum_UNDEFINED } STC_Version_t; #define __STC_INFO_VERSION_LEN 32 typedef struct { int flag; char version[__STC_INFO_VERSION_LEN+1]; } STC_Info_t; typedef struct { char space[__STC_BASIC_DATA_LEN - (sizeof(STC_Version_t) + sizeof(STC_Info_t))]; STC_Info_t info; STC_Version_t Version; } structInfo_t; #define __STC_CustomerLen 26 #define __STC_CityLen 26 #define __STC_DestrictLen 26 #define __STC_InstallationLen 26 #define __STC_CustomerHospitalNUM 3 /*********************************\ * * * MR requirement (ui_window, ...) * * * \*********************************/ #define STC_MIN_WINAUT_CORR 0 #define STC_MAX_WINAUT_CORR 5 typedef struct { int center; int width; int part; } STC_dsvWinAutoCorr_t; /***************************\ * * * MR requirement (M. Gress) * * * \***************************/ /* START CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ typedef enum { __STC_InstallCountry_USA = 1, __STC_InstallCountry_GERMANY, __STC_InstallCountry_UK, __STC_InstallCountry_JAPAN, __STC_InstallCountry_FRANCE, __STC_InstallCountry_ITALY, __STC_InstallCountry_OTHERS = 99, __STC_InstallCountry_UNDEFINED = __STC_Enum_UNDEFINED } STC_InstallCountry_t; /* END CHANGE CHARM 205149 J.H.Siebert 30Apr92 */ /*------------------------------------------------------------------------------* * * * Union and structure definition of the 8 KByte of common status file * * data * * * *------------------------------------------------------------------------------*/ union __STC_BASIC_DATA { char space [__STC_BASIC_DATA_LEN]; /* 8 blocks for map */ structInfo_t structInfo; struct blablabla /* Name needed only for STC tool for */ /* naming within the debugger - Dummy */ { /*------------------------------------------------------------------------------* * * * Manufacturer and installation * * * *------------------------------------------------------------------------------*/ CHAR STC_Manufacturer [8+1]; /* Name of Manufacturer, e.g. */ /* SIEMENS. */ CHAR STC_Manufacturer_Model [26+1]; /* Manufacturer's model, e.g. */ /* SOMATOM, MAGNETOM */ CHAR STC_Installation_Name [__STC_InstallationLen+1]; /* Name of hospital, instal. */ CHAR STC_Software_Version [8+1]; /* Software version for image */ /* text */ UL STC_Int_Software_Version; /* Internal software version */ /*------------------------------------------------------------------------------* * PACS data * *------------------------------------------------------------------------------*/ CHAR STC_PACS_Country_Code [3+1]; /* Country code for PACS */ /* installation */ CHAR STC_PACS_Identification [3+1]; /* Manufacturer PACS identifi- */ /* cation */ CHAR STC_PACS_Modality [4+1]; /* Modality PACS identification */ #ifndef VERSION_NO_PACS STC_PACS_nodelist_t STC_PACS_NodeList; /* List of nodes in net */ #endif UL STC_Serial_Number; /* Serial number of installation*/ UL STC_System_Code; /* System Code - to be defined */ STC_Modality_t STC_Modality; /* System Modality */ STC_Language_t STC_System_Language; /* User language */ STC_Weight_t STC_Weight_System; /* Weight system */ UL STC_Net_Frequency; /* Net frequency in Hz */ STC_Console_t STC_Console_Type; /* Either main or satellite */ /*------------------------------------------------------------------------------* * * * Camera data * * * *------------------------------------------------------------------------------*/ UL STC_Camera_Display_Birthday; /* Text superimposing of birth- */ /* day */ /* Yes = 1 */ /* No = 0 */ UL STC_Camera_1_Type; /* Type of instal. camera no. 1 */ /* = 0 */ /* MINICAM = 1 */ /* MAXICAM = 2 */ /* . . . */ UL STC_Camera_2_Type; /* Type of instal. camera no. 2 */ /* = 0 */ /* MINICAM = 1 */ /* MAXICAM = 2 */ /* . . . */ UL STC_Camera_1_Code; /* Code for type of instal. */ /* camera no. 1 */ /* = 0 */ /* DIGICAM = 1 */ /* . . . */ UL STC_Camera_2_Code; /* Code for type of instal. */ /* camera no. 2 */ /* = 0 */ /* DIGICAM = 1 */ /* . . . */ CHAR STC_Camera_1_Name [40+1]; /* Name of camera no. 1 */ CHAR STC_Camera_2_Name [40+1]; /* Name of camera no. 2 */ UL STC_Camera_1_Copy_Range; /* Copy range for exposure */ UL STC_Camera_1_Low_Level_Timer; /* Low level timer */ UL STC_Camera_1_High_Level_Timer; /* High level timer */ UL STC_Camera_2_Copy_Range; /* Copy range for exposure */ UL STC_Camera_2_Low_Level_Timer; /* Low level timer */ UL STC_Camera_2_High_Level_Timer; /* High level timer */ /*------------------------------------------------------------------------------* * * * Special entries for MINICAM only * * * * A maximum of 100 different film formats is possible. The actual valid * * number of formats is inquired within the installation. Only those ele- * * ments are supplied during instalation. * * * *------------------------------------------------------------------------------*/ UL STC_Minicam_Num_of_Formats; /* Numver of valid formats */ UL STC_Minicam_Filmsize [100]; UL STC_Minicam_Format [100]; UL STC_Minicam_Format_Code [100]; US STC_Minicam_Row [100]; US STC_Minicam_Col [100]; STC_Minicam_Matrix_t STC_Minicam_Matrixsize; /*------------------------------------------------------------------------------* * * * Lokalizer components * * * *------------------------------------------------------------------------------*/ STC_List_Lokalizer_Default_t STC_List_Lokalizer_Default; /*------------------------------------------------------------------------------* * * * SMIV hardware components * * * *------------------------------------------------------------------------------*/ UL STC_LISA_Present; /* Is component LISA installed */ /* Yes = 1 */ /* No = 0 */ /* . . . */ UL STC_Plotter_Present; /* Is a plotter installed */ /* Yes = 1 */ /* No = 0 */ /* . . . */ UL STC_SIAM_Memsize; /* Number of MBytes for SIAM */ UL STC_Num_of_Mem_Board; /* Number of boards for additi- */ /* onal imager memory */ UL STC_Add_Mem_Size; /* Number of MByte per board */ /* for additional imager memory */ /*------------------------------------------------------------------------------* * * * Graphical input medium * * * *------------------------------------------------------------------------------*/ UL STC_Graphic_Input_Type; /* Kind of graphical input : */ /* Mouse = 1 */ /* Trackball = 2 */ /* . . . */ UL STC_Graphic_Input_Character [3]; /* Factors for hysteresis */ /* curve oF sensitivity of */ /* graphical input */ /*------------------------------------------------------------------------------* * * * Patient storage device data * * * *------------------------------------------------------------------------------*/ STC_Device_t STC_Device_Type [10]; /* Kind of device : */ /* Magnetic Disk */ /* Magnetic Tape */ /* Optical Disk */ /* . . . */ CHAR STC_Device_Manufacturer_Name [10] [19+1]; /* Name of Device manufacturer */ UL STC_Device_Net_Installed; /* Is a Net installed : */ /* Yes = 1 */ /* No = 0 */ /* . . . */ /*------------------------------------------------------------------------------* * * * Installation status encoding * * * *------------------------------------------------------------------------------*/ UL STC_Installation_Status; /* Encoded status of already */ /* done installation steps (e.g.*/ /* first installation, partial */ /* installation, etc.) */ /*------------------------------------------------------------------------------* * * * Modality sub-type. This field contains informations about * * variants of the modality described in STC_Modality (e.g. * * STC_Modality == MR and STC_Modality_Sub == STC_Modality_Sub_P8). * * This field can be used by both, CT and MR modalities. * * * * * *------------------------------------------------------------------------------*/ STC_Modality_Sub_t STC_Modality_Sub; /* System Modality Sub-type */ /*------------------------------------------------------------------------------* * * * Host configuration data. * * * * * *------------------------------------------------------------------------------*/ CHAR STC_Config_Hostname [__STC_HostnameLen+1]; /* Name of own host */ CHAR STC_Config_Consoles [8][__STC_HostnameLen+1]; /* Name of other hosts */ CHAR STC_Config_Host_Dbhost [__STC_HostnameLen+1]; /* Name of database host */ CHAR STC_Config_Host_Dbserver [__STC_HostnameLen+1]; /* Name of database server */ CHAR STC_Config_Host_Camera [__STC_HostnameLen+1]; /* Name of camera host */ /*------------------------------------------------------------------------------* * Special entries for MAXICAM only * *------------------------------------------------------------------------------*/ UL STC_Maxicam_Auto_Filmtransport; /* */ UL STC_Maxicam_Density; /* */ UL STC_Maxicam_LUT; /* */ UL STC_Num_Of_Cameras; /* */ /*------------------------------------------------------------------------------* * CT requirement * *------------------------------------------------------------------------------*/ CHAR STC_Customer [__STC_CustomerLen+1]; /* Name of customer */ CHAR STC_City [__STC_CityLen+1]; /* Name of city */ CHAR STC_Destrict [__STC_DestrictLen+1]; /* Name of destrict */ /*------------------------------------------------------------------------------* * MR requirement * *------------------------------------------------------------------------------*/ STC_dsvWinAutoCorr_t STC_dsvWinAutoCorr; /*------------------------------------------------------------------------------* * MR requirement * *------------------------------------------------------------------------------*/ STC_VCR_Norm_t STC_VCR_Norm; /* Videonorm of Camera */ /*------------------------------------------------------------------------------* * New requirement * *------------------------------------------------------------------------------*/ STC_InstallCountry_t STC_Installation_Country; CHAR STC_DefaultHospitalInstall[__STC_InstallationLen+1]; /* Name of default hospital */ CHAR STC_CustomerHospitalInstall [__STC_CustomerHospitalNUM] [__STC_InstallationLen+1]; /* Name of customer hospital, via ui_text installed */ } s; }; /*------------------------------------------------------------------------------*/ struct STC_COMMON { union __STC_BASIC_DATA Basic; }; /*------------------------------------------------------------------------------*/ #endif /*------------------------------------------------------------------------------*/ minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_include_files.h0000644000175000000620000000236612574624760030517 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_include_files.h Description: The header file include all necessary header files for data set library functions. The "#ifndef XXX" - "#endif" construct in the different header files avoid a multiple type, parameter, constant or macro definition. Don't change the order because the header files are implemented as a hierachy and so not indepent of calling order. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_INCLUDE_FILES #define DS_INCLUDE_FILES /* PRECOMILER: sepecial include files */ #include /* PRECOMILER: data set library include files */ #include #include #include #include #include #include #include #include #include #include #include #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_type.h0000644000175000000620000001235412574624760027652 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_type.h Description: The header file defines the data set basic groups as unions for internal use (internal header). Unions are used to overlay a buffer and the group structure for more flexi- bility and compatibility. To expand a group the new item must only be defined at the structure end in header file "ds_head_acr_groups_types.h" or "ds_head_shadow_groups_types.h". Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_TYPE #define DS_HEAD_TYPE typedef union group_0008_tag { char Ide_buf[LENGTH_GROUP_0008]; /* fill-in */ acr_identifying_t Ide; /* work area */ } group_0008_t; typedef union group_0009_tag { char Ide_buf[LENGTH_GROUP_0009]; /* fill-in */ shadow_identifying_t Ide; /* work area */ } group_0009_t; typedef union group_0010_tag { char Pat_buf[LENGTH_GROUP_0010]; /* fill-in */ acr_patient_t Pat; /* work area */ } group_0010_t; typedef union group_0011_tag { char Pat_buf[LENGTH_GROUP_0011]; /* fill-in */ shadow_patient_t Pat; /* work area */ } group_0011_t; typedef union group_0013_tag { char PatMod_buf[LENGTH_GROUP_0013]; /* fill-in */ shadow_patient_modify_t PatMod; /* work area */ } group_0013_t; typedef union group_0018_tag { char Acq_buf[LENGTH_GROUP_0018]; /* fill-in */ acr_acquisition_t Acq; /* work area */ } group_0018_t; typedef union group_0019_part1_tag { char Acq1_buf[LENGTH_GROUP_0019_PART1]; /* fill-in */ shadow_acquisition_cms_t CM; /* work area */ } group_0019_part1_t; typedef union group_0019_part2_tag { char Acq2_buf[LENGTH_GROUP_0019_PART2]; /* fill-in */ shadow_acquisition_ct_t Ct; /* CT common work area */ shadow_acquisition_mr_t Mr; /* MR common work area */ } group_0019_part2_t; typedef union group_0019_part3_tag { char Acq3_buf[LENGTH_GROUP_0019_PART3]; /* fill-in */ shadow_acquisition_ct_conf_t Ct; /* CT configuration work area */ shadow_acquisition_mr_conf_t Mr; /* MR configuration work area */ } group_0019_part3_t; typedef union group_0019_part4_tag { char Acq4_buf[LENGTH_GROUP_0019_PART4]; /* fill-in */ shadow_acquisition_acq_t CM; /* work area */ } group_0019_part4_t; typedef struct group_0019_tag { group_0019_part1_t Acq1; /* CMS subgroup */ group_0019_part2_t Acq2; /* common subgroup */ group_0019_part3_t Acq3; /* config. and adjust subgroup */ group_0019_part4_t Acq4; /* acquisition subgroup */ } group_0019_t; typedef union group_0020_tag { char Rel_buf[LENGTH_GROUP_0020]; /* fill-in */ acr_relationship_t Rel; /* work area */ } group_0020_t; typedef union group_0021_part1_tag { char Rel1_buf[LENGTH_GROUP_0021_PART1]; /* fill-in */ shadow_relationship_med_cms_t CM; /* work area */ } group_0021_part1_t; typedef union group_0021_part2_tag { char Rel2_buf[LENGTH_GROUP_0021_PART2]; /* fill-in */ shadow_relationship_ct_t Ct; /* CT common work area */ shadow_relationship_mr_t Mr; /* MR common work area */ } group_0021_part2_t; typedef union group_0021_part3_tag { char Rel2_buf[LENGTH_GROUP_0021_PART3]; /* fill-in */ shadow_relationship_ct_spe_t Ct; /* CT special work area */ shadow_relationship_mr_spe_t Mr; /* MR special work area */ } group_0021_part3_t; typedef struct group_0021_tag { group_0021_part1_t Rel1; /* CMS subgroup */ group_0021_part2_t Rel2; /* common subgroup */ group_0021_part3_t Rel3; /* special subgroup */ } group_0021_t; typedef union group_0028_tag { char Pre_buf[LENGTH_GROUP_0028]; /* fill-in */ acr_presentation_t Pre; /* work area */ } group_0028_t; typedef union group_0029_tag { char Pre_buf[LENGTH_GROUP_0029]; /* fill-in */ shadow_presentation_t Pre; /* work area */ } group_0029_t; typedef union group_0051_tag { char Txt_buf[LENGTH_GROUP_0051]; /* fill-in */ image_text_t Txt; /* work area */ } group_0051_t; typedef union fill_to_border_tag { char Fill_buf[LENGTH_TO_FILL_K_BORDER]; /* fill-in */ } fill_to_border_t; /*******************/ /* internal header */ /*******************/ typedef struct header_tag { group_0008_t G08; /* Identifying Information */ group_0009_t G09; group_0010_t G10; /* Patient Information */ group_0011_t G11; group_0013_t G13; /* Patient Modify Information */ group_0018_t G18; /* Acquisition Information */ group_0019_t G19; group_0020_t G20; /* Relationship Information */ group_0021_t G21; group_0028_t G28; /* Image Presentation Information */ group_0029_t G29; group_0051_t G51; /* Image Text */ } header_t; #endif /*==========================================================================================*/ #ifdef DS_STC_TOOL char *_STC_HELP[] = { #include #include 0 }; #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_image_text_type.h0000644000175000000620000002645412574624760032066 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_image_text_type.h Description: The header file defines the data set image text for internal use (internal image text area). The image text parameter names are not NEMA item names. Parameters with equal names can but must not contain equal information. The sign "->" means, this parameter is derivated from listed NEMA items. For more information please see document "Data Set Format" chapter "Group 0051'H Image Text". Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_IMAGE_TEXT_TYPE #define DS_HEAD_IMAGE_TEXT_TYPE /* PRECOMPILER: define length constants */ #define DS_PATIENT_NUMBER_SIZE 12 #define DS_PATIENT_DATE_SIZE 11 #define DS_PATIENT_POSITION_SIZE 11 #define DS_IMAGE_NUMBER_SIZE 11 #define DS_IMAGE_NUMBER_TEXT "IMAGE" #define DS_LABEL_SIZE 5 #define DS_DATE_OF_MEASUREMENT_SIZE 11 #define DS_TIME_OF_MEASUREMENT_SIZE 5 #define DS_TIME_OF_ACQUISITION_SIZE 11 #define DS_TIME_OF_ACQUISITION_TEXT_CT "TI" #define DS_TIME_OF_ACQUISITION_TEXT_MR "TA " #define DS_NUMBER_OF_ACQUISITIONS_SIZE 11 #define DS_NUMBER_OF_ACQUISITIONS_TEXT "AC" #define DS_COMMENT_NO1_SIZE 26 #define DS_COMMENT_NO2_SIZE 26 #define DS_INSTALLATION_NAME_SIZE 26 #define DS_SOFTWARE_VERSION_SIZE 11 #define DS_MATRIX_SIZE 11 #define DS_TYPE_OF_MEASUREMENT_SIZE 11 #define DS_SCAN_NUMBER_SIZE 11 #define DS_SCAN_NUMBER_TEXT "SCAN" #define DS_REPETITION_TIME_SIZE 11 #define DS_REPETITION_TIME_TEXT "TR" #define DS_ECHO_TIME_SIZE 11 #define DS_ECHO_TIME_TEXT "TE" #define DS_GATING_AND_TRIGGER_SIZE 11 #define DS_GATING_AND_TRIGGER_TEXT "TD" #define DS_TUBE_CURRENT_SIZE 11 #define DS_TUBE_CURRENT_TEXT "mA" #define DS_TUBE_VOLTAGE_SIZE 11 #define DS_TUBE_VOLTAGE_TEXT "kV" #define DS_SLICE_THICKNESS_SIZE 11 #define DS_SLICE_THICKNESS_TEXT "SL" #define DS_SLICE_POSITION_SIZE 11 #define DS_SLICE_POSITION_TEXT "SP" #define DS_SLICE_ORIENTATION_NO1_SIZE 11 #define DS_SLICE_ORIENTATION_NO2_SIZE DS_SLICE_ORIENTATION_NO1_SIZE #define DS_COR_TEXT "Cor" #define DS_SAG_TEXT "Sag" #define DS_TRA_TEXT "Tra" #define DS_FIELD_OF_VIEW_SIZE 11 #define DS_FIELD_OF_VIEW_TEXT "FoV" #define DS_ZOOM_CENTER_SIZE 11 #define DS_ZOOM_CENTER_TEXT "CE" #define DS_ZOOM_CENTER_TEXT_MR "MF" #define DS_GANTRY_TILT_SIZE 11 #define DS_GANTRY_TILT_TEXT "GT" #define DS_TABLE_POSITION_SIZE 11 #define DS_TABLE_POSITION_TEXT "TP" #define DS_MIP_HEADLINE_SIZE 3 #define DS_MIP_HEADLINE_TEXT "VOI" #define DS_MIP_LINE_SIZE 15 #define DS_MIP_LINE_TEXT "Lin" #define DS_MIP_COLUMN_SIZE 15 #define DS_MIP_COLUMN_TEXT "Col" #define DS_MIP_SLICE_SIZE 15 #define DS_MIP_SLICE_TEXT "Sli" #define DS_STUDY_NUMBER_SIZE 11 #define DS_STUDY_NUMBER_TEXT "STUDY" #define DS_CONTRAST_SIZE 5 #define DS_CONTRAST_TEXT_CT "+C IV" #define DS_CONTRAST_TEXT_MR "+C " #define DS_CONTRAST_TEXT_NONE " " #define DS_PATIENT_BIRTHDATE_SIZE 11 #define DS_SEQUENCE_INFO_SIZE 11 #define DS_SATURATION_REGIONS_SIZE 11 #define DS_SATURATION_REGIONS_TEXT "SAT" #define DS_DATA_SET_ID_SIZE 26 #define DS_DATA_SET_ID_TEXT_STUDY "STU" #define DS_DATA_SET_ID_TEXT_IMAGE "IMA" #define DS_DATA_SET_ID_TEXT_DELIMITER "/" #define DS_MAGNIFICATION_FACTOR_SIZE 11 #define DS_MAGNIFICATION_FACTOR_TEXT "MF" #define DS_MANUFACTURER_MODEL_SIZE 26 #define DS_PATIENT_NAME_SIZE 26 #define DS_TIME_OF_SCANNING_SIZE 8 /* DECLARATION: image text type */ typedef struct image_text_tag { char PatientNumber[DS_PATIENT_NUMBER_SIZE + 1]; /* -> Patient Id */ char PatientSexAndAge[DS_PATIENT_DATE_SIZE + 1]; /* -> Patient Sex, Patient Age */ char PatientPosition[DS_PATIENT_POSITION_SIZE + 1]; /* -> Patient Rest Direction, ... */ char ImageNumber[DS_IMAGE_NUMBER_SIZE + 1]; /* -> Image */ char Label[DS_LABEL_SIZE + 1]; /* -> Archiving Mark Mask, ... */ char DateOfMeasurement[DS_DATE_OF_MEASUREMENT_SIZE + 1]; /* -> Acquisition Date */ char TimeOfMeasurement[DS_TIME_OF_MEASUREMENT_SIZE + 1]; /* -> Acquisition Time */ char TimeOfAcquisition[DS_TIME_OF_ACQUISITION_SIZE + 1]; /* -> CT: Exposure Time -> MR: Total Measurement Time */ char NumberOfAcquisitions[DS_NUMBER_OF_ACQUISITIONS_SIZE + 1]; /* -> Number of Averages */ char CommentNo1[DS_COMMENT_NO1_SIZE + 1]; /* -> Procedure Description */ char CommentNo2[DS_COMMENT_NO2_SIZE + 1]; char InstallationName[DS_INSTALLATION_NAME_SIZE + 1]; /* -> Institution ID */ char SoftwareVersion[DS_SOFTWARE_VERSION_SIZE + 1]; /* -> Software Version */ char Matrix[DS_MATRIX_SIZE + 1]; /* -> Rows, Columns */ char TypeOfMeasurement[DS_TYPE_OF_MEASUREMENT_SIZE + 1]; /* -> Calculation Mode */ char ScanNumber[DS_SCAN_NUMBER_SIZE + 1]; /* -> Acquisition */ char RepetitionTime[DS_REPETITION_TIME_SIZE + 1]; /* -> Repetition Time */ char EchoTime[DS_ECHO_TIME_SIZE + 1]; /* -> Echo Time */ char GatingAndTrigger[DS_GATING_AND_TRIGGER_SIZE + 1]; /* -> Signal Mask */ char TubeCurrent[DS_TUBE_CURRENT_SIZE + 1]; /* -> Exposure */ char TubeVoltage[DS_TUBE_VOLTAGE_SIZE + 1]; /* -> Generator Power */ char SliceThickness[DS_SLICE_THICKNESS_SIZE + 1]; /* -> Slice Thickness */ char SlicePosition[DS_SLICE_POSITION_SIZE + 1]; /* -> Image Distance */ char SliceOrientationNo1[DS_SLICE_ORIENTATION_NO1_SIZE + 1]; /* -> Image Position, ... */ char SliceOrientationNo2[DS_SLICE_ORIENTATION_NO2_SIZE + 1]; char FieldOfView[DS_FIELD_OF_VIEW_SIZE + 1]; /* -> Field of View */ char ZoomCenter[DS_ZOOM_CENTER_SIZE + 1]; /* -> Target */ char GantryTilt[DS_GANTRY_TILT_SIZE + 1]; /* -> Gantry Tilt */ char TablePosition[DS_TABLE_POSITION_SIZE + 1]; /* -> Location */ char MipHeadLine[DS_MIP_HEADLINE_SIZE + 1]; /* -> */ char MipLine[DS_MIP_LINE_SIZE + 1]; /* -> MIP x Row */ char MipColumn[DS_MIP_COLUMN_SIZE + 1]; /* -> MIP x Column */ char MipSlice[DS_MIP_SLICE_SIZE + 1]; /* -> MIP x Slice */ char StudyNumber[DS_STUDY_NUMBER_SIZE + 1]; /* -> Study */ char Contrast[DS_CONTRAST_SIZE + 1]; /* -> Contrast Agent */ char PatientBirthdate[DS_PATIENT_BIRTHDATE_SIZE + 1]; /* -> Patient Birthday */ char SequenceInformation[DS_SEQUENCE_INFO_SIZE + 1]; /* -> Sequence File Owner, ... */ char SaturationRegions[DS_SATURATION_REGIONS_SIZE + 1]; /* -> Saturation Regions, ... */ char DataSetId[DS_DATA_SET_ID_SIZE + 1]; /* -> Image, Study */ char MagnificationFactor[DS_MAGNIFICATION_FACTOR_SIZE + 1]; /* -> Image Maginification Factor */ char ManufacturerModel[DS_MANUFACTURER_MODEL_SIZE + 1]; /* -> Manufacturer Model */ char PatientName[DS_PATIENT_NAME_SIZE + 1]; /* -> Patient Name */ char TimeOfScanning[DS_TIME_OF_SCANNING_SIZE + 1]; /* -> Acquisition Time */ } image_text_t; #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_basic_types.h0000644000175000000620000002405012574624760031172 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_basic_types.h Description: The header file defines the basic types for internal header. Each enumerator identifier is build up as a type qualification and the enumerator item value defined in document [DS Item Format] paragraphs "item "item contents". As exampel: Rotation_CC Rotation --> identifier for item Rotation Direction (0018,1140 CC --> counter clockwise rotation No enumerator value is set to zero to avoid problems e.g. if an automatic initialication during allocation was done. Zore is not a unique identification, because several item values equal zero are defined. The used bit masks are not defined with C-language type bit array, because the internal representation is compiler dependent. To handle not CMS created bit masks (e.g. form foreign manufacturer NEMA data set) the data set library macros DS_BIT_xxx() are used. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_BASIC_TYPES #define DS_HEAD_BASIC_TYPES typedef enum cardiac_code_tag { Cardiac_Code_COMMON = 1, Cardiac_Code_CONFIRM = 2, Cardiac_Code_DIASTOLE = 3, Cardiac_Code_NONE = 4, Cardiac_Code_SYSTOLE = 5, Cardiac_Code_UNDEFINED = Enum_UNDEFINED } cardiac_code_t; typedef enum { Calculation_m_A = 20, Calculation_m_NONE = 1, Calculation_m_PC = 21, Calculation_m_PU = 22, Calculation_m_UNDEFINED = Enum_UNDEFINED } calculation_mode_m_t; typedef enum calculation_mode_s_tag { Calculation_s_BSP = 1, Calculation_s_IRS = 2, Calculation_s_NONE = 3, Calculation_s_SUN = 4, Calculation_s_VAX = 5, Calculation_s_UNDEFINED = Enum_UNDEFINED } calculation_mode_s_t; typedef struct calculation_mode_tag { calculation_mode_m_t M; calculation_mode_s_t S; } calculation_mode_t; typedef enum compression_code_tag { Compression_DONE = 2, Compression_NONE = 1, Compression_UNDEFINED = Enum_UNDEFINED } compression_code_t; typedef enum contrast_tag { Contrast_NONE = 1, Contrast_APPLIED = 2, Contrast_UNDEFINED = Enum_UNDEFINED } contrast_t; typedef enum data_object_subtype_m_tag { Object_m_CT = 1, Object_m_MRS = 2, Object_m_MRU = 3, Object_m_UNDEFINED = Enum_UNDEFINED } data_object_subtype_m_t; typedef enum data_object_subtype_d_tag { Object_d_P = 1, Object_d_U = 2, Object_d_UNDEFINED = Enum_UNDEFINED } data_object_subtype_d_t; typedef enum data_object_subtype_s_tag { Object_s_NONE = 1, Object_s_UNDEFINED = Enum_UNDEFINED } data_object_subtype_s_t; typedef struct data_object_subtype_tag { data_object_subtype_m_t M; data_object_subtype_d_t D; data_object_subtype_s_t S; } data_object_subtype_t; typedef enum data_set_subtype_m_tag { Set_m_IMAGE = 1, Set_m_PLOT = 2, Set_m_RAW = 3, Set_m_SPECT = 4, Set_m_TEXT = 5, Set_m_UNDEFINED = Enum_UNDEFINED } data_set_subtype_m_t; typedef enum data_set_subtype_s_tag { /* NOTE: data_set_subtype_s_tag */ /* If this basic data type "data_set_subtype_s_t" is changed please check also the basic data type "measurement_mode_s_t" and the sequence "determine image type" in data set library function "ds_get_image_text_type()". */ /* To find a free enum number you can use the following command "sort -t= +1 " */ Set_s_NONE = 1, Set_s_DYNA = 14, Set_s_HIS = 19, Set_s_HISC = 60, Set_s_PLOT = 18, Set_s_QUAL = 17, Set_s_ROT = 16, Set_s_SCAN = 10, Set_s_SINC = 11, Set_s_SIN = 12, Set_s_STAT = 15, Set_s_R2D = 62, Set_s_R3D = 63, Set_s_TOPO = 13, Set_s_CAR = 20, Set_s_MC = 21, Set_s_BLK = 41, Set_s_FPA = 42, Set_s_PROJ = 43, Set_s_READ = 44, Set_s_VFLO = 45, Set_s_VFPA = 46, Set_s_VSUM = 47, Set_s_CFL = 51, Set_s_CSH = 52, Set_s_UNDEFINED = Enum_UNDEFINED } data_set_subtype_s_t; typedef struct data_set_subtype_tag { data_set_subtype_m_t M; data_set_subtype_s_t S; } data_set_subtype_t; typedef struct gradient_delay_time_tag { double X; double Y; double Z; } gradient_delay_time_t; typedef struct field_of_view_tag { double Height; double Width; } field_of_view_t; typedef struct filter_parameter_tag { double Value1; double Value2; double Value3; double Value4; } filter_parameter_t; typedef enum filter_type_tag { Filter_EXTERNAL = 5, Filter_FERMI = 1, Filter_GAUSS = 2, Filter_HANNING = 3, Filter_NONE = 4, Filter_UNDEFINED = Enum_UNDEFINED } filter_type_t; typedef enum filter_type_image_tag { Filter_Image_NO1 = 1, Filter_Image_NONE = 4, Filter_Image_UNDEFINED = Enum_UNDEFINED } filter_type_image_t; typedef enum gate_phase_tag { Gate_EXPIRATION = 1, Gate_INSPIRATION = 2, Gate_UNDEFINED = Enum_UNDEFINED } gate_phase_t; typedef enum geometry_tag { Geometry_CURVED = 3, Geometry_PLANAR = 1, Geometry_UNRAVEL = 2, Geometry_UNDEFINED = Enum_UNDEFINED } geometry_t; typedef enum image_format_tag { Format_RECT = 1, Format_UNDEFINED = Enum_UNDEFINED } image_format_t; typedef struct image_id_tag { char gap[LENGTH_LABEL + 1]; char PatientName[LENGTH_LABEL + 1]; char PatientId[LENGTH_LABEL + 1]; long Image; } image_id_t; typedef struct image_location_tag { double Sag; double Cor; double Tra; } image_location_t; typedef struct int_point_tag { long X; long Y; long Z; } int_point_t; typedef enum laterality_tag { Laterality_L = 1, Laterality_NO = 2, Laterality_R = 3, Laterality_UNDEFINED = Enum_UNDEFINED } laterality_t; typedef enum measurement_mode_m_tag { Measurement_m_ADJU = 1, Measurement_m_EXAM = 2, Measurement_m_TEST = 3, Measurement_m_UNDEFINED = Enum_UNDEFINED } measurement_mode_m_t; typedef enum measurement_mode_s_tag { Measurement_s_NONE = 1, Measurement_s_DYNA = 14, Measurement_s_HIST = 19, Measurement_s_PLOT = 18, Measurement_s_QUAL = 17, Measurement_s_ROT = 16, Measurement_s_SCAN = 10, Measurement_s_SINC = 11, Measurement_s_SINZ = 12, Measurement_s_STAT = 15, Measurement_s_R2D = 22, Measurement_s_R3D = 23, Measurement_s_TOPO = 13, Measurement_s_UNDEFINED = Enum_UNDEFINED } measurement_mode_s_t; /* check also data_set_subtype_s_t */ typedef struct measurement_mode_tag { measurement_mode_m_t M; measurement_mode_s_t S; } measurement_mode_t; typedef enum modality_tag { Modality_CT = 1, Modality_MR = 2, Modality_UNDEFINED = Enum_UNDEFINED } modality_t; typedef enum nucleus_tag { Nucleus_C = 1, Nucleus_F = 2, Nucleus_H = 3, Nucleus_N = 4, Nucleus_NA = 5, Nucleus_P = 6, Nucleus_UNDEFINED = Enum_UNDEFINED } nucleus_t; typedef struct object_orientation_tag { double Phi; double Theta; double Radius; } object_orientation_t; typedef struct object_threshold_tag { long LowerBoundary; long UpperBoundary; } object_threshold_t; typedef enum order_of_slices_tag { Slice_Order_ASCENDING = 1, Slice_Order_DECREASING = 2, Slice_Order_FREE = 3, Slice_Order_INTERLEAVED = 4, Slice_Order_NONE = 5, Slice_Order_UNDEFINED = Enum_UNDEFINED } order_of_slices_t; typedef struct patient_orientation_tag { char Y[LENGTH_ORIENTATION + 1]; /* up - down */ char X[LENGTH_ORIENTATION + 1]; /* left - right */ char Z[LENGTH_ORIENTATION + 1]; /* back - front */ } patient_orientation_t; typedef enum patient_phase_tag { Phase_ADULT = 1, Phase_CHILD = 2, Phase_UNDEFINED = Enum_UNDEFINED } patient_phase_t; typedef enum patient_position_tag { Position_LEFT = 1, Position_PRONE = 2, Position_RIGHT = 3, Position_SUPINE = 4, Position_UNDEFINED = Enum_UNDEFINED } patient_position_t; typedef enum patient_region_tag { Region_BODY = 1, Region_HEAD = 2, Region_UNDEFINED = Enum_UNDEFINED } patient_region_t; typedef struct pixel_size_tag { double Row; double Col; } pixel_size_t; typedef enum pixel_quality_mode_tag { Pixel_Quality_ESTIMATED = 1, Pixel_Quality_EXACT = 2, Pixel_Quality_NONE = 3, Pixel_Quality_UNDEFINED = Enum_UNDEFINED } pixel_quality_mode_t; typedef struct pixel_quality_code_tag { pixel_quality_mode_t Min; pixel_quality_mode_t Mean; pixel_quality_mode_t Max; } pixel_quality_code_t; typedef struct pixel_quality_value_tag { long Min; long Mean; long Max; } pixel_quality_value_t; typedef struct reference_tag { image_id_t One; image_id_t Two; image_id_t Three; } reference_t; typedef enum rest_direction_tag { Rest_FEET = 1, Rest_HEAD = 2, Rest_UNDEFINED = Enum_UNDEFINED } rest_direction_t; typedef enum rotation_direction_tag { Rotation_NO = 1, Rotation_CC = 2, Rotation_CW = 3, Rotation_UNDEFINED = Enum_UNDEFINED } rotation_direction_t; typedef struct sar_sed_tag { double Lim; double Cal; double Det; } sar_sed_t; typedef enum save_code_tag { Save_DONE = 1, Save_MARKED = 2, Save_NOT = 3, Save_UNDEFINED = Enum_UNDEFINED } save_code_t; typedef enum sex_tag { Sex_F = 1, Sex_M = 2, Sex_O = 3, Sex_UNDEFINED = Enum_UNDEFINED } sex_t; typedef enum storage_mode_tag { Storage_COMPRESS = 1, Storage_EXPANDED = 2, Storage_MIP_MPR = 5, Storage_REDUCED = 3, Storage_XDR = 4, Storage_UNDEFINED = Enum_UNDEFINED } storage_mode_t; typedef enum study_type_tag { Study_Type_CRE = 1, Study_Type_MEA = 2, Study_Type_MIP = 3, Study_Type_MPR = 4, Study_Type_RAW = 5, Study_Type_UNDEFINED = Enum_UNDEFINED } study_type_t; typedef struct target_point_tag { double X; double Y; } target_point_t; typedef enum view_direction_tag { View_FEET = 1, View_HEAD = 2, View_AtoP = 3, View_LtoR = 4, View_PtoA = 5, View_RtoL = 6, View_UNDEFINED = Enum_UNDEFINED } view_direction_t; typedef struct windows_tag { long X; long Y; } windows_t; typedef enum window_style_tag { Style_DOUBLE = 1, Style_HIGH = 2, Style_NONE = 3, Style_STD_1 = 4, Style_STD_2 = 5, Style_UNDEFINED = Enum_UNDEFINED } window_style_t; #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_messages.h0000644000175000000620000004652612574624760027527 0ustar stevestaff/*--------------------------------------------------- * * include ds_messages.h * *--------------------------------------------------- */ #define DS_RET_MORE_PARAMETER_AVAILABLE 6815752 /* (0x00680008) */ #define DS_RET_MORE_PARAMETER_AVAILABLE_E 6815760 /* (0x00680010) */ #define DS_RET_MORE_PARAMETER_AVAILABLE_U 6815768 /* (0x00680018) */ #define DS_RET_MORE_PARAMETER_AVAILABLE_D 6815776 /* (0x00680020) */ #define DS_RET_MORE_VALUES_AVAILABLE 6815784 /* (0x00680028) */ #define DS_RET_MORE_VALUES_AVAILABLE_E 6815792 /* (0x00680030) */ #define DS_RET_MORE_VALUES_AVAILABLE_U 6815800 /* (0x00680038) */ #define DS_RET_MORE_VALUES_AVAILABLE_D 6815808 /* (0x00680040) */ #define DS_RET_UNDEFINED 6815816 /* (0x00680048) */ #define DS_RET_UNDEFINED_E 6815824 /* (0x00680050) */ #define DS_RET_UNDEFINED_U 6815832 /* (0x00680058) */ #define DS_RET_UNDEFINED_D 6815840 /* (0x00680060) */ #define DS_RET_WITH_WARNING 6815848 /* (0x00680068) */ #define DS_RET_WITH_WARNING_E 6815856 /* (0x00680070) */ #define DS_RET_WITH_WARNING_U 6815864 /* (0x00680078) */ #define DS_RET_WITH_WARNING_D 6815872 /* (0x00680080) */ #define DS_RET_NORMAL 6817345 /* (0x00680641) */ #define DS_RET_NORMAL_E 6817353 /* (0x00680649) */ #define DS_RET_NORMAL_U 6817361 /* (0x00680651) */ #define DS_RET_NORMAL_D 6817369 /* (0x00680659) */ #define DS_RET_BAD_DATES 6818946 /* (0x00680c82) */ #define DS_RET_BAD_DATES_E 6818954 /* (0x00680c8a) */ #define DS_RET_BAD_DATES_U 6818962 /* (0x00680c92) */ #define DS_RET_BAD_DATES_D 6818970 /* (0x00680c9a) */ #define DS_RET_BAD_NUMBER 6818978 /* (0x00680ca2) */ #define DS_RET_BAD_NUMBER_E 6818986 /* (0x00680caa) */ #define DS_RET_BAD_NUMBER_U 6818994 /* (0x00680cb2) */ #define DS_RET_BAD_NUMBER_D 6819002 /* (0x00680cba) */ #define DS_RET_BUILD_UP_FAILED 6819010 /* (0x00680cc2) */ #define DS_RET_BUILD_UP_FAILED_E 6819018 /* (0x00680cca) */ #define DS_RET_BUILD_UP_FAILED_U 6819026 /* (0x00680cd2) */ #define DS_RET_BUILD_UP_FAILED_D 6819034 /* (0x00680cda) */ #define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE 6819042 /* (0x00680ce2) */ #define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_E 6819050 /* (0x00680cea) */ #define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_U 6819058 /* (0x00680cf2) */ #define DS_RET_CMS_TO_NEMA_NOT_POSSIBLE_D 6819066 /* (0x00680cfa) */ #define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE 6819074 /* (0x00680d02) */ #define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_E 6819082 /* (0x00680d0a) */ #define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_U 6819090 /* (0x00680d12) */ #define DS_RET_CMS_TO_SOM0_NOT_POSSIBLE_D 6819098 /* (0x00680d1a) */ #define DS_RET_DAY_OUT_OF_RANGE 6819106 /* (0x00680d22) */ #define DS_RET_DAY_OUT_OF_RANGE_E 6819114 /* (0x00680d2a) */ #define DS_RET_DAY_OUT_OF_RANGE_U 6819122 /* (0x00680d32) */ #define DS_RET_DAY_OUT_OF_RANGE_D 6819130 /* (0x00680d3a) */ #define DS_RET_FRACTION_OUT_OF_RANGE 6819138 /* (0x00680d42) */ #define DS_RET_FRACTION_OUT_OF_RANGE_E 6819146 /* (0x00680d4a) */ #define DS_RET_FRACTION_OUT_OF_RANGE_U 6819154 /* (0x00680d52) */ #define DS_RET_FRACTION_OUT_OF_RANGE_D 6819162 /* (0x00680d5a) */ #define DS_RET_HOUR_OUT_OF_RANGE 6819170 /* (0x00680d62) */ #define DS_RET_HOUR_OUT_OF_RANGE_E 6819178 /* (0x00680d6a) */ #define DS_RET_HOUR_OUT_OF_RANGE_U 6819186 /* (0x00680d72) */ #define DS_RET_HOUR_OUT_OF_RANGE_D 6819194 /* (0x00680d7a) */ #define DS_RET_INVALID_IMAGE_AXIS 6819202 /* (0x00680d82) */ #define DS_RET_INVALID_IMAGE_AXIS_E 6819210 /* (0x00680d8a) */ #define DS_RET_INVALID_IMAGE_AXIS_U 6819218 /* (0x00680d92) */ #define DS_RET_INVALID_IMAGE_AXIS_D 6819226 /* (0x00680d9a) */ #define DS_RET_INVALID_IMAGE_GRAPHICS 6819234 /* (0x00680da2) */ #define DS_RET_INVALID_IMAGE_GRAPHICS_E 6819242 /* (0x00680daa) */ #define DS_RET_INVALID_IMAGE_GRAPHICS_U 6819250 /* (0x00680db2) */ #define DS_RET_INVALID_IMAGE_GRAPHICS_D 6819258 /* (0x00680dba) */ #define DS_RET_INVALID_IMAGE_PLACE 6819266 /* (0x00680dc2) */ #define DS_RET_INVALID_IMAGE_PLACE_E 6819274 /* (0x00680dca) */ #define DS_RET_INVALID_IMAGE_PLACE_U 6819282 /* (0x00680dd2) */ #define DS_RET_INVALID_IMAGE_PLACE_D 6819290 /* (0x00680dda) */ #define DS_RET_INVALID_IMAGE_TEXT 6819298 /* (0x00680de2) */ #define DS_RET_INVALID_IMAGE_TEXT_E 6819306 /* (0x00680dea) */ #define DS_RET_INVALID_IMAGE_TEXT_U 6819314 /* (0x00680df2) */ #define DS_RET_INVALID_IMAGE_TEXT_D 6819322 /* (0x00680dfa) */ #define DS_RET_INVALID_INTERFACE 6819330 /* (0x00680e02) */ #define DS_RET_INVALID_INTERFACE_E 6819338 /* (0x00680e0a) */ #define DS_RET_INVALID_INTERFACE_U 6819346 /* (0x00680e12) */ #define DS_RET_INVALID__INTERFACE_D 6819354 /* (0x00680e1a) */ #define DS_RET_INVALID_MATRIX 6819362 /* (0x00680e22) */ #define DS_RET_INVALID_MATRIX_E 6819370 /* (0x00680e2a) */ #define DS_RET_INVALID_MATRIX_U 6819378 /* (0x00680e32) */ #define DS_RET_INVALID_MATRIX_D 6819386 /* (0x00680e3a) */ #define DS_RET_INVALID_VIEW_DIRECTION 6819394 /* (0x00680e42) */ #define DS_RET_INVALID_VIEW_DIRECTION_E 6819402 /* (0x00680e4a) */ #define DS_RET_INVALID_VIEW_DIRECTION_U 6819410 /* (0x00680e52) */ #define DS_RET_INVALID_VIEW_DIRECTION_D 6819418 /* (0x00680e5a) */ #define DS_RET_ITEM_NOT_FOUND 6819426 /* (0x00680e62) */ #define DS_RET_ITEM_NOT_FOUND_E 6819434 /* (0x00680e6a) */ #define DS_RET_ITEM_NOT_FOUND_U 6819442 /* (0x00680e72) */ #define DS_RET_ITEM_NOT_FOUND_D 6819450 /* (0x00680e7a) */ #define DS_RET_LENGTH_NOT_EVEN 6819458 /* (0x00680e82) */ #define DS_RET_LENGTH_NOT_EVEN_E 6819466 /* (0x00680e8a) */ #define DS_RET_LENGTH_NOT_EVEN_U 6819474 /* (0x00680e92) */ #define DS_RET_LENGTH_NOT_EVEN_D 6819482 /* (0x00680e9a) */ #define DS_RET_LENGTH_OUT_OF_RANGE 6819490 /* (0x00680ea2) */ #define DS_RET_LENGTH_OUT_OF_RANGE_E 6819498 /* (0x00680eaa) */ #define DS_RET_LENGTH_OUT_OF_RANGE_U 6819506 /* (0x00680eb2) */ #define DS_RET_LENGTH_OUT_OF_RANGE_D 6819514 /* (0x00680eba) */ #define DS_RET_LENGTH_TO_LESS 6819522 /* (0x00680ec2) */ #define DS_RET_LENGTH_TO_LESS_E 6819530 /* (0x00680eca) */ #define DS_RET_LENGTH_TO_LESS_U 6819538 /* (0x00680ed2) */ #define DS_RET_LENGTH_TO_LESS_D 6819546 /* (0x00680eda) */ #define DS_RET_MINUTE_OUT_OF_RANGE 6819554 /* (0x00680ee2) */ #define DS_RET_MINUTE_OUT_OF_RANGE_E 6819562 /* (0x00680eea) */ #define DS_RET_MINUTE_OUT_OF_RANGE_U 6819570 /* (0x00680ef2) */ #define DS_RET_MINUTE_OUT_OF_RANGE_D 6819578 /* (0x00680efa) */ #define DS_RET_MISSING_INFORMATION 6819586 /* (0x00680f02) */ #define DS_RET_MISSING_INFORMATION_E 6819594 /* (0x00680f0a) */ #define DS_RET_MISSING_INFORMATION_U 6819602 /* (0x00680f12) */ #define DS_RET_MISSING_INFORMATION_D 6819610 /* (0x00680f1a) */ #define DS_RET_MONTH_OUT_OF_RANGE 6819618 /* (0x00680f22) */ #define DS_RET_MONTH_OUT_OF_RANGE_E 6819626 /* (0x00680f2a) */ #define DS_RET_MONTH_OUT_OF_RANGE_U 6819634 /* (0x00680f32) */ #define DS_RET_MONTH_OUT_OF_RANGE_D 6819642 /* (0x00680f3a) */ #define DS_RET_NIL_VECTOR 6819650 /* (0x00680f42) */ #define DS_RET_NIL_VECTOR_E 6819658 /* (0x00680f4a) */ #define DS_RET_NIL_VECTOR_U 6819666 /* (0x00680f52) */ #define DS_RET_NIL_VECTOR_D 6819674 /* (0x00680f5a) */ #define DS_RET_NO_VALID_DATE 6819682 /* (0x00680f62) */ #define DS_RET_NO_VALID_DATE_E 6819690 /* (0x00680f6a) */ #define DS_RET_NO_VALID_DATE_U 6819698 /* (0x00680f72) */ #define DS_RET_NO_VALID_DATE_D 6819706 /* (0x00680f7a) */ #define DS_RET_NO_VALID_ITEM_VALUE 6819714 /* (0x00680f82) */ #define DS_RET_NO_VALID_ITEM_VALUE_E 6819722 /* (0x00680f8a) */ #define DS_RET_NO_VALID_ITEM_VALUE_U 6819730 /* (0x00680f92) */ #define DS_RET_NO_VALID_ITEM_VALUE_D 6819738 /* (0x00680f9a) */ #define DS_RET_NO_VALID_TIME 6819746 /* (0x00680fa2) */ #define DS_RET_NO_VALID_TIME_E 6819754 /* (0x00680faa) */ #define DS_RET_NO_VALID_TIME_U 6819762 /* (0x00680fb2) */ #define DS_RET_NO_VALID_TIME_D 6819770 /* (0x00680fba) */ #define DS_RET_NUMBER_NOT_EVEN 6819778 /* (0x00680fc2) */ #define DS_RET_NUMBER_NOT_EVEN_E 6819786 /* (0x00680fca) */ #define DS_RET_NUMBER_NOT_EVEN_U 6819794 /* (0x00680fd2) */ #define DS_RET_NUMBER_NOT_EVEN_D 6819802 /* (0x00680fda) */ #define DS_RET_NUMBER_OUT_OF_RANGE 6819810 /* (0x00680fe2) */ #define DS_RET_NUMBER_OUT_OF_RANGE_E 6819818 /* (0x00680fea) */ #define DS_RET_NUMBER_OUT_OF_RANGE_U 6819826 /* (0x00680ff2) */ #define DS_RET_NUMBER_OUT_OF_RANGE_D 6819834 /* (0x00680ffa) */ #define DS_RET_OUT_OF_FORMAT 6819842 /* (0x00681002) */ #define DS_RET_OUT_OF_FORMAT_E 6819850 /* (0x0068100a) */ #define DS_RET_OUT_OF_FORMAT_U 6819858 /* (0x00681012) */ #define DS_RET_OUT_OF_FORMAT_D 6819866 /* (0x0068101a) */ #define DS_RET_SECOND_OUT_OF_RANGE 6819874 /* (0x00681022) */ #define DS_RET_SECOND_OUT_OF_RANGE_E 6819882 /* (0x0068102a) */ #define DS_RET_SECOND_OUT_OF_RANGE_U 6819890 /* (0x00681032) */ #define DS_RET_SECOND_OUT_OF_RANGE_D 6819898 /* (0x0068103a) */ #define DS_RET_SEPARATE_FAILED 6819906 /* (0x00681042) */ #define DS_RET_SEPARATE_FAILED_E 6819914 /* (0x0068104a) */ #define DS_RET_SEPARATE_FAILED_U 6819922 /* (0x00681052) */ #define DS_RET_SEPARATE_FAILED_D 6819930 /* (0x0068105a) */ #define DS_RET_STRING_TO_LONG 6819938 /* (0x00681062) */ #define DS_RET_STRING_TO_LONG_E 6819946 /* (0x0068106a) */ #define DS_RET_STRING_TO_LONG_U 6819954 /* (0x00681072) */ #define DS_RET_STRING_TO_LONG_D 6819962 /* (0x0068107a) */ #define DS_RET_SYSTEM_ERROR 6819970 /* (0x00681082) */ #define DS_RET_SYSTEM_ERROR_E 6819978 /* (0x0068108a) */ #define DS_RET_SYSTEM_ERROR_U 6819986 /* (0x00681092) */ #define DS_RET_SYSTEM_ERROR_D 6819994 /* (0x0068109a) */ #define DS_RET_UNKNOWN_OVERLAY 6820002 /* (0x006810a2) */ #define DS_RET_UNKNOWN_OVERLAY_E 6820010 /* (0x006810aa) */ #define DS_RET_UNKNOWN_OVERLAY_U 6820018 /* (0x006810b2) */ #define DS_RET_UNKNOWN_OVERLAY_D 6820026 /* (0x006810ba) */ #define DS_RET_UNKNOWN_UPDATE_CODE 6820034 /* (0x006810c2) */ #define DS_RET_UNKNOWN_UPDATE_CODE_E 6820042 /* (0x006810ca) */ #define DS_RET_UNKNOWN_UPDATE_CODE_U 6820050 /* (0x006810d2) */ #define DS_RET_UNKNOWN_UPDATE_CODE_D 6820058 /* (0x006810da) */ #define DS_RET_UNKNOWN_HEADER 6820066 /* (0x006810e2) */ #define DS_RET_UNKNOWN_HEADER_E 6820074 /* (0x006810ea) */ #define DS_RET_UNKNOWN_HEADER_U 6820082 /* (0x006810f2) */ #define DS_RET_UNKNOWN_HEADER_D 6820090 /* (0x006810fa) */ #define DS_RET_UNKNOWN_UPDATE_MODE 6820098 /* (0x00681102) */ #define DS_RET_UNKNOWN_UPDATE_MODE_E 6820106 /* (0x0068110a) */ #define DS_RET_UNKNOWN_UPDATE_MODE_U 6820114 /* (0x00681112) */ #define DS_RET_UNKNOWN_UPDATE_MODE_D 6820122 /* (0x0068111a) */ #define DS_RET_UNKNOWN_VALUE_MODE 6820130 /* (0x00681122) */ #define DS_RET_UNKNOWN_VALUE_MODE_E 6820138 /* (0x0068112a) */ #define DS_RET_UNKNOWN_VALUE_MODE_U 6820146 /* (0x00681132) */ #define DS_RET_UNKNOWN_VALUE_MODE_D 6820154 /* (0x0068113a) */ #define DS_RET_VECTOR_NOT_DEFINED 6820162 /* (0x00681142) */ #define DS_RET_VECTOR_NOT_DEFINED_E 6820170 /* (0x0068114a) */ #define DS_RET_VECTOR_NOT_DEFINED_U 6820178 /* (0x00681152) */ #define DS_RET_VECTOR_NOT_DEFINED_D 6820186 /* (0x0068115a) */ #define DS_RET_VECTOR_NOT_NORMALIZED 6820194 /* (0x00681162) */ #define DS_RET_VECTOR_NOT_NORMALIZED_E 6820202 /* (0x0068116a) */ #define DS_RET_VECTOR_NOT_NORMALIZED_U 6820210 /* (0x00681172) */ #define DS_RET_VECTOR_NOT_NORMALIZED_D 6820218 /* (0x0068117a) */ #define DS_RET_YEAR_OUT_OF_RANGE 6820226 /* (0x00681182) */ #define DS_RET_YEAR_OUT_OF_RANGE_E 6820234 /* (0x0068118a) */ #define DS_RET_YEAR_OUT_OF_RANGE_U 6820242 /* (0x00681192) */ #define DS_RET_YEAR_OUT_OF_RANGE_D 6820250 /* (0x0068119a) */ #define DS_RET_FUNCTION_NOT_IMPLEMENTED 6820547 /* (0x006812c3) */ #define DS_RET_FUNCTION_NOT_IMPLEMENTED_E 6820555 /* (0x006812cb) */ #define DS_RET_FUNCTION_NOT_IMPLEMENTED_U 6820563 /* (0x006812d3) */ #define DS_RET_FUNCTION_NOT_IMPLEMENTED_D 6820571 /* (0x006812db) */ #define DS_RET_ITEM_FOUND 6820579 /* (0x006812e3) */ #define DS_RET_ITEM_FOUND_E 6820587 /* (0x006812eb) */ #define DS_RET_ITEM_FOUND_U 6820595 /* (0x006812f3) */ #define DS_RET_ITEM_FOUND_D 6820603 /* (0x006812fb) */ #define DS_RET_NO_GRAPHICS_AVAILABLE 6820611 /* (0x00681303) */ #define DS_RET_NO_GRAPHICS_AVAILABLE_E 6820619 /* (0x0068130b) */ #define DS_RET_NO_GRAPHICS_AVAILABLE_U 6820627 /* (0x00681313) */ #define DS_RET_NO_GRAPHICS_AVAILABLE_D 6820635 /* (0x0068131b) */ #define DS_RET_STRING_NOT_FOUND 6820643 /* (0x00681323) */ #define DS_RET_STRING_NOT_FOUND_E 6820651 /* (0x0068132b) */ #define DS_RET_STRING_NOT_FOUND_U 6820659 /* (0x00681333) */ #define DS_RET_STRING_NOT_FOUND_D 6820667 /* (0x0068133b) */ #define DS_RET_STRINGS_ARE_EQUAL 6820675 /* (0x00681343) */ #define DS_RET_STRINGS_ARE_EQUAL_E 6820683 /* (0x0068134b) */ #define DS_RET_STRINGS_ARE_EQUAL_U 6820691 /* (0x00681353) */ #define DS_RET_STRINGS_ARE_EQUAL_D 6820699 /* (0x0068135b) */ #define DS_RET_DATA_SET_OUT_OF_ORDER 6822148 /* (0x00681904) */ #define DS_RET_DATA_SET_OUT_OF_ORDER_E 6822156 /* (0x0068190c) */ #define DS_RET_DATA_SET_OUT_OF_ORDER_U 6822164 /* (0x00681914) */ #define DS_RET_DATA_SET_OUT_OF_ORDER_D 6822172 /* (0x0068191c) */ #define DS_RET_END_OF_DATA_SET_FOUND 6822180 /* (0x00681924) */ #define DS_RET_END_OF_DATA_SET_FOUND_E 6822188 /* (0x0068192c) */ #define DS_RET_END_OF_DATA_SET_FOUND_U 6822196 /* (0x00681934) */ #define DS_RET_END_OF_DATA_SET_FOUND_D 6822204 /* (0x0068193c) */ #define DS_RET_FIRST_ELEMENT_INVALID 6822212 /* (0x00681944) */ #define DS_RET_FIRST_ELEMENT_INVALID_E 6822220 /* (0x0068194c) */ #define DS_RET_FIRST_ELEMENT_INVALID_U 6822228 /* (0x00681954) */ #define DS_RET_FIRST_ELEMENT_INVALID_D 6822236 /* (0x0068195c) */ #define DS_RET_UNKNOWN_COMPRESSION_CODE 6822244 /* (0x00681964) */ #define DS_RET_UNKNOWN_COMPRESSION_CODE_E 6822252 /* (0x0068196c) */ #define DS_RET_UNKNOWN_COMPRESSION_CODE_U 6822260 /* (0x00681974) */ #define DS_RET_UNKNOWN_COMPRESSION_CODE_D 6822268 /* (0x0068197c) */ #define DS_RET_UNKNOWN_DATA_SET 6822276 /* (0x00681984) */ #define DS_RET_UNKNOWN_DATA_SET_E 6822284 /* (0x0068198c) */ #define DS_RET_UNKNOWN_DATA_SET_U 6822292 /* (0x00681994) */ #define DS_RET_UNKNOWN_DATA_SET_D 6822300 /* (0x0068199c) */ #define DS_RET_UNKNOWN_DATA_SET_OWNER 6822308 /* (0x006819a4) */ #define DS_RET_UNKNOWN_DATA_SET_OWNER_E 6822316 /* (0x006819ac) */ #define DS_RET_UNKNOWN_DATA_SET_OWNER_U 6822324 /* (0x006819b4) */ #define DS_RET_UNKNOWN_DATA_SET_OWNER_D 6822332 /* (0x006819bc) */ #define DS_RET_UNKNOWN_MODALITY 6822340 /* (0x006819c4) */ #define DS_RET_UNKNOWN_MODALITY_E 6822348 /* (0x006819cc) */ #define DS_RET_UNKNOWN_MODALITY_U 6822356 /* (0x006819d4) */ #define DS_RET_UNKNOWN_MODALITY_D 6822364 /* (0x006819dc) */ #define DS_RET_UNKNOWN_SWAP_MODE 6822372 /* (0x006819e4) */ #define DS_RET_UNKNOWN_SWAP_MODE_E 6822380 /* (0x006819ec) */ #define DS_RET_UNKNOWN_SWAP_MODE_U 6822388 /* (0x006819f4) */ #define DS_RET_UNKNOWN_SWAP_MODE_D 6822396 /* (0x006819fc) */ #define DS_RET_WITH_ERROR 6822404 /* (0x00681a04) */ #define DS_RET_WITH_ERROR_E 6822412 /* (0x00681a0c) */ #define DS_RET_WITH_ERROR_U 6822420 /* (0x00681a14) */ #define DS_RET_WITH_ERROR_D 6822428 /* (0x00681a1c) */ #define DS_CODE_SET_OUT_OF_ORDER 6825347 /* (0x00682583) */ #define DS_CODE_SET_OUT_OF_ORDER_E 6825355 /* (0x0068258b) */ #define DS_CODE_SET_OUT_OF_ORDER_U 6825363 /* (0x00682593) */ #define DS_CODE_SET_OUT_OF_ORDER_D 6825371 /* (0x0068259b) */ #define DS_CODE_END_OF_SET_FOUND 6825379 /* (0x006825a3) */ #define DS_CODE_END_OF_SET_FOUND_E 6825387 /* (0x006825ab) */ #define DS_CODE_END_OF_SET_FOUND_U 6825395 /* (0x006825b3) */ #define DS_CODE_END_OF_SET_FOUND_D 6825403 /* (0x006825bb) */ #define DS_CODE_FATAL_ERROR 6825411 /* (0x006825c3) */ #define DS_CODE_FATAL_ERROR_E 6825419 /* (0x006825cb) */ #define DS_CODE_FATAL_ERROR_U 6825427 /* (0x006825d3) */ #define DS_CODE_FATAL_ERROR_D 6825435 /* (0x006825db) */ #define DS_CODE_FIRST_ELEMENT_INVALID 6825443 /* (0x006825e3) */ #define DS_CODE_FIRST_ELEMENT_INVALID_E 6825451 /* (0x006825eb) */ #define DS_CODE_FIRST_ELEMENT_INVALID_U 6825459 /* (0x006825f3) */ #define DS_CODE_FIRST_ELEMENT_INVALID_D 6825467 /* (0x006825fb) */ #define DS_CODE_INVALID_MODALITY 6825475 /* (0x00682603) */ #define DS_CODE_INVALID_MODALITY_E 6825483 /* (0x0068260b) */ #define DS_CODE_INVALID_MODALITY_U 6825491 /* (0x00682613) */ #define DS_CODE_INVALID_MODALITY_D 6825499 /* (0x0068261b) */ #define DS_CODE_INVALID_SOM0_PARAMETERS 6825507 /* (0x00682623) */ #define DS_CODE_INVALID_SOM0_PARAMETERS_E 6825515 /* (0x0068262b) */ #define DS_CODE_INVALID_SOM0_PARAMETERS_U 6825523 /* (0x00682633) */ #define DS_CODE_INVALID_PARAMETERS_D 6825531 /* (0x0068263b) */ #define DS_CODE_INVALID_INTERFACE 6825539 /* (0x00682643) */ #define DS_INVALID_INTERFACE_E 6825547 /* (0x0068264b) */ #define DS_INVALID_INTERFACE_U 6825555 /* (0x00682653) */ #define DS_INVALID_INTERFACE_D 6825563 /* (0x0068265b) */ #define DS_CODE_MULTIPLE_FATAL_ERROR 6825571 /* (0x00682663) */ #define DS_CODE_FMULTIPLE_ATAL_ERROR_E 6825579 /* (0x0068266b) */ #define DS_CODE_MULTIPLE_FATAL_ERROR_U 6825587 /* (0x00682673) */ #define DS_CODE_MULTIPLE_FATAL_ERROR_D 6825595 /* (0x0068267b) */ #define DS_CODE_NO_OWNER 6825603 /* (0x00682683) */ #define DS_CODE_NO_OWNER_E 6825611 /* (0x0068268b) */ #define DS_CODE_NO_OWNER_U 6825619 /* (0x00682693) */ #define DS_CODE_NO_OWNER_D 6825627 /* (0x0068269b) */ #define DS_CODE_TRANSFORM_NOT_POSSIBLE 6825635 /* (0x006826a3) */ #define DS_CODE_TRANSFORM_NOT_POSSIBLE_E 6825643 /* (0x006826ab) */ #define DS_CODE_TRANSFORM_NOT_POSSIBLE_U 6825651 /* (0x006826b3) */ #define DS_CODE_TRANSFORM_NOT_POSSIBLE_D 6825659 /* (0x006826bb) */ #define DS_CODE_UNKNOWN_COMPRESSION 6825667 /* (0x006826c3) */ #define DS_CODE_UNKNOWN_COMPRESSION_E 6825675 /* (0x006826cb) */ #define DS_CODE_UNKNOWN_COMPRESSION_U 6825683 /* (0x006826d3) */ #define DS_CODE_UNKNOWN_COMPRESSION_D 6825691 /* (0x006826db) */ #define DS_CODE_UNKNOWN_DATA_SET 6825699 /* (0x006826e3) */ #define DS_CODE_UNKNOWN_DATA_SET_E 6825707 /* (0x006826eb) */ #define DS_CODE_UNKNOWN_DATA_SET_U 6825715 /* (0x006826f3) */ #define DS_CODE_UNKNOWN_DATA_SET_D 6825723 /* (0x006826fb) */ #define DS_CODE_UNKNOWN_HEADER 6825731 /* (0x00682703) */ #define DS_CODE_UNKNOWN_HEADER_E 6825739 /* (0x0068270b) */ #define DS_CODE_UNKNOWN_HEADER_U 6825747 /* (0x00682713) */ #define DS_CODE_UNKNOWN_HEADER_D 6825755 /* (0x0068271b) */ #define DS_CODE_UNKNOWN_OVERLAY 6825763 /* (0x00682723) */ #define DS_CODE_UNKNOWN_OVERLAY_E 6825771 /* (0x0068272b) */ #define DS_CODE_UNKNOWN_OVERLAY_U 6825779 /* (0x00682733) */ #define DS_CODE_UNKNOWN_OVERLAY_D 6825787 /* (0x0068273b) */ #define DS_CODE_MISSING_TYP_1 6825795 /* (0x00682743) */ #define DS_CODE_MISSING_TYP_1_E 6825803 /* (0x0068274b) */ #define DS_CODE_MISSING_TYP_1_U 6825811 /* (0x00682753) */ #define DS_CODE_MISSING_TYP_1_D 6825819 /* (0x0068275b) */ #define DS_CODE_MULTIPLE_ERROR 6825827 /* (0x00682763) */ #define DS_CODE_MULTIPLE_ERROR_E 6825835 /* (0x0068276b) */ #define DS_CODE_MULTIPLE_ERROR_U 6825843 /* (0x00682773) */ #define DS_CODE_MULTIPLE_ERROR_D 6825851 /* (0x0068277b) */ #define DS_CODE_NOT_ALL_FILLED 6825859 /* (0x00682783) */ #define DS_CODE_NOT_ALL_FILLED_E 6825867 /* (0x0068278b) */ #define DS_CODE_NOT_ALL_FILLED_U 6825875 /* (0x00682793) */ #define DS_CODE_NOT_ALL_FILLED_D 6825883 /* (0x0068279b) */ #define DS_CODE_NORMAL 6825891 /* (0x006827a3) */ #define DS_CODE_NORMAL_E 6825899 /* (0x006827ab) */ #define DS_CODE_NORMAL_U 6825907 /* (0x006827b3) */ #define DS_CODE_NORMAL_D 6825915 /* (0x006827bb) */ #define DS_CODE_UNDEFINED 6825923 /* (0x006827c3) */ #define DS_CODE_UNDEFINED_E 6825931 /* (0x006827cb) */ #define DS_CODE_UNDEFINED_U 6825939 /* (0x006827d3) */ #define DS_CODE_UNDEFINED_D 6825947 /* (0x006827db) */ minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_constants.h0000644000175000000620000001133112574624760030677 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_constants.h Description: The header file defines the constants used by the internal header definiton. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_CONSTANTS #define DS_HEAD_CONSTANTS #define DS_DUMMY_LENGTH 1L /* PRECOMPILER: define length of group overlaying buffers */ #define LENGTH_GROUP_0008 384 #define LENGTH_GROUP_0009 384 #define LENGTH_GROUP_0010 256 #define LENGTH_GROUP_0011 128 #define LENGTH_GROUP_0013 384 #define LENGTH_GROUP_0018 384 #define LENGTH_GROUP_0019_PART1 128 #define LENGTH_GROUP_0019_PART2 512 #define LENGTH_GROUP_0019_PART3 384 #define LENGTH_GROUP_0019_PART4 256 #define LENGTH_GROUP_0020 512 #define LENGTH_GROUP_0021_PART1 256 #define LENGTH_GROUP_0021_PART2 256 #define LENGTH_GROUP_0021_PART3 768 #define LENGTH_GROUP_0028 256 #define LENGTH_GROUP_0029 256 #define LENGTH_GROUP_0051 640 #define LENGTH_TO_FILL_K_BORDER DS_DUMMY_LENGTH /* PRECOMPILER: define length of common header strings */ #define LENGTH_LABEL 26L /* PRECOMPILER: define length of special header stings */ #define LENGTH_AGE 4L #define LENGTH_COMMENT 26L #define LENGTH_DIAGNOSIS 40L #define LENGTH_DIRECTION 4L #define LENGTH_FILE_NAME 64L #define LENGTH_FILTER_ID 12L #define LENGTH_HEADER_VERSION 8L #define LENGTH_NUCLEUS 8L #define LENGTH_MANUFACTURER 8L #define LENGTH_ORIENTATION 3L #define LENGTH_PATIENT_ID 12L #define LENGTH_SEQUENCE_INFO 8L #define LENGTH_SOFTWARE_VERSION 8L /* PRECOMPILER: define length of origianl headers */ #define LENGTH_NUM1_HEADER 4096L #define LENGTH_NUM2_HEADER 8192L #define LENGTH_SOM0_HEADER 2048L #define LENGTH_SOM0_IMAGE_TEXT 960L #define LENGTH_SOM1_HEADER 4096L #define LENGTH_SOM_HEADER LENGTH_SOM1_HEADER /* PRECOMPILER: define Xxx_UNDEFINED constants */ #define Enum_UNDEFINED (-19222) #define Integer_UNDEFINED (-19222) #define String_UNDEFINED '?' #define Real_UNDEFINED (-19222.0) #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_shadow_groups_types.h0000644000175000000620000011626612574624760033010 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_shadow_groups_types.h Description: The header file defines the SPI and CMS defined data set basic groups (odd group numbers) as structures for internal use (internal header). Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_SHADOW_GROUPS_TYPES #define DS_HEAD_SHADOW_GROUPS_TYPES /******************************************/ /* Identifying Information (Group 0009'H) */ /******************************************/ typedef struct shadow_identifying_tag { data_object_subtype_t DataObjectSubtype; /* (0009,1041) 8 AT EV 2NS-SPI */ long NumberOfMeasurements; /* (0009,1200) 6 AN FF 3NS-CMS */ storage_mode_t StorageMode; /* (0009,1210) 8 AT EV 2DS-CMS */ long EvaluationMask; /* (0009,1212) 4 BD FF 2DS-CMS */ long Gap1212; ds_date_t LastMoveDate; /* (0009,1226) 10 AT DF 3NS-CMS */ ds_time_t LastMoveTime; /* (0009,1227) 12 AT DF 3NS-CMS */ char GeneratorIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1310) 26 AT DF 2DS-CMS */ char GantryIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1311) 26 AT DF 2DS-CMS */ char XRayTubeIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1312) 26 AT DF 2DS-CMS */ char DetectorIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1313) 26 AT DF 2DS-CMS */ char DASIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1314) 26 AT DF 2DS-CMS */ char SMIIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1315) 26 AT DF 2DS-CMS */ char CPUIdentificationLabel[LENGTH_LABEL + 1]; /* (0009,1316) 26 AT DF 2DS-CMS */ char HeaderVersion[LENGTH_HEADER_VERSION + 1]; /* (0009,1320) 8 AT DF 2DS-CMS */ } shadow_identifying_t; /**************************************/ /* Patient Information (Group 0011'H) */ /**************************************/ typedef struct shadow_patient_tag { char Organ[LENGTH_LABEL + 1]; /* (0011,1010) 26 AT FF 3NS-SPI */ ds_date_t RegistrationDate; /* (0011,1110) 10 AT DF 2NS-CMS */ ds_time_t RegistrationTime; /* (0011,1111) 12 AT DF 2NS-CMS */ long UsedPatientWeight; /* (0011,1123) 4 AN FF 2NS-CMS */ long OrganCode; /* (0011,1012) 6 AN FF 3NS-CMS */ } shadow_patient_t; /*********************************************/ /* Patient Modify Information (Group 0013'H) */ /*********************************************/ typedef struct shadow_patient_modify_tag { char ModifyingPhysician[LENGTH_LABEL + 1]; /* (0013,1000) 26 AT FF 2NS-CMS */ ds_date_t ModificationDate; /* (0013,1010) 10 AT DF 2NS-CMS */ ds_time_t ModificationTime; /* (0013,1012) 12 AT DF 2NS-CMS */ char PatientName[LENGTH_LABEL + 1]; /* (0013,1020) 26 AT FF 3NS-CMS */ char PatientId[LENGTH_PATIENT_ID + 1]; /* (0013,1022) 12 AT FF 3NS-CMS */ ds_date_t PatientBirthdate; /* (0013,1030) 10 AT DF 3NS-CMS */ long PatientWeight; /* (0013,1031) 6 AN FF 3NS-CMS */ char PatientMaidenName[LENGTH_LABEL + 1]; /* (0013,1032) 26 AT FF 3NS-CMS */ char ReferringPhysician[LENGTH_LABEL + 1]; /* (0013,1033) 26 AT FF 3NS-CMS */ char AdmittingDiagnosis[LENGTH_DIAGNOSIS + 1]; /* (0013,1034) 40 AT FF 3DS-CMS */ sex_t PatientSex; /* (0013,1035) 2 AT EV 3NS-CMS */ char ProcedureDescription_1[LENGTH_COMMENT + 1]; /* (0013,1040) 52 AT FF 3NS-CMS */ char ProcedureDescription_2[LENGTH_COMMENT + 1]; rest_direction_t RestDirection; /* (0013,1042) 4 AT EV 3NS-CMS */ patient_position_t PatientPosition; /* (0013,1044) 8 AT EV 3NS-CMS */ view_direction_t ViewDirection; /* (0013,1046) 4 AT EV 2NS-CMS */ } shadow_patient_modify_t; /******************************************/ /* Acquisition Information (Group 0019'H) */ /******************************************/ typedef struct shadow_acquisition_cms_tag /* CMS shadowsubgroup */ { long NetFrequency; /* (0019,1010) 6 AN FF 3NS-CMS */ measurement_mode_t MeasurementMode; /* (0019,1020) 8 AT EV 2DS-CMS */ calculation_mode_t CalculationMode; /* (0019,1030) 8 AT EV 2NS-CMS */ long Gap1040; long NoiseLevel; /* (0019,1050) 6 AN FF 3NS-CMS */ long NumberOfDataBytes; /* (0019,1060) 6 AN FF 2NS-CMS */ } shadow_acquisition_cms_t; typedef struct shadow_acquisition_ct_tag /* CT shadowsubgroup */ { double SourceSideCollimatorAperture; /* (0019,1110) 14 AN FF 3NS-CMS */ double DetectorSideCollimatorAperture; /* (0019,1111) 14 AN FF 3NS-CMS */ long ExposureTime; /* (0019,1120) 6 AN FF 3NS-CMS */ long Exposure; /* (0019,1121) 6 AN FF 3NS-CMS */ double GeneratorPower; /* (0019,1125) 14 AN FF 3NS-CMS */ long GeneratorVoltage; /* (0019,1126) 6 AN FF 3NM-CMS */ long GeneratorVoltageDual; long MasterControlMask; /* (0019,1140) 4 BD HX 3NM-CMS */ long Gap1140; short ProcessingMask[5]; /* (0019,1142) 2 BI HX 3NM-CMS */ short Gap1142[3]; long NumberOfVirtuellChannels; /* (0019,1162) 6 AN FF 3NS-CMS */ long NumberOfReadings; /* (0019,1170) 6 AN FF 3NS-CMS */ long NumberOfProjections; /* (0019,1174) 6 AN FF 3NS-CMS */ long NumberOfBytes; /* (0019,1175) 6 AN FF 3NS-CMS */ long ReconstructionAlgorithmSet[3]; /* (0019,1180) 6 AN FF 3NS-CMS */ long Gap1180[2]; long ReconstructionAlgorithmIndex; /* (0019,1181) 6 AN FF 3NS-CMS */ char RegenerationSoftwareVersion[LENGTH_SOFTWARE_VERSION + 1]; /* (0019,1182) 8 AT FF 3NS-CMS */ } shadow_acquisition_ct_t; typedef struct shadow_acquisition_mr_tag /* MR shadowsubgroup */ { double TotalMeasurementTime; /* (0019,1210) 14 AN FF 3NS-CMS */ double StartDelayTime; /* (0019,1212) 14 AN FF 3NS-CMS */ long NumberOfPhases; /* (0019,1214) 6 AN FF 2DS-CMS */ long SequenceControlMask[2]; /* (0019,1216) 4 BD HX 3NM-CMS */ long Gap1216[2]; long NumberOfFourierLinesNominal; /* (0019,1220) 6 AN FF 3NS-CMS */ long NumberOfFourierLinesCurrent; /* (0019,1221) 6 AN FF 3NS-CMS */ long NumberOfFourierLinesAfterZero; /* (0019,1226) 6 AN FF 3NS-CMS */ long FirstMeasuredFourierLine; /* (0019,1228) 6 AN FF 3NS-CMS */ long AcquisitionColumns; /* (0019,1230) 6 AN FF 3NS-CMS */ long ReconstructionColumns; /* (0019,1231) 6 AN FF 3NS-CMS */ long NumberOfAverages; /* (0019,1250) 6 AN FF 3NS-CMS */ double FlipAngle; /* (0019,1260) 14 AN FF 3NS-CMS */ long NumberOfPrescans; /* (0019,1270) 6 AN FF 3NS-CMS */ filter_type_t FilterTypeRawData; /* (0019,1281) 8 AT EV 2DS-CMS */ filter_parameter_t FilterParameterRawData; /* (0019,1282) 14 AN FF 3NM-CMS */ filter_type_image_t FilterTypeImageData; /* (0019,1283) 8 AT EV 2DS-CMS */ int Pad1; /* Dummy for byte alignament */ filter_parameter_t FilterParameterImageData; /* (0019,1284) 14 AN FF 3NM-CMS */ filter_type_t FilterTypePhaseCorrection; /* (0019,1285) 8 AT EV 2DS-CMS */ int Pad2; /* Dummy for byte alignament */ filter_parameter_t FilterParameterPhaseCorrection; /* (0019,1286) 14 AN FF 3NM-CMS */ long NumberOfSaturationRegions; /* (0019,1290) 6 AN FF 2DS-CMS */ /* short Pad4[5]; /* Dummy for byte alignment */ int Pad3; /* Dummy for byte alignament */ double ImageRotationAngle; /* (0019,1294) 14 AN FF 3NS-CMS */ double DwellTime; /* (0019,1213) 14 AN FF 3NS-CMS */ long CoilIdMask[3]; /* (0019,1296) 4 BD HX 3NM-CMS */ long Gap1296[2]; int Pad4; /* Dummy for byte alignament */ image_location_t CoilPosition; /* (0019,1298) 14 AN FF 2NM-CMS */ double TotalMeasurementTimeCur; /* (0019,1211) 14 AN FF 3NS-CMS*/ long MeasurementStatusMask; /* (0019,1218) 4 BD FF 2DS-CMS */ } shadow_acquisition_mr_t; typedef struct shadow_acquisition_ct_conf_tag /* CT configuration and adjust shadowsubgroup */ { long DistanceSourceToSourceSideCollimator; /* (0019,1310) 6 AN FF 3NS-CMS */ long DistanceSourceToDetectorSideCollimator; /* (0019,1311) 6 AN FF 3NS-CMS */ long NumberOfPossibleChannels; /* (0019,1320) 6 AN FF 3NS-CMS */ long MeanChannelNumber; /* (0019,1321) 6 AN FF 3NS-CMS */ double DetectorSpacing; /* (0019,1322) 14 AN FF 3NS-CMS */ double ReadingIntegrationTime; /* (0019,1324) 14 AN FF 3NS-CMS */ double DetectorAlignment; /* (0019,1350) 14 AN FF 3NS-CMS */ double FocusAlignment; /* (0019,1360) 14 AN FF 3NS-CMS */ long FocalSpotDeflectionAmplitude; /* (0019,1365) 4 BD HX 3NS-CMS */ long FocalSpotDeflectionPhase; /* (0019,1366) 4 BD HX 3NS-CMS */ long FocalSpotDeflectionOffset; /* (0019,1367) 4 BD HX 3NS-CMS */ double WaterScalingFactor; /* (0019,1370) 14 AN FF 3NS-CMS */ double InterpolationFactor; /* (0019,1371) 14 AN FF 3NS-CMS */ patient_region_t PatientRegion; /* (0019,1380) 4 AT EV 3NS-CMS */ patient_phase_t PatientPhaseOfLife; /* (0019,1382) 8 AT EV 3NS-CMS */ double DetectorCenter; /* (0019,1323) 14 AN FF 3NS-CMS */ } shadow_acquisition_ct_conf_t; typedef struct shadow_acquisition_mr_conf_tag /* MR configuration and adjust shadowsubgroup */ { double MagneticFieldStrength; /* (0019,1412) 14 AN FF 3NS-CMS */ double ADCVoltage; /* (0019,1414) 14 AN FF 3NS-CMS */ double TransmitterAmplitude; /* (0019,1420) 14 AN FF 3NS-CMS */ long NumberOfTransmitterAmplitudes; /* (0019,1421) 6 AN FF 3NS-CMS */ int Pad1; /* Dummy for byte alignment */ double TransmitterCalibration; /* (0019,1424) 14 AN FF 3NS-CMS */ double ReceiverTotalGain; /* (0019,1450) 14 AN FF 3NS-CMS */ double ReceiverAmplifierGain; /* (0019,1451) 14 AN FF 3NS-CMS */ double ReceiverPreamplifierGain; /* (0019,1452) 14 AN FF 3NS-CMS */ double ReceiverCableAttenuation; /* (0019,1454) 14 AN FF 3NS-CMS */ double ReconstructionScaleFactor; /* (0019,1460) 14 AN FF 3NS-CMS */ double PhaseGradientAmplitude; /* (0019,1470) 14 AN FF 3NS-CMS */ double ReadoutGradientAmplitude; /* (0019,1471) 14 AN FF 3NS-CMS */ double SelectionGradientAmplitude; /* (0019,1472) 14 AN FF 3NS-CMS */ gradient_delay_time_t GradientDelayTime; /* (0019,1480) 14 AN FF 3NS-CMS */ char SensitivityCorrectionLabel[LENGTH_LABEL + 1]; /* (0019,1490) 26 AT FF 2DS-CMS */ int Pad2; /* Dummy for byte alignment */ double ADCOffset[2]; /* (0019,1416) 14 AN FF 3NM-CMS */ double TransmitterAttenuator; /* (0019,1422) 14 AN FF 3NS-CMS */ double TransmitterReference; /* (0019,1426) 14 AN FF 3NS-CMS */ double ReceiverReferenceGain; /* (0019,1455) 14 AN FF 3NS-CMS */ long ReceiverFilterFrequency; /* (0019,1456) 6 AN FF 3NS-CMS */ int Pad3; /* Dummy for byte alignment */ double ReferenceScaleFactor; /* (0019,1462) 14 AN FF 3NS-CMS */ double TotalGradientDelayTime; /* (0019,1482) 14 AN FF 3NS-CMS */ long RfWatchdogMask; /* (0019,14A0) 4 BD HX 3NM-CMS */ int Pad4; /* Dummy for byte alignment */ double RfPowerErrorIndicator; /* (0019,14A2) 14 AN FF 3NS-CMS */ sar_sed_t SarWholeBody; /* (0019,14A5) 14 AN FF 3NS-CMS */ sar_sed_t Sed; /* (0019,14A6) 14 AN FF 3NS-CMS */ long AdjustmentStatusMask; /* (0019,14B0) 4 BD FF 2DS-CMS */ } shadow_acquisition_mr_conf_t; typedef struct shadow_acquisition_acq_tag /* acquisition shadowsubgroup */ { char ParameterFileName[LENGTH_FILE_NAME + 1]; /* (0019,1510) 64 AT FF 3NS-CMS */ char SequenceFileName[LENGTH_FILE_NAME + 1]; /* (0019,1511) 64 AT FF 3NS-CMS */ char SequenceFileOwner[LENGTH_SEQUENCE_INFO + 1]; /* (0019,1512) 8 AT FF 2DS-CMS */ char SequenceDescription[LENGTH_SEQUENCE_INFO + 1]; /* (0019,1513) 8 AT FF 2DS-CMS */ } shadow_acquisition_acq_t; /*******************************************/ /* Relationship Information (Group 0021'H) */ /*******************************************/ typedef struct shadow_relationship_med_cms_tag /* MED and CMS shadowsubgroups */ { double Gap1010; target_point_t Target; /* (0021,1011) 30 AT FF 2NM-MED */ short RoiMask; /* (0021,1020) 2 BI HX 2NS-MED */ int Pad1; /* Dummy for byte alignment */ field_of_view_t FoV; /* (0021,1120) 30 AN FF 2NM-CMS */ view_direction_t ViewDirection; /* (0021,1130) 4 AT EV 2NS-CMS */ rest_direction_t RestDirection; /* (0021,1132) 4 AT EV 2NS-CMS */ image_location_t ImagePosition; /* (0021,1160) 14 AN FF 2NM-CMS */ image_location_t ImageNormal; /* (0021,1161) 14 AN FF 2NM-CMS */ double ImageDistance; /* (0021,1163) 14 AN FF 2NM-CMS */ short ImagePositioningHistoryMask; /* (0021,1165) 2 BI HX 2DS-CMS */ int Pad2; /* Dummy for byte alignment */ image_location_t ImageRow; /* (0021,116A) 14 AN FF 2NM-CMS */ image_location_t ImageColumn; /* (0021,116B) 14 AN FF 2NM-CMS */ patient_orientation_t PatientOrientationSet1; /* (0021,1170) 4 AT EV 2NM-CMS */ patient_orientation_t PatientOrientationSet2; /* (0021,1171) 4 AT EV 2NM-CMS */ char StudyName[LENGTH_LABEL + 1]; /* (0021,1180) 26 AT FF 3NS-CMS */ study_type_t StudyType; /* (0021,1182) 4 AT EV 3NS-CMS */ double ImageMagnificationFactor; /* (0021,1122) 14 AN FF 2DS-CMS */ } shadow_relationship_med_cms_t; typedef struct shadow_relationship_ct_tag /* CT common shadowsubgroups */ { long RotationAngle; /* (0021,1210) 6 AN FF 3NS-CMS */ long StartAngle; /* (0021,1211) 6 AN FF 3NS-CMS */ long TubePosition; /* (0021,1230) 6 AN FF 3NS-CMS */ long LengthOfTopogram; /* (0021,1232) 6 AN FF 3NS-CMS */ double CorrectionFactor; /* (0021,1234) 14 AN FF 3NS-CMS */ long MaximumTablePosition; /* (0021,1236) 6 AN FF 3NS-CMS */ long TableMoveDirectionCode; /* (0021,1240) 6 AN FF 3NS-CMS */ long VectorStartRow; /* (0021,1250) 6 AN FF 3NS-CMS */ long VectorRowStep; /* (0021,1251) 6 AN FF 3NS-CMS */ long VectorStartColumn; /* (0021,1252) 6 AN FF 3NS-CMS */ long VectorColumnStep; /* (0021,1253) 6 AN FF 3NS-CMS */ long VoiStartRow; /* (0021,1245) 6 AN FF 3NS-CMS */ long VoiStopRow; /* (0021,1246) 6 AN FF 3NS-CMS */ long VoiStartColumn; /* (0021,1247) 6 AN FF 3NS-CMS */ long VoiStopColumn; /* (0021,1248) 6 AN FF 3NS-CMS */ long VoiStartSlice; /* (0021,1249) 6 AN FF 3NS-CMS */ long VoiStopSlice; /* (0021,124A) 6 AN FF 3NS-CMS */ long RangeTypeCode; /* (0021,1260) 6 AN FF 3NS-CMS */ long ReferenceTypeCode; /* (0021,1262) 6 AN FF 3NS-CMS */ object_orientation_t ObjectOrientation; /* (0021,1270) 14 AN FF 3NS-CMS */ object_orientation_t LightOrientation; /* (0021,1272) 14 AN FF 3NS-CMS */ double LightBrightness; /* (0021,1275) 14 AN FF 3NS-CMS */ double LightContrast; /* (0021,1276) 14 AN FF 3NS-CMS */ object_threshold_t OverlayThreshold; /* (0021,127A) 12 AN FF 3NM-CMS */ object_threshold_t SurfaceThreshold; /* (0021,127B) 12 AN FF 3NM-CMS */ object_threshold_t GreyScaleThreshold; /* (0021,127C) 12 AN FF 3NM-CMS */ } shadow_relationship_ct_t; typedef struct shadow_relationship_mr_tag /* MR common shadowsubgroups */ { long PhaseCorRowSeq; /* (0021,1320) 6 AN FF 3NS-CMS */ long PhaseCorColSeq; /* (0021,1321) 6 AN FF 3NS-CMS */ long PhaseCorRowRec; /* (0021,1322) 6 AN FF 3NS-CMS */ long PhaseCorColRec; /* (0021,1324) 6 AN FF 3NS-CMS */ long NumberOf3DRawPartNom; /* (0021,1330) 6 AN FF 3NS-CMS */ long NumberOf3DRawPartCur; /* (0021,1331) 6 AN FF 3NS-CMS */ long NumberOf3DImaPart; /* (0021,1334) 6 AN FF 3NS-CMS */ long Actual3DImaPartNumber; /* (0021,1336) 6 AN FF 3NS-CMS */ long Gap1338; long NumberOfSlicesNom; /* (0021,1340) 6 AN FF 3NS-CMS */ long NumberOfSlicesCur; /* (0021,1341) 6 AN FF 3NS-CMS */ long CurrentSliceNumber; /* (0021,1342) 6 AN FF 3NS-CMS */ long CurrentGroupNumber; /* (0021,1343) 6 AN FF 3NS-CMS */ long MipStartRow; /* (0021,1345) 6 AN FF 3NS-CMS */ long MipStopRow; /* (0021,1346) 6 AN FF 3NS-CMS */ long MipStartColumn; /* (0021,1347) 6 AN FF 3NS-CMS */ long MipStopColumn; /* (0021,1348) 6 AN FF 3NS-CMS */ long MipStartSlice; /* (0021,1349) 6 AN FF 3NS-CMS */ long MipStopSlice; /* (0021,134A) 6 AN FF 3NS-CMS */ long SignalMask; /* (0021,1350) 4 BI HX 2DS-CMS */ long Gap1350; long DelayAfterTrigger; /* (0021,1352) 6 AN FF 3NS-CMS */ long RRInterval; /* (0021,1353) 6 AN FF 3NS-CMS */ int Pad1; /* Dummy for byte alignment */ double NumberOfTriggerPulses; /* (0021,1354) 14 AN FF 3NS-CMS */ double RepetitionTime; /* (0021,1356) 14 AN FF 3NS-CMS */ gate_phase_t GatePhase; /* (0021,1357) 12 AT EV 3NS-CMS */ int Pad2; /* Dummy for byte alignment */ double GateThreshold; /* (0021,1358) 14 AN FF 3NS-CMS */ double GateRatio; /* (0021,1359) 14 AN FF 3NS-CMS */ long NumberOfInterpolatedImages; /* (0021,1360) 6 AN FF 3NS-CMS */ long NumberOfEchoes; /* (0021,1370) 6 AN FF 3NS-CMS */ double SecondEchoTime; /* (0021,1372) 14 AN FF 3NS-CMS */ double SecondRepetitionTime; /* (0021,1373) 14 AN FF 3NS-CMS */ long CardiacCode; /* (0021,1380) 6 AN FF 3NS-CMS */ int Pad3; /* Dummy for byte alignment */ double CurrentSliceDistanceFactor; /* (0021,1344) 14 AN FF 3NS-CMS */ order_of_slices_t OrderOfSlices; /* (0021,134F) 12 AT EV 3NS-CMS */ int Pad4; /* Dummy for byte alignment */ double SlabThickness; /* (0021,1339) 6 AN FF 3NS-CMS */ } shadow_relationship_mr_t; typedef struct shadow_relationship_ct_spe_tag /* CT special shadowsubgroups */ { long EvaluationMask[2]; /* (0021,2220) 4 BD FF 2DM-CMS */ long Gap2220[2]; short ExtendedProcessingMask[7]; /* (0021,2230) 4 BD FF 2DM-CMS */ short Gap2230[3]; long CreationMask[2]; /* (0021,2210) 2 BI HX 3NM-CMS */ long Gap2210[2]; } shadow_relationship_ct_spe_t; typedef struct shadow_relationship_mr_spe_tag /* MR special shadowsubgroups */ { double EpiReconstructionPhase; /* (0019,12A0) 14 AN FF 3NS-CMS */ double EpiReconstructionSlope; /* (0019,12A1) 14 AN FF 3NS-CMS */ double EpiCapacity[6]; /* (0019,14C1) 14 AN FF 3NM-CMS */ double EpiInductance[3]; /* (0019,14C2) 14 AN FF 3NM-CMS */ long EpiSwitchConfigurationCode[3]; /* (0019,14C3) 6 AN FF 3NM-CMS */ long EpiSwitchHardwareCode[3]; /* (0019,14C4) 6 AN FF 3NM-CMS */ long EpiSwitchDelayTime[6]; /* (0019,14C5) 6 AN FF 3NM-CMS */ char EpiFileName[LENGTH_FILE_NAME + 1]; /* (0019,1514) 64 AT FF 3NS-CMS */ } shadow_relationship_mr_spe_t; /*************************************************/ /* Image Presentation Information (Group 0029'H) */ /*************************************************/ typedef struct shadow_presentation_tag { window_style_t WindowStyle; /* (0029,1110) 8 AT EV 2DS-CMS */ pixel_quality_code_t PixelQualityCode; /* (0029,1120) 12 AT FF 2DM-CMS */ pixel_quality_value_t PixelQualityValue; /* (0029,1122) 6 AN FF 3NM-CMS */ save_code_t ArchiveCode; /* (0029,1150) 8 AN EV 3NS-CMS */ save_code_t ExposureCode; /* (0029,1151) 8 AN EV 3NS-CMS */ long SortCode; /* (0029,1152) 6 AN FF 3NS-CMS */ long Splash; /* (0029,1160) 6 AN FF 3NS-CMS */ } shadow_presentation_t; #endif /*== HELP TEXT ===========================================================================*/ #ifdef DS_STC_TOOL "G09.Ide.DataObjectSubtype.M", "Data Object Subtype (0009,0041) - main: CT | MR Signed | MR Unsigned", "G09.Ide.DataObjectSubtype.D", "Data Object Subtype (0009,0041) - defined by: Predefined | defined by User", "G09.Ide.DataObjectSubtype.S - sub: NONE", "Data Object Subtype (0009,0041)", "G09.Ide.NumberOfMeasurements", "Number of Measurements (0009,1200)", "G09.Ide.StorageMode", "Storage Mode (0009,1210): COMPRESS | EXPANDED | MIP_MPR | REDUCED | XDR", "G09.Ide.EvaluationMask", "Evaluation Mask - Image (0009,1212)", "G09.Ide.LastMoveDate.Year", "Last Move Date (0009,1226)", "G09.Ide.LastMoveTime.Hour", "Last Move Time (0009,1227)", "G09.Ide.GeneratorIdentificationLabel", "Generator Identification Label (0009,1310)", "G09.Ide.GantryIdentificationLabel", "Gantry Identification Label (0009,1311)", "G09.Ide.XRayTubeIdentificationLabel", "X-Ray Tube Identification Label (0009,1312)", "G09.Ide.DetectorIdentificationLabel", "Detector Identification Label (0009,1313)", "G09.Ide.DASIdentificationLabel", "DAS Identification Label (0009,1314)", "G09.Ide.SMIIdentificationLabel", "SMI Identification Label (0009,1315)", "G09.Ide.CPUIdentificationLabel", "CPU Identification Label (0009,1316)", "G09.Ide.HeaderVersion", "Header Version (0009,1320)", "G11.Pat.Organ", "Organ (0011,1010)", "G11.Pat.RegistrationDate.Year", "Registration Date (0011,1110)", "G11.Pat.RegistrationTime.Hour", "Registration Time (0011,1111)", "G11.Pat.UsedPatientWeight", "Used Patient Weight (0011,1123) in kg", "G11.Pat.OrganCode", "Organ Code (0011,1140)", "G13.PatMod.ModifyingPhysician", "Modifying Physician (0013,1000)", "G13.PatMod.ModificationDate.Year", "Modification Date (0013,1022)", "G13.PatMod.ModificationTime.Hour", "Modification Time (0013,1010)", "G13.PatMod.PatientName", "former Patient Name (0013,1020)", "G13.PatMod.PatientId", "former Patient Id (0013,1022)", "G13.PatMod.PatientBirthdate.Year", "former Patient Birthdate (0013,1030)", "G13.PatMod.PatientWeight", "former Patient Weight (0013,1031)", "G13.PatMod.PatientMaidenName", "former Patient Maiden Name (0013,1032)", "G13.PatMod.ReferringPhysician", "former Referring Physician (0013,1033)", "G13.PatMod.AdmittingDiagnosis", "former Admitting Diagnosis (0013,1034)", "G13.PatMod.PatientSex", "former Patient Sex (0013,1035)", "G13.PatMod.ProcedureDescription_1", "former Procedure Description_1 (0013,1040)", "G13.PatMod.ProcedureDescription_2", "former Procedure Description_2", "G13.PatMod.RestDirection", "former Rest Direction (0013,1042)", "G13.PatMod.PatientPosition", "former Patient Position (0013,1044)", "G13.PatMod.ViewDirection", "former View Direction (0013,1046)", "G19.Acq1.CM.NetFrequency", "Net Frequency (0019,1010) in Hz", "G19.Acq1.CM.MeasurementMode.M", "Measurement Mode (0019,1020) - main: ADJUstment | EXAMination | TEST", "G19.Acq1.CM.MeasurementMode.S", "Measurement Mode (0019,1020) - subreason", "G19.Acq1.CM.CalculationMode.M", "CalculationMode (0019,1030) - main: NONE | A| PC | PU", "G19.Acq1.CM.CalculationMode.S", "Calculation Mode (0019,1030) - sub: NONE |BSP | IRS | SUN | VAX", "G19.Acq1.CM.NoiseLevel", "Noise Level (0019,1050)", "G19.Acq1.CM.NumberOfDataBytes", "Number of Data Bytes (0019,1060)", "G19.Acq2.Ct.SourceSideCollimatorAperture", "Source Side Collimator Aperture (0019,1110) in mm", "G19.Acq2.Ct.DetectorSideCollimatorAperture", "Detector Side Collimator Aperture (0019,1111) in mm", "G19.Acq2.Ct.ExposureTime", "current Exposure Time (0019,1120) in msec", "G19.Acq2.Ct.Exposure", "current Exposure (0019,1121) in mAs", "G19.Acq2.Ct.GeneratorPower", "current Generator Power (0019,1125) in kW", "G19.Acq2.Ct.GeneratorVoltage", "current Generator Voltage (0019,1126) in kV", "G19.Acq2.Ct.GeneratorVoltageDual", "second value of Generator Voltage (0019,1126) in kV", "G19.Acq2.Ct.MasterControlMask", "Master Control Mask (0019,1140)", "G19.Acq2.Ct.ProcessingMask", "Processing Mask (0019,1142)", "G19.Acq2.Ct.NumberOfVirtuellChannels", "Number of Virtuell Channels (0019,1162)", "G19.Acq2.Ct.NumberOfReadings", "Number of Readings (0019,1170)", "G19.Acq2.Ct.NumberOfProjections", "Number of Projections (0019,1174)", "G19.Acq2.Ct.NumberOfBytes", "Number of Bytes (0019,1175)", "G19.Acq2.Ct.ReconstructionAlgorithmSet", "Reconstruction Algorithm Set (0019,1180): smooth, standard, sharp RPF number", "G19.Acq2.Ct.ReconstructionAlgorithmIndex", "Reconstruction Algorithm Index (0019,1181)", "G19.Acq2.Ct.RegenerationSoftwareVersion", "Regeneration Software Version (0019,1182)", "G19.Acq2.Mr.TotalMeasurementTime", "Total Measurement Time - nominal (0019,1210) in sec", "G19.Acq2.Mr.TotalMeasurementTimeCur", "Total Measurement Time - current (0019,1211) in sec", "G19.Acq2.Mr.StartDelayTime", "Start Delay Time (0019,1212) in sec", "G19.Acq2.Mr.DwellTime", "Dwell Time (0019,1213) in usec", "G19.Acq2.Mr.NumberOfPhases", "Number of Phases (0019,1214)", "G19.Acq2.Mr.SequenceControlMask", "Sequence Control Mask (0019,1216)", "G19.Acq2.Mr.MeasurementStatusMask", "Measurement Status Mask (0019,1218)", "G19.Acq2.Mr.NumberOfFourierLinesNominal", "nominal Number of Fourier Lines (0019,1220)", "G19.Acq2.Mr.NumberOfFourierLinesCurrent", "current Number of Fourier Lines (0019,1221)", "G19.Acq2.Mr.NumberOfFourierLinesAfterZero", "Number of Fourier Lines after Zero (0019,1226)", "G19.Acq2.Mr.FirstMeasuredFourierLine", "First Measured Fourier Line (0019,1228)", "G19.Acq2.Mr.AcquisitionColumns", "Acquisition Columns (0019,1230)", "G19.Acq2.Mr.ReconstructionColumns", "Reconstruction Columns (0019,1231)", "G19.Acq2.Mr.NumberOfAverages", "current Number of Averages (0019,1250)", "G19.Acq2.Mr.FlipAngle", "Flip Angle (0019,1260) in degree", "G19.Acq2.Mr.NumberOfPrescans", "Number of Prescans (0019,1270) in main loop", "G19.Acq2.Mr.FilterTypeRawData", "Filter Type Raw Data (0019,1281): NONE | EXTERNAL | FERMI | GAUSS |HANNING", "G19.Acq2.Mr.FilterParameterRawData.Value1", "Filter Parameter Raw Data (0019,1282)", "G19.Acq2.Mr.FilterTypeImageData", "Filter Type Image Data (0019,1283): NONE | NO1", "G19.Acq2.Mr.FilterParameterImageData.Value1", "Filter Parameter Image Data (0019,1284)", "G19.Acq2.Mr.FilterTypePhaseCorrection", "Filter Type Phase Correction (0019,1285): NONE | EXTERNAL | FERMI | GAUSS |HANNING", "G19.Acq2.Mr.FilterParameterPhaseCorrection", "Filter Parameter Phase Correction (0019,1286)", "G19.Acq2.Mr.NumberOfSaturationRegions", "Number of Saturation Regions (0019,1290)", "G19.Acq2.Mr.ImageRotationAngle", "Image Rotation Angle (0019,1294) for readout direction in radiant", "G19.Acq2.Mr.CoilIdMask", "Coil ID Mask (0019,1296)", "G19.Acq2.Mr.CoilPosition.Sag", "Coil Position (0019,1298) vector (sag, cor, tra) in mm", "G21.Rel3.Mr.EpiReconstructionPhase", "EPI Reconstruction Phase (0019,12A0)", "G21.Rel3.Mr.EpiReconstructionSlope", "EPI Reconstruction Slope (0019,12A1)", "G19.Acq3.Ct.DistanceSourceToSourceSideCollimator", "Distance Source to Source Side Collimator (0019,1310) in mm", "G19.Acq3.Ct.DistanceSourceToDetectorSideCollimator", "Distance Source to Detector Side Collimator (0019,1311) in mm", "G19.Acq3.Ct.NumberOfPossibleChannels", "Number of Possible Channels (0019,1320)", "G19.Acq3.Ct.MeanChannelNumber", "Mean Channel Number (0019,1321)", "G19.Acq3.Ct.DetectorSpacing", "Detector Spacing (0019,1322) in minutes", "G19.Acq3.Ct.DetectorCenter", "Detector Center (0019,1323) in channels as real", "G19.Acq3.Ct.ReadingIntegrationTime", "Reading Integration Time (0019,1324) in ms", "G19.Acq3.Ct.DetectorAlignment", "Detector Alignment (0019,1350) in mm", "G19.Acq3.Ct.FocusAlignment", "Focus Alignment (0019,1360) in channel digits", "G19.Acq3.Ct.FocalSpotDeflectionAmplitude", "Focal Spot Deflection Amplitude (0019,1365) in DMS digits", "G19.Acq3.Ct.FocalSpotDeflectionPhase", "Focal Spot Deflection Phase (0019,1366) in DMS digits", "G19.Acq3.Ct.FocalSpotDeflectionOffset", "Focal Spot Deflection Offset (0019,1367) in DMS digits", "G19.Acq3.Ct.WaterScalingFactor", "Water Scaling Factor (0019,1370)", "G19.Acq3.Ct.InterpolationFactor", "Interpolation Factor (0019,1371)", "G19.Acq3.Ct.PatientRegion", "Patient Region (0019,1380): BODY | HEAD", "G19.Acq3.Ct.PatientPhaseOfLife", "Patient Phase of Life (0019,1382): ADULT | CHILD", "G19.Acq3.Mr.MagneticFieldStrength", "Magnetic Field Strength (0019,1412) in T", "G19.Acq3.Mr.ADCVoltage", "ADC Voltage (0019,1414) in V", "G19.Acq3.Mr.ADCOffset", "ADC Offset (0019,1416): real value, imaginary value", "G19.Acq3.Mr.TransmitterAmplitude", "Transmitter Amplitude (0019,1420) in V", "G19.Acq3.Mr.NumberOfTransmitterAmplitudes", "Number of Transmitter Amplitudes (0019,1421)", "G19.Acq3.Mr.TransmitterAttenuator", "Transmitter Attenuator (0019,1422) in dB", "G19.Acq3.Mr.TransmitterCalibration", "Transmitter Calibration (0019,1424) in V", "G19.Acq3.Mr.TransmitterReference", "Transmitter Reference (0019,1426) in V", "G19.Acq3.Mr.ReceiverTotalGain", "Receiver Total Gain (0019,1450) in dB", "G19.Acq3.Mr.ReceiverAmplifierGain", "Receiver Amplifier Gain (0019,1451) in dB", "G19.Acq3.Mr.ReceiverPreamplifierGain", "Receiver Preamplifier Gain (0019,1452) in dB", "G19.Acq3.Mr.ReceiverCableAttenuation", "Receiver Cable Attenuation (0019,1454) in dB", "G19.Acq3.Mr.ReceiverReferenceGain", "Receiver Reference Gain (0019,1455) in dB", "G19.Acq3.Mr.ReceiverFilterFrequency", "Receiver Filter Frequency (0019,1456) in Hz", "G19.Acq3.Mr.ReconstructionScaleFactor", "Reconstruction Scale Factor (0019,1460)", "G19.Acq3.Mr.ReferenceScaleFactor", "Reference Scale Factor (0019,1462)", "G19.Acq3.Mr.PhaseGradientAmplitude", "Phase Gradient Amplitude (0019,1470) in mT/m", "G19.Acq3.Mr.ReadoutGradientAmplitude", "Readout Gradient Amplitude (0019,1471) in mT/m", "G19.Acq3.Mr.SelectionGradientAmplitude", "Selection Gradient Amplitude (0019,1472) in mT/m", "G19.Acq3.Mr.GradientDelayTime.X", "Gradient Delay Time (0019,1480) in usec", "G19.Acq3.Mr.TotalGradientDelayTime", "Total Gradient Delay Time (0019,1482) in usec", "G19.Acq3.Mr.SensitivityCorrectionLabel", "Sensitivity Correction Label (0019,1490)", "G19.Acq3.Mr.RfWatchdogMask", "RF Watchdog Mask (0019,14A0)", "G19.Acq3.Mr.RfPowerErrorIndicator", "RF Power Error Indicator (0019,14A2)", "G19.Acq3.Mr.SarWholeBody.Lim", "Specific Absorption Rate (SAR) - whole body (0019,14A5) - limit in W/kg", "G19.Acq3.Mr.SarWholeBody.Cal", "Specific Absorption Rate (SAR) - whole body (0019,14A5) - calculated value in W/kg", "G19.Acq3.Mr.SarWholeBody.Det", "Specific Absorption Rate (SAR) - whole body (0019,14A5) - detected value in W/kg", "G19.Acq3.Mr.Sed.Lim", "Specific Energy Dose (SED) (0019,14A6) - limit in Wmin/kg", "G19.Acq3.Mr.Sed.Cal", "Specific Energy Dose (SED) (0019,14A6) - calculated value in Wmin/kg", "G19.Acq3.Mr.Sed.Det", "Specific Energy Dose (SED) (0019,14A6) - detected value in Wmin/kg", "G19.Acq3.Mr.AdjustmentStatusMask", "Adjustment Status Mask (0019,14B0)", "G21.Rel3.Mr.EpiCapacity", "EPI Capacity (0019,14C1) in nF", "G21.Rel3.Mr.EpiInductance", "EPI Inductance (0019,14C2) in uH", "G21.Rel3.Mr.EpiSwitchConfigurationCode", "EPI Switch Configuration Code (0019,14C3): 0 (without) | 1 (two) | 2 (four)", "G21.Rel3.Mr.EpiSwitchHardwareCode", "EPI Switch Hardware Code (0019,14C4): 0 (no) | 1 (thyristor) | 2 (transistor)", "G21.Rel3.Mr.EpiSwitchDelayTime", "EPI Switch Delay Time (0019,14C5) in ns", "G19.Acq4.CM.ParameterFileName", "Parameter File Name (0019,1510)", "G19.Acq4.CM.SequenceFileName", "Sequence File Name (0019,1511)", "G19.Acq4.CM.SequenceFileOwner", "Sequence File Owner (0019,1512)", "G19.Acq4.CM.SequenceDescription", "Sequence Description (0019,1513)", "G21.Rel3.Mr.EpiFileName", "EPI File Name (0019,1514)", "G21.Rel1.CM.Target.X", "Target (0021,1011)", "G21.Rel1.CM.RoiMask", "ROI Mask (0021,1020)", "G21.Rel1.CM.FoV.Height", "Field of View (0021,1120) - height in mm", "G21.Rel1.CM.FoV.Width", "Field of View (0021,1120) - width in mm", "G21.Rel1.CM.ImageMagnificationFactor", "Image Magnification Factor (0021,1122)", "G21.Rel1.CM.ViewDirection", "View Direction (0021,1130): HEAD | FEET | AtoP | PtoA | RtoL | LtoR", "G21.Rel1.CM.RestDirection", "Rest Direction (0021,1132): HEAD | FEET", "G21.Rel1.CM.ImagePosition.Sag", "Image Position (0021,1160) vector (sag, cor, tra) in mm", "G21.Rel1.CM.ImageNormal.Sag", "Image Normal (0021,1161) cosinus directions (sag, cor, tra)", "G21.Rel1.CM.ImageDistance", "Image Distance (0021,1163) in mm", "G21.Rel1.CM.ImagePositioningHistoryMask", "Image Positioning History Mask (0021,1165)", "G21.Rel1.CM.ImageRow.Sag", "Image Row (0021,116A) cosinus directions (sag, cor, tra)", "G21.Rel1.CM.ImageColumn.Sag", "Image Column (0021,116B) cosinus directions (sag, cor, tra)", "G21.Rel1.CM.PatientOrientationSet1.Y", "Patient Orientation Set 1 (0021,1170): Y = up, X = left, Z = back", "G21.Rel1.CM.PatientOrientationSet2.Y", "Patient Orientation Set 2 (0021,1171): Y = down, X = right, Z = front", "G21.Rel1.CM.StudyName", "Study Name (0021,1180)", "G21.Rel1.CM.StudyType", "Study Type (0021,1182): CRE | MEA | MIP | MPR | RAW", "G21.Rel2.Ct.RotationAngle", "Rotation Angle (0021,1210) in degree", "G21.Rel2.Ct.StartAngle", "Start Angle (0021,1211) in degree (0.0 == 03:00)", "G21.Rel2.Ct.TubePosition", "Tube Position (0021,1230) in degree (0.0 == 03:00)", "G21.Rel2.Ct.LengthOfTopogram", "Length of Topogram (0021,1232) in mm", "G21.Rel2.Ct.CorrectionFactor", "Correction Factor (0021,1234) for topogram in x-direction", "G21.Rel2.Ct.MaximumTablePosition", "Maximum Table Position (0021,1236) in mm", "G21.Rel2.Ct.TableMoveDirectionCode", "Tabel Move Direction Code (0019,1240): -1 (into) | 1 (out)", "G21.Rel2.Ct.VoiStartRow", "VOI Start Row (0021,1245)", "G21.Rel2.Ct.VoiStopRow", "VOI Stop Row (0021,1246)", "G21.Rel2.Ct.VoiStartColumn", "VOI Start Column (0021,1247)", "G21.Rel2.Ct.VoiStopColumn", "VOI Stop Column (0021,1248)", "G21.Rel2.Ct.VoiStartSlice", "VOI Start Slice (0021,1249)", "G21.Rel2.Ct.VoiStopSlice", "VOI Stop Slice (0021,124A)", "G21.Rel2.Ct.VectorStartRow", "Vector Start Row(0021,1250)", "G21.Rel2.Ct.VectorRowStep", "Vector Row Step(0021,1251)", "G21.Rel2.Ct.VectorStartColumn", "Vector Start Column(0021,1252)", "G21.Rel2.Ct.VectorColumnStep", "Vector Column Step(0021,1253)", "G21.Rel2.Ct.RangeTypeCode", "Range Type Code (0021,1260): 0 (sector) | 1 (parallel)", "G21.Rel2.Ct.ReferenceTypeCode", "Reference Type Code (0021,1262): 0 (topo) | 1 (tra) | 2 (sag) | 3 (cor)", "G21.Rel2.Ct.ObjectOrientation.Phi", "Object Orientation (0021,1270) vector (phi, theta, 1.0) in degree", "G21.Rel2.Ct.LightOrientation.Phi", "Light Orientation (0021,1272) vector (phi, theta, 1.0) in degree", "G21.Rel2.Ct.LightBrightness", "Light Brightness (0021,1275)", "G21.Rel2.Ct.LightContrast", "Light Contrast (0021,1276)", "G21.Rel2.Ct.OverlayThreshold.LowerBoundary", "Overlay Threshold (0021,127A) boundaries (lower, upper)", "G21.Rel2.Ct.SurfaceThreshold.LowerBoundary", "Surface Threshold (0021,127B) boundaries (lower, upper)", "G21.Rel2.Ct.GreyScaleThreshold.LowerBoundary", "Grey Scale Threshold (0021,127C) boundaries (lower, upper)", "G21.Rel2.Mr.PhaseCorRowSeq", "Phase Corrections Rows (0021,1320) of actual sequence", "G21.Rel2.Mr.PhaseCorColSeq", "Phase Corrections Columns (0021,1321) of actual sequence", "G21.Rel2.Mr.PhaseCorRowRec", "Phase Corrrections Rows (0021,1322) of actual reconstruction", "G21.Rel2.Mr.PhaseCorColRec", "Phase Corrrections Columns (0021,1324) of actual reconstruction", "G21.Rel2.Mr.NumberOf3DRawPartNom", "nominal Number of 3D Raw Partitions (0021,1330)", "G21.Rel2.Mr.NumberOf3DRawPartCur", "current Number of 3D Raw Partitions (0021,1331)", "G21.Rel2.Mr.NumberOf3DImaPart", "Number of 3D Image Partitions (0021,1334)", "G21.Rel2.Mr.Actual3DImaPartNumber", "Actual 3D Image Partitions Number (0021,1336)", "G21.Rel2.Mr.SlabThickness", "Slab Thickness (0021,1339) in mm", "G21.Rel2.Mr.NumberOfSlicesNom", "nominal Number of Slices (0021,1340)", "G21.Rel2.Mr.NumberOfSlicesCur", "current Number of Slices (0021,1341)", "G21.Rel2.Mr.CurrentSliceNumber", "Current Slice Number (0021,1342)", "G21.Rel2.Mr.CurrentGroupNumber", "Current Group Number (0021,1343)", "G21.Rel2.Mr.CurrentSliceDistanceFactor", "Current Slice Distance Factor (0021,1344)", "G21.Rel2.Mr.MipStartRow", "MIP Start Row (0021,1345)", "G21.Rel2.Mr.MipStopRow", "MIP Stop Row (0021,1346)", "G21.Rel2.Mr.MipStartColumn", "MIP Start Column (0021,1347)", "G21.Rel2.Mr.MipStopColumn", "MIP Stop Column (0021,1348)", "G21.Rel2.Mr.MipStartSlice", "MIP Start Slice (0021,1349)", "G21.Rel2.Mr.MipStopSlice", "MIP StopSlice (0021,134A)", "G21.Rel2.Mr.OrderOfSlices", "Order of Slices (0021,134F): NONE | ASCENDING | DECREASING | FREE | INTERLEAVED", "G21.Rel2.Mr.SignalMask", "Signal Mask (0021,1350)", "G21.Rel2.Mr.DelayAfterTrigger", "Delay After Trigger (0021,1352) in msec", "G21.Rel2.Mr.RRInterval", "RR Interval (0021,1353) in msec", "G21.Rel2.Mr.NumberOfTriggerPulses", "Number of Trigger Pulses (0021,1354)", "G21.Rel2.Mr.RepetitionTime", "effective Repetition Time (0021,1356) in msec", "G21.Rel2.Mr.GatePhase", "Gate Phase (0021,1357): EXPIRATION | INSPIRATION", "G21.Rel2.Mr.GateThreshold", "Gate Threshold (0021,1358) in %", "G21.Rel2.Mr.GateRatio", "Gate Ratio (0021,1359) in %", "G21.Rel2.Mr.NumberOfInterpolatedImages", "Number of Interpolated Images (0021,1360)", "G21.Rel2.Mr.NumberOfEchoes", "total Number of Echoes (0021,1370)", "G21.Rel2.Mr.SecondEchoTime", "second Echo Time (0021,1372) in msec", "G21.Rel2.Mr.SecondRepetitionTime", "second Repetition Time (0021,1373) in msec", "G21.Rel2.Mr.CardiacCode", "Cardiac Code (0021,1380)", "G21.Rel3.Ct.CreationMask", "Creation Mask - Service (0021,2210)", "G21.Rel3.Ct.EvaluationMask", "Evaluation Mask - Service (0021,2220)", "G21.Rel3.Ct.ExtendedProcessingMask", "Extended Processing Mask (0021,2230)", "G29.Pre.WindowStyle", "Window Style (0029,1110): NONE | DOUBLE | HIGH | STD 1 | STD 2", "G29.Pre.PixelQualityCode.Min", "Pixel Quality Code (0029,1120): NONE | ESTIMATED | EXACT", "G29.Pre.PixelQualityValue.Min", "Pixel Quality Value (0029,1122)", "G29.Pre.ArchiveCode", "Archive Code (0029,1150): NOT | MARKED | DONE", "G29.Pre.ExposureCode", "Exposure Code (0029,1151): NOT | MARKED | DONE", "G29.Pre.SortCode", "Sort Code (0029,1152): 0 (CH) | 1 (AN) | 2 (TE) | 3 (TR) | 4 (TD) | 5 (CA)", "G29.Pre.Splash", "Splash (0029,1160)", "G51.Txt_buf", "Image Text (0051,1010)", "Gap.Fill_buf", "reserved: gap to fill Internal Header to kByte border", #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_transformation.h0000644000175000000620000007500512574624760030760 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_transformation.h Description: The header file defines the common types, constants and macros for the data set library. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_TRANSFORMATION #define DS_TRANSFORMATION /*== VERSION CONTROL ====================================================================*/ /* PRECOMPILER: define version control contants */ /* NOTE: versions */ /* The following list shows the used version codes in chronological order: Date | Doc DS IH ACR SPI notes -------------+---------------------------------------------------------------- 1989-JUL-03 | V0.2 - - 1.0 01.00 1990-MAR-10 | V1.1 - VA1 2.0 01.00 1990-MAR-12 | V1.1 V1.2 VA1 2.0 01.00 1990-APR-27 | V1.3 V1.3 VA3 2.0 01.00 internal version 1990-AUG-01 | V1.4 V1.4 VA4 2.0 01.00 1990-OCT-01 | V1.5 V1.5 VA5 2.0 01.00 1990-OCT-11 | V1.5 V1.51 VA5 2.0 01.00 M 15 1990-OCT-15 | V1.5 V1.52 VA5 2.0 01.00 M 13, M19 1990-NOV-16 | V1.5 V1.53 VA53 2.0 01.00 parts of M16 1990-NOV-23 | V1.5 V1.54 VA53 2.0 01.00 M 28 1990-DEC-07 | V1.5 V1.55 VA55 2.0 01.00 M31, parts of M16, M30 1991-JAN-31 | V2.0 V2.0 VB 2.0 01.00 M*, som, num2, ... 1991-FEB-19 | V2.0 V2.1 VB 2.0 01.00 M 40 1991-FEB-28 | V2.0 V2.2 VB2 2.0 01.00 M 38, M 39, M 40, M 45 1991-MAR-14 | V2.0 V2.3 VB2 2.0 01.00 M 41, M 48 1991-MAR-28 | V2.0 V2.4 VB2 2.0 01.00 M*, som, num2, ... 1991-APR-17 | V2.0 V2.5 VB3 2.0 01.00 M* 1991-MAY-23 | V2.0 V2.6 VB4 2.0 01.00 parts of M49 1991-JUL-31 | V2.0 V2.7 VB5 2.0 01.00 M*, db_set, num1, ... 1991-AUG-08 | V2.0 V2.8 VB5 2.0 01.00 M 65 1991-SEP-12 | V2.0 V2.9 VB5 2.0 01.00 Image Text: M80, 73, 66, 64, 60, 59 1991-OCT-10 | V2.0 V2.10 VB5 2.0 01.00 M 86 1991-NOV | V3.0 V3.0 VB6 2.0 01.00 M*, som0 labels: ------- Doc Documentation Version ("Phone Book" Version) DS Data Set Library (Software) Version IH Internal Header Version ACR NEMA Standard Version SPI SPI Standard Version */ /* version strings */ /* NOTE: Software Version String */ /* The constant "DS_SOFTWARE_VERSION_STRING" is not longer supported. The software version is set in this and future software releases in data set library function "ds_info_get_software_version" directly. In this way several modul compilations after changing this common used data set library header file are avoided. */ /* constant definition */ #define HEADER_VERSION_STRING "VB6 " #define RECOGNITION_CODE_STRING "ACR-NEMA 2.0" #define SPI_VERSION_STRING "SPI VERSION 01.00 " /*== CONSTANTS ==========================================================================*/ /* PRECOMPILER: define common non-numeric constants */ /* logical constants */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* common status file constants */ #define DS_STATUS_FILE_TYPE struct STC_COMMON /* common nil pointer constants */ #define DS_CHAR_NIL ((char *) 0) #define DS_DOUBLE_NIL ((double *) 0) #define DS_INT_NIL ((int *) 0) #define DS_LONG_NIL ((long *) 0) #define DS_SHORT_NIL ((short *) 0) #define DS_STATUS_FILE_NIL ((DS_STATUS_FILE_TYPE *) 0) #define DS_VOID_NIL ((void *) 0) /* PRECOMPILER: define status area element codes */ /* NOTE: list of status abbreviations */ /* PRE == PREparation of item value e.g. adapt, collect information KEY == build up item KEY BUF == transfere item value into nema data set BUFfer SEP == SEParation of item from nema data set buffer SET == SET back item value for internal use POS == POSition item value into interanl header e.g. split, convert information CH1 == Check a first parameter CH2 == Check a second parameter */ /* error handling constants */ #define PRE 0 #define KEY 1 #define BUF 2 #define SEP 0 #define SET 1 #define POS 2 #define CH1 0 #define CH2 1 /* sign constants */ #define DELIMITER_DS_SIGN '\\' #define INITALIZE_DS_SIGN ' ' #define NON_PRINT_ABLE_DS_SIGN '~' #define OVERLAY_DS_SIGN '/' #define PADDING_DS_SIGN ' ' #define UNDEFINED_TEXT_DS_SIGN ' ' /* string constants */ #define IMAGE_GRAPHIC_FORMAT_CMS "CMS " /* end-of constants */ #define EOAL (-19223) #define END_OF_DATA_SET (-19224) /* coordinate system constants */ #define AAA 0 #define BBB 1 #define CCC 2 #define XXX 0 #define YYY 1 #define ZZZ 2 #define SAG 0 #define COR 1 #define TRA 2 #define ROW 0 #define COLUMN 1 #define DOWN 0 #define RIGHT 1 /* common numeric contants */ #define DS_DO_NOT_FILL (-1L) #define DS_MAX_CHAR (256 + 1) #define DS_SAVE_FACTOR (4096) #define DS_UID_SIZE (26) #define DS_WORK_CHAR (32 + 1) /*== RUNTIME CONTROL ====================================================================*/ /* PRECOMPILER: define debug constants */ /* NOTE: debug constants */ /* The constant 'DEBUGGING_BY_THUMSER' is used to compile or not compile C-source code for debugging. If you change the '#undef'-command in a '#define'-command all parts with debug code are compiled. This is usefull to activate calls of function printf() etc. The macro 'DS_ERROR_CONDITION' controls the call of function ds_initiate_error_handling(). During normal run time this macro is defined in a matter, that only if a error or warning raised the function "ds_initiate_error_handling()" will be actived. If the debug switch 'DEBUGGING_BY_THUMSER' is defined for every item a message is printed to standard output device. Note that in this case the interface status is not updated correctly. Please use the shown messages to anlyse the item transformation results. */ #undef DEBUGGING_BY_THUMSER #ifndef DEBUGGING_BY_THUMSER #define DS_ERROR_CONDITION (!(StatusList[0] & 0x0001) || \ !(StatusList[1] & 0x0001) || \ !(StatusList[2] & 0x0001)) #else #define DS_ERROR_CONDITION TRUE #endif #define ds_initiate_error_handling(Group, Element, Error, Status, Return) \ ds_init_error_handling(__FILE__, __LINE__, Group, Element, Error, Status, Return) /*-----------------------------------------------------------------------------------------*/ /* PRECOMPILER: define interacive constants */ /* NOTE: interacive constants */ /* The constant 'DS_INTERACTIVE' is used to compile or not compile C-source code for a interactive use of separation functions. If you change the '#undef'-command in a '#define'-command all parts with interactive code are compiled. */ #undef DS_INTERACTIVE /*-----------------------------------------------------------------------------------------*/ /* PRECOMPILER: define Exception Handler */ /* NOTE: Exception Handler */ /* Three makros are implemented to raise the exception handler. You can choise one for your current work by precompiler statement #define and #undefine, respectively. DS_RAISE_NORMAL The raise is done with a goto statement. Only to raise an exception handler the goto statement is used in data set library software. No other actions will be done. DS_RAISE_TRACE The raise is done with a goto statement also, but before flow control goes to the raised exception handler a printf() call is done. The following text is printed: "", line : raise ==> The print out of this message can be controlled by file '/tmp/ds_trace_on'. If the file exist the print out is done otherwise nothing is done. In this manner the switch can be done during runtime without new compilation or process restart. If 'DS_RAISE_TRACE' defined an item trace is possible too. For more information about this topic see description of function 'ds_finally_interface_status_update()' in modul 'ds_mixed.c'. DS_RAISE_DBX The raise is done with a goto statement also, but before flow control goes to the raised exception handler a ds_stop_dbx() is done. This function has no other function than to stop point for debugger. Use during a dbx session the command 'stop in ds_stop_dbx'. The debugger stops program execution everytime a exception is raised. A check of current active parameters is now possible. */ #undef DS_RAISE_NORMAL #define DS_RAISE_TRACE #undef DS_RAISE_DBX #ifdef DS_RAISE_NORMAL #define DS_RAISE_EXCEPTION(x) goto x #endif #ifdef DS_RAISE_TRACE #define DS_RAISE_EXCEPTION(x) { \ ds_trace_control(__FILE__, __LINE__, Status); \ goto x; \ } #endif #ifdef DS_RAISE_DBX #define DS_RAISE_EXCEPTION(x) { \ ds_stop_dbx(); \ goto x; \ } #endif /* PRECOMPILER: define Return Code Handler */ /* NOTE: Return Code Handler */ /* To set a special return code the makro 'DS_SET_RETURN_CODE()' is available. Three forms of this makro are implemented to manage the handling during setting of a special return code. You can choise one for your current work by precompiler statement #define and #undefine, respectively. DS_RETURN_NORMAL The return code is set with a normal assign statement. No other actions will be done. DS_RETURN_TRACE The return code is set with an assign statement also, but in addition a printf() call is done. The following text is printed: "", line : return ==> The print out of this message can be controlled by file '/tmp/ds_return_on'. If the file exist the print out is done otherwise nothing is done. In this manner the switch can be done during runtime without new compilation or process restart. DS_RETURN_DBX The return code is set with an assign statement also, but in additon a call of function ds_stop_dbx() is done. This function has no other function than to stop point for debugger. Use during a dbx session the command 'stop in ds_stop_dbx'. The debugger stops program execution everytime a special return code is set. A check of current active parameters is now possible. */ #undef DS_RETURN_NORMAL #define DS_RETURN_TRACE #undef DS_RETURN_DBX #ifdef DS_RETURN_NORMAL #define DS_SET_RETURN_CODE(x) ReturnCode = (x); #endif #ifdef DS_RETURN_TRACE #define DS_SET_RETURN_CODE(x) { \ ds_return_control(__FILE__, __LINE__, (x)); \ ReturnCode = (x); \ } #endif #ifdef DS_RETURN_DBX #define DS_SET_RETURN_CODE(x) { \ ds_stop_dbx(); \ ReturnCode = (x); \ } #endif /*-----------------------------------------------------------------------------------------*/ /* PRECOMPILER: define length of NEMA item values */ /* NOTE: length of NEAM item values control */ /* If you want to use other lengths for NEMA item values as the CMS defined standard lengths then change the following precompiler statement '#undef DS_USE_NOT_STAN- DARD_LENGTH' to '#define DS_USE_NOT_STANDARD_LENGTH'. A reason to do this is do minimize the NEAM Data Set Length during data set build up. If you want a minimal NEAM Data Set length define the following xxx_LENGTH identifier with a replacement of 2. Then the Data Set Library functions get and use the minimal necessary length of each item. If you use a replacement less than two then a fatal error occur. */ #undef DS_USE_NOT_STANDARD_LENGTH #ifndef DS_USE_NOT_STANDARD_LENGTH #define AGE_LENGTH LENGTH_AGE #define DATE_LENGTH 10L #define DIAGNOSIS_LENGTH LENGTH_DIAGNOSIS #define DIRECTION_LENGTH LENGTH_DIRECTION #define COMMENT_LENGTH LENGTH_COMMENT #define FILE_NAME_LENGTH LENGTH_FILE_NAME #define FILTER_ID_LENGTH LENGTH_FILTER_ID #define HEADER_VERSION_LENGTH LENGTH_HEADER_VERSION #define INTEGER_NUMBER_LENGTH 6L #define LABEL_LENGTH LENGTH_LABEL #define LITTLE_IDENT_LENGTH 4L #define LONG_IDENT_LENGTH 12L #define NUCLEUS_LENGTH LENGTH_NUCLEUS #define MANUFACTURER_LENGTH LENGTH_MANUFACTURER #define MIDDLE_IDENT_LENGTH 8L #define ORIENTATION_LENGTH LENGTH_ORIENTATION #define PATIENT_ID_LENGTH LENGTH_PATIENT_ID #define REAL_NUMBER_LENGTH 14L #define SEQUENCE_INFO_LENGTH LENGTH_SEQUENCE_INFO #define SHORT_IDENT_LENGTH 2L #define SOFTWARE_VERSION_LENGTH LENGTH_SOFTWARE_VERSION #define SPI_VERSION_LENGTH 18L #define TIME_LENGTH 12L #else #define AGE_LENGTH 2L #define DIAGNOSIS_LENGTH 2L #define DIRECTION_LENGTH 2L #define COMMENT_LENGTH 2L #define FILE_NAME_LENGTH 2L #define FILTER_ID_LENGTH 2L #define HEADER_VERSION_LENGTH 2L #define INTEGER_NUMBER_LENGTH 2L #define LABEL_LENGTH 2L #define LITTLE_IDENT_LENGTH 2L #define LONG_IDENT_LENGTH 2L #define NUCLEUS_LENGTH 2L #define MANUFACTURER_LENGTH 2L #define MIDDLE_IDENT_LENGTH 2L #define ORIENTATION_LENGTH 2L #define PATIENT_ID_LENGTH 2L #define REAL_NUMBER_LENGTH 2L #define SEQUENCE_INFO_LENGTH 2L #define SHORT_IDENT_LENGTH 2L #define SOFTWARE_VERSION_LENGTH 2L #define SPI_VERSION_LENGTH 2L #define DATE_LENGTH 10L #define TIME_LENGTH 12L #endif /*-----------------------------------------------------------------------------------------*/ /*== TYPES ==============================================================================*/ /* DECLARATION: declare types */ /* - undependent types */ typedef enum data_area_type_tag { Area_Type_BYTE = 1, Area_Type_LONG = 2, Area_Type_SHORT = 3, Area_Type_UNDEFINED = Enum_UNDEFINED } data_area_type_t; typedef enum data_set_format_tag { Set_format_BLOCK = 1, Set_format_NEMA = 2, Set_format_UNDEFINED = Enum_UNDEFINED } data_set_format_t; typedef struct image_orientation_tag { double RowX; double RowY; double RowZ; double ColX; double ColY; double ColZ; } image_orientation_t; typedef enum image_class_tag { Image_Class_CORONAL = 10, Image_Class_SAGITTAL = 20, Image_Class_TRANSVERSAL = 30, Image_Class_UNDEFINED = Enum_UNDEFINED } image_class_t; typedef enum image_text_type_tag { Image_Text_Type_BLACK = 1, Image_Text_Type_MR_RAW = 2, Image_Text_Type_NONE = 3, Image_Text_Type_NORMAL = 4, Image_Text_Type_CT_REBUILD = 5, Image_Text_Type_UNDEFINED = Enum_UNDEFINED } image_text_type_t; typedef enum item_quality_tag { Item_Quality_AFTER_IN_FOLLOWING_GROUP = 10, Item_Quality_AFTER_IN_THIS_GROUP = 11, Item_Quality_BEFORE = 20, Item_Quality_END_OF_DATA_SET = 50, Item_Quality_EQUAL = 30, Item_Quality_OUT_OF_RANGE = 99, Item_Quality_UNDEFINED = Enum_UNDEFINED } item_quality_t; typedef char nema_patient_place_t[2][4]; typedef enum overlay_number_tag { Overlay_6000 = 1, Overlay_6002 = 2, Overlay_6004 = 3, Overlay_6006 = 4, Overlay_UNDEFINED = Enum_UNDEFINED } overlay_number_t; typedef union quart_as_tag /* four bytes as different bit patterns */ { char AsBytes[4]; /* as four bytes */ float AsFloat; /* as float */ long AsLong; /* as long */ short AsShort[2]; /* as two short */ } quart_as_t; typedef enum swap_mode_tag { Swap_NO = 0, Swap_YES = 1, Swap_UNDEFINED = Enum_UNDEFINED } swap_mode_t; typedef struct transformation_list_tag { int Index; double Factor; } transformation_list_t[3]; typedef double matrix_3x3_t[3][3]; typedef enum update_mode_tag { Update_ELEMENT = 1, Update_FOUND = 2, Update_GROUP = 3, Update_UNDEFINED = Enum_UNDEFINED } update_mode_t; typedef enum value_mode_tag { Value_IS_NORMAL = 1, Value_IS_LAST = 2, Value_UNDEFINED = Enum_UNDEFINED } value_mode_t; typedef enum value_representation_tag { Representation_AN = 10, Representation_AT = 11, Representation_BI = 20, Representation_BR = 21, Representation_BY = 22, Representation_BX = 23, Representation_UNDEFINED = Enum_UNDEFINED } value_representation_t; typedef double vector_t[3]; /* - dependent types */ typedef struct nema_image_place_tag { nema_patient_place_t PatientOrientation; vector_t ImagePosition; vector_t ImageOrientation[2]; } nema_image_place_t; /*== MACROS =============================================================================*/ /* PRECOMPILER: define macros */ /* NOTE: define macros */ /* If you want to use a "Mathematics" macro don't forget to insert the precompiler line "#include ". */ /*-----------------------------------------------------------------------------------------*/ /* Bit Manipulation */ #define DS_BIT_CLEAR(Variable, BitNumber) ((Variable) = ((Variable) & ~(1<<(BitNumber)))) #define DS_BIT_TEST(Variable, BitNumber) (((Variable) & (1<<(BitNumber))) != 0) #define DS_BIT_SET(Variable, BitNumber) ((Variable) = ((Variable) | (1<<(BitNumber)))) #define DS_BIT_TOGGLE(Variable, BitNumber) (for future use) /*-----------------------------------------------------------------------------------------*/ /* Mathematics */ #define DS_MAX(a,b) ((a)>(b)?(a):(b)) #define DS_MIN(a,b) ((a)>(b)?(b):(a)) #define DS_I_ABS(x) ((x) < 0 ? -(x) : (x)) #define DS_R_ABS(x) fabs((x)) #define DS_R_EQUAL(x, y) (fabs((double)(x) - (double)(y)) <= (double)0.00001) #define DS_R_GREATER(x, y) ((x) > (y)) #define DS_R_LESS(x, y) ((x) < (y)) #define DS_V_EQUAL(x, y) ds_vector_check_if_equal((x), (y)) #define DS_G_SIN(x) (sin(((M_PI/180.0)*(x)))) #define DS_G_COS(x) (cos(((M_PI/180.0)*(x)))) #define DS_G_TAN(x) (tan(((M_PI/180.0)*(x)))) #define DS_G_TAN2(x,y) (tan2((M_PI/180.0)*((x),(y)))) #define DS_G_ASIN(x) ((180.0/M_PI)*asin((x))) #define DS_G_ACOS(x) ((180.0/M_PI)*acos((x))) #define DS_G_ATAN(x) ((180.0/M_PI)*atan((x))) #define DS_G_ATAN2(x,y) ((180.0/M_PI)*atan2((x),(y))) #define DS_R_SIN(x) (sin((x)))) #define DS_R_COS(x) (cos((x)))) #define DS_R_TAN(x) (tan((x)))) #define DS_R_TAN2(x,y) (tan2((x),(y)))) #define DS_R_ASIN(x) (asin((x))) #define DS_R_ACOS(x) (acos((x))) #define DS_R_ATAN(x) (atan((x))) #define DS_R_ATAN2(x,y) (atan2((x),(y))) /*-----------------------------------------------------------------------------------------*/ /* Transformation Matrix Handling */ #define DS_SET_TRANSFORMATION_LIST(a_fac, a_ind, b_fac, b_ind, c_fac, c_ind) \ { \ TransformationList[AAA].Index = (a_ind); \ TransformationList[AAA].Factor = (a_fac); \ TransformationList[BBB].Index = (b_ind); \ TransformationList[BBB].Factor = (b_fac); \ TransformationList[CCC].Index = (c_ind); \ TransformationList[CCC].Factor = (c_fac); \ } /*-----------------------------------------------------------------------------------------*/ /* Image Class / History Label */ #define DS_SET_HISTORY_LABEL(ClassLabel, No1Label, No2Label) \ strncpy(Header->G51.Txt.SliceOrientationNo1, (ClassLabel), (int) 3); \ if (!(DS_R_EQUAL(SliceAngleNo1, 0.0))) \ { \ Header->G51.Txt.SliceOrientationNo1[3] = '>'; \ strncpy(&(Header->G51.Txt.SliceOrientationNo1[4]), (No1Label), (int) 3); \ } \ if (!(DS_R_EQUAL(SliceAngleNo2, 0.0))) \ { \ Header->G51.Txt.SliceOrientationNo2[3] = '>'; \ strncpy(&(Header->G51.Txt.SliceOrientationNo2[4]), (No2Label), (int) 3); \ } /*-----------------------------------------------------------------------------------------*/ /* String Initialization */ #define DS_SET_STRING_DEFAULT(String, DefaultSign, StringLength) \ memset((String), (DefaultSign), (int) (StringLength)); \ String[(StringLength)] = '\0' #define DS_SET_STRING_UNDEFINED(String, FillSign, StringLength) \ memset((String), (FillSign), (int) (StringLength)); \ String[(StringLength)] = '\0'; \ String[0] = String_UNDEFINED #define DS_SET_STRING_VALUE(String, FillString, StringLength) \ bcopy((FillString), (String), (int) (StringLength)); \ String[(StringLength)] = '\0' #define DS_SET_STRING_CHANGEABLE(String) \ ds_string_delete_leading_characters(String, String, ' '); \ ds_string_delete_tailing_characters(String, String, ' ') /*-----------------------------------------------------------------------------------------*/ /*== FUNCTIONS ==========================================================================*/ /* NOTE: define system library functions */ /* The following system library functions are defined here to avoid an error message during lint task. */ /* DECLARATION: define special parameter */ /*char *sprintf();*/ /*========================================================================================*/ #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_functions.h0000644000175000000620000004230212574624760027714 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_functions.h Description: The header file defines all functions declared by Data Set Library. To generate an actual function list delete the current list and copy the output of following command as statements in paragraph 'Sequence: define functions'. % grep "Name:" ds_*.c | grep "()" | awk '{print " long " $3 ";"}' | sort To generate an actual modul list use the command: % ls -l ds_*.c | awk '{print " " $8}' To generate an actual header file list use the command: % ls -l ds_*.h | awk '{print " " $8}' Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ /* NOTE: list of data set library source code moduls */ /* The first 15 characters must be unique. The unix "ar" utility works only with the first 15 characters. */ /* ds_456789012345----0----5----0 ds_adapt.c ds_build_acr.c ds_build_area.c ds_build_s1.c ds_build_s2.c ds_build_up.c ds_collect.c ds_convert.c ds_date.c ds_db.c ds_fill_acr.c ds_fill_area.c ds_fill_s1.c ds_fill_s2.c ds_fill_sha.c ds_format.c ds_get1.c ds_get2.c ds_head.c ds_info.c ds_interface.c ds_mani.c ds_mixed.c ds_num1.c ds_num1_xyz.c ds_num2.c ds_num2_xyz.c ds_parser.c ds_post.c ds_pre.c ds_separate.c ds_set2.c ds_set_s.c ds_som0_b1.c ds_som0_b2.c ds_som0_b3.c ds_som0_b4.c ds_som0_b5.c ds_som0_s.c ds_som1.c ds_split.c ds_string.c ds_text.c ds_top.c ds_transform.c ds_vector.c ds_xyz.c ds_456789012345----0----5----0 */ /* NOTE: list of data set library header files */ /* ds_date.h ds_functions.h ds_head_acr_groups_types.h ds_head_basic_types.h ds_head_constants.h ds_head_image_text_type.h ds_head_numaris1.h ds_head_numaris2.h ds_head_shadow_groups_types.h ds_head_somaris0.h ds_head_somaris1.h ds_head_type.h ds_include_files.h ds_mani.h ds_messages.h ds_transformation.h ds_transformation_control.h */ #ifndef DS_FUNCTIONS #define DS_FUNCTIONS /* DECLARATION: define data set library functions */ /* DECLARATION: define integer data set library functions */ long ds_adapt_double(); long ds_adapt_int(); long ds_adapt_string(); long ds_build_up_char_buf(); long ds_build_up_cms_nema_data_set(); long ds_build_up_data_groups(); long ds_build_up_graphic_groups(); long ds_build_up_group0008(); long ds_build_up_group0009(); long ds_build_up_group0010(); long ds_build_up_group0011(); long ds_build_up_group0013(); long ds_build_up_group0018(); long ds_build_up_group0019(); long ds_build_up_group0020(); long ds_build_up_group0021(); long ds_build_up_group0028(); long ds_build_up_group0029(); long ds_build_up_group0051(); long ds_build_up_group600x(); long ds_build_up_group6021(); long ds_build_up_group7FE0(); long ds_build_up_group7FE1(); long ds_build_up_header_groups(); long ds_build_up_header_groups(); long ds_build_up_image_text_item_value(); long ds_build_up_key(); long ds_build_up_long_buf(); long ds_build_up_short_buf(); long ds_build_up_som00008(); long ds_build_up_som00009(); long ds_build_up_som00010(); long ds_build_up_som00011(); long ds_build_up_som00018(); long ds_build_up_som00020(); long ds_build_up_som00021(); long ds_build_up_som00028(); long ds_build_up_som0600x(); long ds_build_up_som07001(); long ds_build_up_som07003(); long ds_build_up_som07005(); long ds_build_up_som0_blocked_data_set(); long ds_build_up_som0_framed_data_set(); long ds_collect_calculation_mode(); long ds_collect_cardiac_code(); long ds_collect_compression_code(); long ds_collect_contrast(); long ds_collect_data_object_subtype(); long ds_collect_data_object_type(); long ds_collect_data_set_subtype(); long ds_collect_data_set_type(); long ds_collect_date(); long ds_collect_filter_type(); long ds_collect_filter_type_image(); long ds_collect_gate_phase(); long ds_collect_ident(); long ds_collect_image_format(); long ds_collect_image_geometry_type(); long ds_collect_image_location(); long ds_collect_imaged_nucleus(); long ds_collect_integer_number(); long ds_collect_laterality(); long ds_collect_measurement_mode(); long ds_collect_modality(); long ds_collect_order_of_slices(); long ds_collect_patient_phase(); long ds_collect_patient_position(); long ds_collect_patient_region(); long ds_collect_pixel_quality_mode(); long ds_collect_procedure_description(); long ds_collect_real_number(); long ds_collect_rest_direction(); long ds_collect_rotation_direction(); long ds_collect_save_code(); long ds_collect_sex(); long ds_collect_storage_mode(); long ds_collect_study_type(); long ds_collect_time(); long ds_collect_uid(); long ds_collect_view_direction(); long ds_collect_window_style(); long ds_convert_double(); long ds_convert_int(); long ds_convert_string(); long ds_date_check_cms(); long ds_date_cms_to_display(); long ds_date_cms_to_ingres(); long ds_date_cms_to_nema(); long ds_date_cms_to_rt11(); long ds_date_cms_to_spi(); long ds_date_days_of_month(); long ds_date_days_of_year(); long ds_date_display_to_cms(); long ds_date_get(); long ds_date_get_age(); long ds_date_get_new(); long ds_date_ingres_to_cms(); long ds_date_nema_to_cms(); long ds_date_pdp_to_cms(); long ds_date_string_to_cms(); long ds_date_vax_to_cms(); long ds_date_vms_to_cms(); long ds_db_set_comdiainfo(); long ds_db_set_imainfo(); long ds_db_set_patinfo(); long ds_db_set_stuinfo(); long ds_db_use_comdiainfo(); long ds_db_use_patinfo(); long ds_file_control(); long ds_fill_basic_structs(); long ds_fill_binary_data(); long ds_fill_binary_g7FE0(); long ds_fill_binary_g7FE1(); long ds_fill_common_black_image_data(); long ds_fill_data_structs(); long ds_fill_default_data(); long ds_fill_fix_data(); long ds_fill_graphic_structs(); long ds_fill_image_g7FE0(); long ds_fill_image_graphic(); long ds_fill_image_text(); long ds_fill_patient_data(); long ds_fill_struct_g08(); long ds_fill_struct_g09(); long ds_fill_struct_g09Cms(); long ds_fill_struct_g09Lab(); long ds_fill_struct_g09Spi(); long ds_fill_struct_g10(); long ds_fill_struct_g11(); long ds_fill_struct_g11Cms(); long ds_fill_struct_g11Spi(); long ds_fill_struct_g13(); long ds_fill_struct_g13Cms(); long ds_fill_struct_g18(); long ds_fill_struct_g19(); long ds_fill_struct_g19Acqu(); long ds_fill_struct_g19Cms(); long ds_fill_struct_g19Ct(); long ds_fill_struct_g19CtCoAd(); long ds_fill_struct_g19Mr(); long ds_fill_struct_g19MrCoAd(); long ds_fill_struct_g20(); long ds_fill_struct_g21(); long ds_fill_struct_g21Cms(); long ds_fill_struct_g21Ct(); long ds_fill_struct_g21CtRaw(); long ds_fill_struct_g21Med(); long ds_fill_struct_g21Mr(); long ds_fill_struct_g21MrRaw(); long ds_fill_struct_g28(); long ds_fill_struct_g29(); long ds_fill_struct_g29Cms(); long ds_fill_struct_g29Spi(); long ds_fill_struct_g51(); long ds_fill_struct_g6021(); long ds_fill_test_data(); long ds_finally_interface_status_update(); long ds_generate_image_text(); long ds_get_data_set_owner(); long ds_get_data_set_type(); long ds_get_g09_med_parameter(); long ds_get_subgroup_owner_code(); long ds_get_swap_mode(); long ds_get_vax_char_buf(); long ds_get_vax_integer2_buf(); long ds_get_vax_integer4_buf(); long ds_get_vax_real_buf(); long ds_get_vax_skip_gap(); long ds_hea_slice_1_2_single(); long ds_info_get_xxx_version(); long ds_info_show_versions(); long ds_init_error_handling(); long ds_init_header(); long ds_initialize_nema_parser(); long ds_mani_gen_rot_matrix(); long ds_mani_image_magnify(); long ds_mani_image_mirror(); long ds_mani_image_rotate_90(); long ds_mani_mult_matrix_matrix(); long ds_mani_mult_matrix_scalar(); long ds_mani_patient_modify(); long ds_mani_set_id_matrix(); long ds_mani_transform_vector(); long ds_mes_calc_prs(); long ds_modify_patient_data(); long ds_nema_check_if_transformation_possible(); long ds_num1_blocked_fill_basic_structs(); long ds_num1_blocked_fill_data_structs(); long ds_num1_framed_fill_basic_structs(); long ds_num1_framed_fill_data_structs(); long ds_num1_get_cms_sep_basic_data(); long ds_num1_get_pixel_quality_code(); long ds_num1_get_pixel_quality_mode(); long ds_num1_num1_to_cms(); long ds_num1_vax_to_sun(); long ds_num2_fill_basic_structs(); long ds_num2_fill_data_structs(); long ds_num2_get_cms_sep_basic_data(); long ds_num2_get_pixel_quality_code(); long ds_num2_get_pixel_quality_mode(); long ds_num2_num2_to_cms(); long ds_num2_vax_to_sun(); long ds_parser_error_handling(); long ds_post_build_up_name(); long ds_post_build_up_record(); long ds_post_separate(); long ds_pre_allocate_data_areas_b1(); long ds_pre_allocate_data_areas_b2(); long ds_pre_allocate_data_areas_s(); long ds_pre_build_up_name(); long ds_pre_build_up_record(); long ds_pre_determine_control_status_b1(); long ds_pre_determine_control_status_b2(); long ds_pre_determine_control_status_s(); long ds_pre_determine_data_area_lengths_b1(); long ds_pre_determine_data_area_lengths_b2(); long ds_pre_determine_data_area_lengths_s(); long ds_pre_initialize(); long ds_pre_separation(); long ds_reset_nema_parser(); long ds_return_control(); long ds_separate_byte_buf(); long ds_separate_char_buf(); long ds_separate_cms_blocked_data_set(); long ds_separate_common_data_set(); long ds_separate_given_item(); long ds_separate_group_600x(); long ds_separate_key(); long ds_separate_long_buf(); long ds_separate_nema_data_set(); long ds_separate_num1_blocked_data_set(); long ds_separate_num1_framed_data_set(); long ds_separate_num2_data_set(); long ds_separate_short_buf(); long ds_separate_som0_blocked_data_set(); long ds_separate_som0_framed_data_set(); long ds_separate_som_blocked_data_set(); long ds_separate_som_framed_data_set(); long ds_set_pdp_byte_buf(); long ds_set_pdp_char_buf(); long ds_set_pdp_fill_gap(); long ds_set_pdp_integer2_buf(); long ds_set_pdp_integer4_buf(); long ds_set_struct_g09Cms(); long ds_set_struct_g09Lab(); long ds_set_struct_g09Spi(); long ds_set_struct_g11Cms(); long ds_set_struct_g11Spi(); long ds_set_struct_g19Acqu(); long ds_set_struct_g19Cms(); long ds_set_struct_g19Ct(); long ds_set_struct_g19CtCoAd(); long ds_set_struct_g19Mr(); long ds_set_struct_g19MrCoAd(); long ds_set_struct_g21Cms(); long ds_set_struct_g21Ct(); long ds_set_struct_g21CtRaw(); long ds_set_struct_g21Med(); long ds_set_struct_g21Mr(); long ds_set_struct_g21MrRaw(); long ds_set_struct_g29Cms(); long ds_set_struct_g29Spi(); long ds_show_help_text(); long ds_simulate_ct_mr_status_file(); long ds_som0_blocked_build_up_basic_structs(); long ds_som0_blocked_build_up_data_structs(); long ds_som0_blocked_fill_basic_structs(); long ds_som0_blocked_fill_data_structs(); long ds_som0_check_if_transformation_possible(); long ds_som0_cms_to_som0(); long ds_som0_fill_bild_text(); long ds_som0_fill_block_0(); long ds_som0_framed_build_up_basic_groups(); long ds_som0_framed_build_up_data_groups(); long ds_som0_framed_fill_basic_structs(); long ds_som0_framed_fill_data_structs(); long ds_som0_get_cms_sep_basic_data(); long ds_som0_get_patient_orientation_vector(); long ds_som0_pdp_to_sun(); long ds_som0_print_build_up_protocol(); long ds_som0_som0_to_cms(); long ds_som0_sun_to_pdp(); long ds_som_blocked_fill_basic_structs(); long ds_som_blocked_fill_data_structs(); long ds_som_framed_fill_basic_structs(); long ds_som_framed_fill_data_structs(); long ds_som_get_cms_sep_basic_data(); long ds_som_get_patient_position(); long ds_som_get_rest_direction(); long ds_som_som_to_cms(); long ds_som_vax_to_sun(); long ds_split_calculation_mode(); long ds_split_cardiac_code(); long ds_split_compression_code(); long ds_split_contrast(); long ds_split_data_object_subtype(); long ds_split_data_set_subtype(); long ds_split_filter_type(); long ds_split_filter_type_image(); long ds_split_gate_phase(); long ds_split_ident(); long ds_split_image_format(); long ds_split_image_geometry_type(); long ds_split_imaged_nucleus(); long ds_split_integer_number(); long ds_split_laterality(); long ds_split_measurement_mode(); long ds_split_modality(); long ds_split_order_of_slices(); long ds_split_patient_phase(); long ds_split_patient_position(); long ds_split_patient_region(); long ds_split_pixel_quality_mode(); long ds_split_procedure_description(); long ds_split_real_number(); long ds_split_rest_direction(); long ds_split_rotation_direction(); long ds_split_save_code(); long ds_split_sex(); long ds_split_storage_mode(); long ds_split_study_type(); long ds_split_view_direction(); long ds_split_window_style(); long ds_stop_dbx(); long ds_string_delete_leading_characters(); long ds_string_delete_multiple_characters(); long ds_string_delete_tailing_characters(); long ds_string_find_string_in_string(); long ds_string_reformate(); long ds_string_replace(); long ds_text_comment_no_1(); long ds_text_comment_no_2(); long ds_text_contrast(); long ds_text_data_set_id(); long ds_text_date_of_measurement(); long ds_text_echo_time(); long ds_text_field_of_view(); long ds_text_gantry_tilt(); long ds_text_gating_and_trigger(); long ds_text_get_black_image_text(); long ds_text_get_none_image_text(); long ds_text_get_normal_image_text(); long ds_text_get_recon_3d_image_text(); long ds_text_image_number(); long ds_text_installation_name(); long ds_text_label(); long ds_text_magnification_factor(); long ds_text_manufacturer_model(); long ds_text_matrix(); long ds_text_mip_column(); long ds_text_mip_head_line(); long ds_text_mip_line(); long ds_text_mip_slice(); long ds_text_number_of_acquisitions(); long ds_text_patient_birthdate(); long ds_text_patient_name(); long ds_text_patient_number(); long ds_text_patient_orientation_set_1(); long ds_text_patient_orientation_set_2(); long ds_text_patient_orientation_sets(); long ds_text_patient_orientation_string(); long ds_text_patient_position(); long ds_text_patient_sex_and_age(); long ds_text_repetition_time(); long ds_text_saturation_regions(); long ds_text_scan_number(); long ds_text_sequence_information(); long ds_text_slice_orientation_no(); long ds_text_slice_position(); long ds_text_slice_thickness(); long ds_text_software_version(); long ds_text_study_number(); long ds_text_table_position(); long ds_text_time_of_acquisition(); long ds_text_time_of_measurement(); long ds_text_time_of_scanning(); long ds_text_tube_current(); long ds_text_tube_voltage(); long ds_text_type_of_measurement(); long ds_text_zoom_center(); long ds_time_check_cms(); long ds_time_cms_to_display(); long ds_time_cms_to_nema(); long ds_time_cms_to_pdp(); long ds_time_cms_to_spi(); long ds_time_display_to_cms(); long ds_time_nema_to_cms(); long ds_time_pdp_to_cms(); long ds_time_string_to_cms(); long ds_top_up_header(); long ds_trace_control(); long ds_transform_cms_to_blocked_som0(); long ds_transform_cms_to_cms_nema(); long ds_transform_cms_to_framed_som0(); long ds_transform_common_to_cms(); long ds_update_nema_parser(); long ds_vector_add(); long ds_vector_assign(); long ds_vector_check_if_equal(); long ds_vector_check_if_nil(); long ds_vector_check_if_normalized(); long ds_vector_check_if_undefined(); long ds_vector_cut(); long ds_vector_get(); long ds_vector_inner_product(); long ds_vector_multiply(); long ds_vector_orthogonal_to_polar(); long ds_vector_polar_to_orthogonal(); long ds_vector_print(); long ds_vector_put(); long ds_vector_set(); long ds_vector_subtract(); long ds_vector_transform_l(); long ds_vector_transform_m(); long ds_vector_vector_product(); long ds_xyz_get_cms_black_basic_data(); long ds_xyz_get_cms_derived_data(); long ds_xyz_get_cms_field_of_view(); long ds_xyz_get_cms_sep_basic_data(); long ds_xyz_get_image_class(); long ds_xyz_get_nema_image_place(); long ds_xyz_get_slice_orientation_parameters(); long ds_xyz_get_transformation_list1(); long ds_xyz_get_transformation_list2(); long ds_xyz_get_view_direction(); long ds_xyz_shift_definition_point(); /* DECLARATION: define special data set library functions */ data_area_type_t ds_get_data_area_type(); item_quality_t ds_get_item_quality(); image_text_type_t ds_get_image_text_type(); double ds_get_max_abs_vector_component(); char *ds_info_get_header_version(); char *ds_info_get_nema_version(); char *ds_info_get_software_version(); char *ds_info_get_spi_version(); char *ds_strstr(); long unsigned ds_cal_image_base_vectors(); long unsigned ds_cal_vec_mode(); long unsigned ds_mpr_mult_matmat3(); #define ds_string_find_string_in_string ds_strstr /* DECLARATION: define special system functions */ char *malloc(); #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_head_acr_groups_types.h0000644000175000000620000002751112574624760032262 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_head_acr_groups_types.h Description: The header file defines the NEMA defined data set basic groups (even group numbers) as structures for internal use (internal header). Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_HEAD_ACR_GROUPS_TYPES #define DS_HEAD_ACR_GROUPS_TYPES /******************************************/ /* Identifying Information (Group 0008'H) */ /******************************************/ typedef struct acr_identifying_tag { ds_date_t StudyDate; /* (0008,0020) 10 AT DF 2NS-NEM */ ds_date_t AcquisitionDate; /* (0008,0022) 10 AT DF 2NS-CMS */ ds_date_t ImageDate; /* (0008,0023) 10 AT DF 2NS-CMS */ ds_time_t StudyTime; /* (0008,0030) 12 AT DF 2NS-NEM */ ds_time_t AcquisitionTime; /* (0008,0032) 12 AT DF 2NS-CMS */ ds_time_t ImageTime; /* (0008,0033) 12 AT DF 2NS-CMS */ data_set_subtype_t DataSetSubtype; /* (0008,0041) 8 AT EV 2NS-CMS */ modality_t Modality; /* (0008,0060) 2 AT EV 1NS-CMS */ char Manufacturer[LENGTH_MANUFACTURER + 1]; /* (0008,0070) 8 AT FF 2NS-NEM */ char InstitutionID[LENGTH_LABEL + 1]; /* (0008,0080) 26 AT FF 2NS-NEM */ char ReferringPhysician[LENGTH_LABEL + 1]; /* (0008,0090) 26 AT FF 2NS-NEM */ char StationID[LENGTH_LABEL + 1]; /* (0008,1010) 26 AT FF 2NS-NEM */ char ProcedureDescription_1[LENGTH_COMMENT + 1]; /* (0008,1030) 52 AT FF 2DS-CMS */ char ProcedureDescription_2[LENGTH_COMMENT + 1]; char AdmittingDiagnosis[LENGTH_DIAGNOSIS + 1]; /* (0008,1080) 40 AT FF 2DS-CMS */ char ManufacturerModel[LENGTH_LABEL + 1]; /* (0008,1090) 26 AT FF 2DS-CMS */ } acr_identifying_t; /**************************************/ /* Patient Information (Group 0010'H) */ /**************************************/ typedef struct acr_patient_tag { char PatientName[LENGTH_LABEL + 1]; /* (0010,0010) 26 AT FF 2NS-NEM */ char PatientId[LENGTH_PATIENT_ID + 1]; /* (0010,0020) 12 AT FF 2NS-NEM */ ds_date_t PatientBirthdate; /* (0010,0030) 10 AT DF 2NS-NEM */ sex_t PatientSex; /* (0010,0040) 2 AT EV 2NS-NEM */ char PatientMaidenName[LENGTH_LABEL + 1]; /* (0010,1005) 26 AT FF 2DS-CMS */ char PatientAge[LENGTH_AGE + 1]; /* (0010,1010) 4 AT DF 2NS-CMS */ double PatientSize; /* (0010,1020) 6 AN FF 3NS-NEM */ long PatientWeight; /* (0010,1030) 6 AN FF 2NS-CMS */ } acr_patient_t; /******************************************/ /* Acquisition Information (Group 0018'H) */ /******************************************/ typedef struct acr_acquisition_tag { contrast_t Contrast; /* (0018,0010) 8 AT EV 2DS-NEM */ int Pad1; /* Dummy for byte alignment */ double SliceThickness; /* (0018,0050) 14 AN FF 2NS-NEM */ long GeneratorVoltage; /* (0018,0060) 6 AN FF 2NM-NEM */ long GeneratorVoltageDual; /* (0018,0060) */ double RepetitionTime; /* (0018,0080) 14 AN FF 2NS-NEM */ double EchoTime; /* (0018,0081) 14 AN FF 2NS-NEM */ double InversionTime; /* (0018,0082) 14 AN FF 2NS-NEM */ long NumberOfAverages; /* (0018,0083) 6 AN FF 3NS-NEM */ int Pad2; /* Dummy for byte alignment */ double ImagingFrequency; /* (0018,0084) 14 AN FF 2NS-NEM */ nucleus_t Gap0085; long EchoNumber; /* (0018,0086) 6 AN FF 3NS-CMS */ long DataCollectionDiameter; /* (0018,0090) 6 AN FF 3NS-NEM */ char DeviceSerialNumber[LENGTH_LABEL + 1]; /* (0018,1000) 26 AT FF 2DS-CMS */ char SoftwareVersion[LENGTH_SOFTWARE_VERSION + 1]; /* (0018,1020) 8 AT FF 2DS-CMS */ long DistanceSourceToDetector; /* (0018,1110) 6 AN FF 3NS-NEM */ long DistanceSourceToPatient; /* (0018,1111) 6 AN FF 3NS-NEM */ long GantryTilt; /* (0018,1120) 6 AN FF 2NS-CMS */ long TableHeight; /* (0018,1130) 6 AN FF 3NS-NEM */ rotation_direction_t RotationDirection; /* (0018,1140) 2 AT EV 3NS-NEM */ long ExposureTime; /* (0018,1150) 6 AN FF 2NS-CMS */ long Exposure; /* (0018,1152) 6 AN FF 2NS-CMS */ char FilterIdLabel[LENGTH_FILTER_ID + 1]; /* (0018,1160) 12 AT FF 3NS-NEM */ int Pad3; /* Dummy for byte alignment */ double GeneratorPower; /* (0018,1170) 14 AN FF 2NS-CMS */ double FocalSpot; /* (0018,1190 14 AN FF 3NS-NEM) */ ds_date_t CalibrationDate; /* (0018,1200) 10 AT DF 3NS-NEM */ ds_time_t CalibrationTime; /* (0018,1201) 12 AT DF 3NS-NEM */ char ConvolutionKernel[LENGTH_LABEL + 1]; /* (0018,1210) 12 AT DF 3NS-NEM */ char ReceivingCoil[LENGTH_LABEL + 1]; /* (0018,1250) 26 AT FF 2DS-CMS */ char Gap1251[LENGTH_LABEL + 1]; patient_position_t PatientPosition; /* (0018,5100) 8 AT EV 2NS-CMS */ char ImagedNucleus[LENGTH_NUCLEUS + 1]; /* (0018,0085) 8 AT FF 2NS-NEM */ } acr_acquisition_t; /*******************************************/ /* Relationship Information (Group 0020'H) */ /*******************************************/ typedef struct acr_relationship_tag { long Study; /* (0020,0010) 6 AN FF 2NS-NEM */ long Gap0011; long Acquisition; /* (0020,0012) 6 AN FF 2DS-NEM */ long Image; /* (0020,0013) 6 AN FF 2DS-NEM */ long Gap0030[3]; int Pad1; /* Dummy for byte alignment */ double Gap0035[6]; long Location; /* (0020,0050) 6 AN FF 3NS-NEM */ laterality_t Laterality; /* (0020,0060) 2 AT EV 2DS-NEM */ geometry_t ImageGeometryType; /* (0020,0070) 8 AT EV 2DS-NEM */ long AcquisitionsInSeries; /* (0020,1001) 6 AN FF 3NS-NEM */ reference_t Reference; /* (0020,1020) n AT FF 3NM-NEM */ } acr_relationship_t; /*************************************************/ /* Image Presentation Information (Group 0028'H) */ /*************************************************/ typedef struct acr_presentation_tag { short ImageDimension; /* (0028,0005) 2 BI HX 1DS-NEM */ short Rows; /* (0028,0010) 2 BI HX 1NS-NEM */ short Columns; /* (0028,0011) 2 BI HX 1NS-NEM */ pixel_size_t PixelSize; /* (0028,0030) 30 AN FF 2NM-NEM */ image_format_t ImageFormat; /* (0028,0040) 4 AT EV 1DS-NEM */ compression_code_t CompressionCode; /* (0028,0060) 4 AT EV 1DS-NEM */ short BitsAllocated; /* (0028,0100) 2 BI HX 1DS-NEM */ short BitsStored; /* (0028,0101) 2 BI HX 1DS-NEM */ short HighBit; /* (0028,0102) 2 BI HX 1DS-NEM */ short PixelRepresentation; /* (0028,0103) 2 BI HX 1DS-NEM */ windows_t WindowCenter; /* (0028,1050) 12 AN DF 2NM-MED */ windows_t WindowWidth; /* (0028,1051) 12 AN DF 2NM-MED */ long RescaleIntercept; /* (0028,1052) 6 AN FF 2NS-MED */ long RescaleSlope; /* (0028,1053) 6 AN FF 2NS-MED */ } acr_presentation_t; #endif /*== HELP TEXT ===========================================================================*/ #ifdef DS_STC_TOOL "G08.Ide.StudyDate.Year", "Study Date (0008,0020)", "G08.Ide.AcquisitionDate.Year", "Acquisition Date (0008,0022)", "G08.Ide.ImageDate.Year", "Image Date (0008,0023)", "G08.Ide.StudyTime.Hour", "Study Time (0008,0030)", "G08.Ide.AcquisitionTime.Hour", "Acquisition Time (0008,0032)", "G08.Ide.ImageTime.Hour", "Image Time (0008,0033)", "G08.Ide.DataSetSubtype.M", "Data Set Subtype (0008,0041)", "G08.Ide.Modality", "Modality (0008,0060)", "G08.Ide.Manufacturer", "Manufacturer (0008,0070)", "G08.Ide.InstitutionID", "Institution ID (0008,0080)", "G08.Ide.ReferringPhysician", "Referring Physician (0008,0090)", "G08.Ide.StationID", "Station ID (0008,1010)", "G08.Ide.ProcedureDescription_1", "Procedure Description (0008,1030) - first part", "G08.Ide.ProcedureDescription_2", "Procedure Description (0008,1030) - second part", "G08.Ide.AdmittingDiagnosis", "Admitting Diagnosis (0008,1080)", "G08.Ide.ManufacturerModel", "Manufacturer Model (0008,1090)", "G10.Pat.PatientName", "Patient Name (0010,0010)", "G10.Pat.PatientId", "Patient Id (0010,0020)", "G10.Pat.PatientBirthdate.Year", "Patient Birthdate (0010,0030)", "G10.Pat.PatientSex", "Patient Sex (0010,0040): Female | Male | Others", "G10.Pat.PatientMaidenName", "Patient Maiden Name (0010,1005)", "G10.Pat.PatientAge", "Patient Age (0010,1010) in Years | Months | Days", "G10.Pat.PatientSize", "Patient Size (0010,1020) in meters", "G10.Pat.PatientWeight", "Patient Weight (0010,1030) in kilograms", "G18.Acq.Contrast", "Contrast (0018,0010): APPLIED | NONE", "G18.Acq.SliceThickness", "Slice Thickness (0018,0050) in mm", "G18.Acq.GeneratorVoltage", "nominal Generator Voltage (0018,0060) in kV", "G18.Acq.GeneratorVoltageDual", "second value of Generator Voltage (0018,0060) in kV", "G18.Acq.RepetitionTime", "Repetition Time (0018,0080) in msec", "G18.Acq.EchoTime", "Echo Time (0018,0081) in msec", "G18.Acq.InversionTime", "Inversion Time (0018,0082) in msec", "G18.Acq.NumberOfAverages", "nominal Number of Averages (0018,0083)", "G18.Acq.ImagingFrequency", "Imaging Frequency (0018,0084) in MHz", "G18.Acq.ImagedNucleus", "Imaged Nucleus (0018,0085)", "G18.Acq.EchoNumber", "Echo Number (0018,0086)", "G18.Acq.DataCollectionDiameter", "Data Collection Diameter (0018,0090) in mm", "G18.Acq.DeviceSerialNumber", "Device Serial Number (0018,1000)", "G18.Acq.SoftwareVersion", "Software Version (0018,1020)", "G18.Acq.DistanceSourceToDetector", "Distance Source to Detector (0018,1110)in mm", "G18.Acq.DistanceSourceToPatient", "Distance Source to Patient (0018,1111) in mm", "G18.Acq.GantryTilt", "Gantry Tilt (0018,1120) in degrees", "G18.Acq.TableHeight", "Table Height (0018,1130) in mm", "G18.Acq.RotationDirection", "Rotation Direction (0018,1140): Counter Clock | Clock Wise | NO", "G18.Acq.ExposureTime", "nominal Exposure Time (0018,1150) in msec", "G18.Acq.Exposure", "nominal Exposure (0018,1152) in mAs", "G18.Acq.FilterIdLabel", "Filter Id Label (0018,1160)", "G18.Acq.GeneratorPower", "nominal Generator Power (0018,1170) in kW", "G18.Acq.FocalSpot", "Focal Spot (0018,1190) in mm", "G18.Acq.CalibrationDate.Year", "Calibration Date (0018,1200)", "G18.Acq.CalibrationTime.Hour", "Calibration Time (0018,1201)", "G18.Acq.ConvolutionKernel", "Convolution Kernel (0018,1210)", "G18.Acq.ReceivingCoil", "Receiving Coil (0018,1250)", "G18.Acq.PatientPosition", "Patient Position (0018,5100)", "G20.Rel.Study", "Study (0020,0010)", "G20.Rel.Acquisition", "Acquisition (0020,0012)", "G20.Rel.Image", "Image (0020,0013)", "G20.Rel.Location", "Location (0020,0050) in mm", "G20.Rel.Laterality", "Laterality (0020,0060)", "G20.Rel.ImageGeometryType", "Image Geometry Type (0020,0070): PLANAR | UNRAVEL | CURVED", "G20.Rel.AcquisitionsInSeries", "Acquisitions in Series (0020,1001)", "G20.Rel.Reference.One.Image", "Reference (0020,1020)", "G28.Pre.ImageDimension", "Image Dimension (0028,0005)", "G28.Pre.Rows", "Rows (0028,0010)", "G28.Pre.Columns", "Columns (0028,0011)", "G28.Pre.PixelSize.Row", "Pixel Size (0028,0030) in mm", "G28.Pre.ImageFormat", "Image Format (0028,0040)", "G28.Pre.CompressionCode", "Compression Code (0028,0060)", "G28.Pre.BitsAllocated", "Bits Allocated (0028,0100)", "G28.Pre.BitsStored", "Bits Stored (0028,0101)", "G28.Pre.HighBit", "High Bit (0028,0102)", "G28.Pre.PixelRepresentation", "Pixel Representation (0028,0103)", "G28.Pre.WindowCenter.X", "Window Center (0028,1050)", "G28.Pre.WindowWidth.X", "Window Width (0028,1051)", "G28.Pre.RescaleIntercept", "Rescale Intercept (0028,1052)", "G28.Pre.RescaleSlope", "Rescale Slope (0028,1053)", #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_date.h0000644000175000000620000001026612574624760026625 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_date.h Description: The header file defines the basic types, constants and macros for the data set library part "ds_date": date and time converting handling. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_DATE #define DS_DATE /* DECLARATION: types */ typedef struct ds_date_tag { long Year; /* four digits e.g. 1989 */ long Month; /* 1 - 12 */ long Day; /* 1 - 31 */ } ds_date_t; typedef struct ds_time_tag { long Hour; /* 0 - 23 */ long Minute; /* 0 - 59 */ long Second; /* 0 - 59 */ long Fraction; /* 0 - 999 */ } ds_time_t; typedef struct date_position_table_tag { long PreBegin; /* number of first character of date string part pre-number; always 0 */ long PreDelimiter; /* number of pre-delimiter character */ long MonthBegin; /* number of first character of date string part month */ long PostDelimiter; /* number of post-delimiter character */ long PostBegin; /* number of first character of date string part post-number */ } date_position_table_t; typedef struct time_position_table_tag { long HourBegin; /* number of first character of time string part hour; always 0 */ long HourDelimiter; /* number of hour delimiter character */ long MinuteBegin; /* number of first character of time string part minute */ long MinuteDelimiter; /* number of minute delimiter character */ long SecondBegin; /* number of first character of time string part second */ long SecondDelimiter; /* number of second delimiter character */ long FractionBegin; /* number of first character of time string part fraction */ } time_position_table_t; typedef union ds_pdp_time_tag { short ValueAsShort[2]; long ValueAsLong; } ds_pdp_time_t; /* PRECOMPILER: common constants */ #define CENTURY 1900L #define DEFAULT 0L #define MAX_ALLOWED_DATE_STRING_LENGTH 20L #define MAX_ALLOWED_TIME_STRING_LENGTH 15L #define WITH_FRACTION 2L #define WITH_SECOND 1L /* PRECOMPILER: range constants */ #define DS_A_DAY_IN_MS (24.0 * 60.0 * 60.0 * 1000.0) #define DS_A_HOUR_IN_MS (60 * 60 * 1000) #define DS_A_MINUTE_IN_MS (60 * 1000) #define DS_A_SECOND_IN_MS (1000) #define DS_A_HOUR_IN_S (60 * 60) #define DS_A_MINUTE_IN_S (60) /* PRECOMPILER: delimiter constants */ #define COMMON_DATE_DELIMITER_SET ".-/:" #define DB_DATE_DELIMITER_SET "-" #define NEMA_DATE_DELIMITER_SET "." #define VMS_DATE_DELIMITER_SET "-" #define COMMON_TIME_DELIMITER_SET ":.-/" #define DB_TIME_DELIMITER_SET ":" #define NEMA_TIME_DELIMITER_SET ":." #define VMS_TIME_DELIMITER_SET ":." #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_include/ds_transformation_control.h0000644000175000000620000007027212574624760032521 0ustar stevestaff/*[- HEADER FILE -------------------------------------------------------------------------*/ /* Name: ds_transformation_control.h Description: The header file defines the types, constants and macros necessary to control a data set transformation. NOTE The discussion about graphic representation has not finished in the moment (1989-DEC). A final declaration is possible after publishing the graphic discussion results. Author: THUMSER, Andreas (TH); Siemens AG UBMed CMS/SCE64; phone: 09131 844797 */ /*]-----------------------------------------------------------------------------------------*/ #ifndef DS_TRANSFORMATION_CONTROL #define DS_TRANSFORMATION_CONTROL /*== "Interpace Pointer List" ============================================================*/ /*[ Description: Entity "Interpace Pointer List" The most important part to control the transformation is the Interface Pointer List. To use the data set library build up function "ds_build_up_ cms_nema_data_set() to export an Internal Data Set or the data set library separation function "ds_separate_common_data_set() to import a NEMA Data Set this structure must be filled with data to control the transformation. This structure is the link between the different data areas in the host memory. The following C-code fragment shows an example to fill an Interface Pointer List before a NEMA Data Set is build up and separate respectively. The example areas are named 'MyHead', 'MyGraphic', 'MyData' for Internal Data Set and 'NemaSet' for the NEMA Data Set. The Interface Pointer List 'MyInterface' is used. Please note such areas must be available and filled with valid data before a data set transformation can be done. It is the user's responsibility to ensure that enough storage is available for the different areas. To avoid nested comments in the source file the C++ comment sign "//" is used. // SEQUENCE: build up CMS defined NEMA Data Set ... // update interface pointer list MyInterface.In.HeadAddress = &MyHead; // pointer to head area MyInterface.In.HeadLength = sizeof(header_t); // length of head area MyInterface.In.GraphicAddress = MyGraphic_p; // pointer to graphic area // or DS_VOID_NIL MyInterface.In.GraphicLength = MyGraphicLength; // length of graphic area // or Integer_UNDEFINED // or 0 MyInterface.In.DataAddress = (void *)MyData_p; // pointer to data area MyInterface.In.DataLength = MyDataLength; // length of data area 1) MyInterface.Ex.HeadAddress = MyNema_p; // pointer to NEMA area MyInterface.Ex.HeadLength = Integer_UNDEFINED; // filled during build up MyInterface.Ex.GraphicAddress = DS_VOID_NIL; // filled during build up MyInterface.Ex.GraphicLength = Integer_UNDEFINED; // filled during build up MyInterface.Ex.DataAddress = DS_VOID_NIL; // filled during build up MyInterface.Ex.DataLength = Integer_UNDEFINED; // filled during build up MyInterface.Ex.DataGroup = (short) Integer_UNDEFINED; // filled during build up MyInterface.CntStatus.Owner = DS_owner_UNDEFINED; // initialization MyInterface.CntStatus.Modality = ; // p r e s e t t i n g !! MyInterface.CntStatus.Code = DS_code_UNDEFINED; // initialization MyInterface.CntStatus.StatusFileAddress = MyFileAddress; // pointer to mapped status file MyInterface.CntStatus.StopItem.GroupNumber = 0; // initialization MyInterface.CntStatus.StopItem.ElementNumber = 0; // initialization ... 1) The pure data length in bytes is also stored into item Number of Data Bytes (0019'H,1060'H). // SEQUENCE: separate common Data Set ... // update interface pointer list MyInterface.In.HeadAddress = &MyHead; // pointer to head area MyInterface.In.HeadLength = sizeof(header_t); // length of head area MyInterface.In.GraphicAddress = MyGraphic_p; // pointer to graphic area MyInterface.In.GraphicLength = Integer_UNDEFINED; // filled during separation MyInterface.In.DataAddress = (void *)MyData_p; // pointer to data area MyInterface.In.DataLength = Integer_UNDEFINED; // filled during separation MyInterface.Ex.HeadAddress = MyNema_p; // pointer to NEMA area MyInterface.Ex.HeadLength = Integer_UNDEFINED; // filled during separation MyInterface.Ex.GraphicAddress = DS_VOID_NIL; // filled during separation MyInterface.Ex.GraphicLength = Integer_UNDEFINED; // filled during separation MyInterface.Ex.DataAddress = DS_VOID_NIL; // filled during separation MyInterface.Ex.DataLength = Integer_UNDEFINED; // filled during separation MyInterface.Ex.DataGroup = (short) Integer_UNDEFINED; // filled during separation MyInterface.CntStatus.Owner = DS_owner_UNDEFINED; // initialization or presetting MyInterface.CntStatus.Modality = Modality_UNDEFINED; // initialization or presetting MyInterface.CntStatus.Code = DS_code_UNDEFINED; // initialization MyInterface.CntStatus.StatusFileAddress = DS_STATUS_FILE_NIL; // not used during separation MyInterface.CntStatus.StopItem.GroupNumber = 0; // initialization MyInterface.CntStatus.StopItem.ElementNumber = 0; // initialization ... */ /* DECLARATION: declare the interpace pointer list types */ typedef enum transformation_status_tag { /* NOTE: enum transformation_status_tag */ /* The data set library defined transformation control status is supported by native language support (nls). This means it is possible to use this code for a detailed status messages after a transformation task. For example the following lines // SEQUENCE: transformate data set MyStatus = ds_separate_common_data_set(&MyInterface); // SEQUENCE: show transformation task status printf("\ninfo: separate data set: (%s)", nls_message((int) MyStatus)); printf("\n (%s)", nls_message((int) MyInterface.CntStatus.Code)); print after transformatin task with error info: separate data set: ret: (DS_RET_WITH_ERROR an error occur during transformation task) (DS_CODE_SET_OUT_OF_ORDER data set out of order) */ DS_code_DATA_SET_OUT_OF_ORDER = DS_CODE_SET_OUT_OF_ORDER, /* Error - groups or elements not in ascending order. Build up or separation was aborted. */ DS_code_END_OF_DATA_SET_FOUND = DS_CODE_END_OF_SET_FOUND, /* Error - End Of Data set was encountered. Build up or separation was aborted. */ DS_code_FATAL_ERROR = DS_CODE_FATAL_ERROR, /* Error - fatal error occured during data set transformation Build up or separation was aborted. */ DS_code_FIRST_ELEMENT_IN_GROUP_INVALID = DS_CODE_FIRST_ELEMENT_INVALID, /* Error - first element in group is not element Group Length (x'H,0000'H). Build up or separation was aborted. */ DS_code_INVALID_MODALITY = DS_CODE_INVALID_MODALITY, /* Error - desired modality is not supported. Build up or separation was aborted. */ DS_code_INVALID_SOM0_PARAMETERS = DS_CODE_INVALID_SOM0_PARAMETERS, /* Error - given data set can not transformed. Build up or separation was aborted. */ DS_code_INVALID_INTERFACE = DS_CODE_INVALID_INTERFACE, /* Error - the interface pointer list dosen't contain length information. Separation of internal data set was aborted. */ DS_code_NO_OWNER = DS_CODE_NO_OWNER, /* Error - no owner entries found in shadow groups. Build up or separation was aborted. */ DS_code_TRANSFORMATION_NOT_POSSIBLE = DS_CODE_TRANSFORM_NOT_POSSIBLE, /* Error - The transformation is not possible, because required dependencies are not fulfilled. Please see data set library function descriptions "ds_*_check_if_transformation_pos sible()" for more information. */ DS_code_UNKNOWN_COMPRESSION_CODE = DS_CODE_UNKNOWN_COMPRESSION, /* Error - compression code is unknown or not supported. Build up or separation was aborted. */ DS_code_UNKNOWN_DATA_SET = DS_CODE_UNKNOWN_DATA_SET, /* Error - data set format is unknown. Build up or separation was aborted. */ DS_code_UNKNOWN_HEADER = DS_CODE_UNKNOWN_HEADER, /* Error - header format is unknown. Build up or separation was aborted. */ DS_code_UNKNOWN_OVERLAY = DS_CODE_UNKNOWN_OVERLAY, /* Warning - a given overlay number is invalid. The build up task omitted the build up of this overlay group. */ DS_code_MISSING_TYP_1_VALUE = DS_CODE_MISSING_TYP_1, /* Warning - not all type 1 NEMA items could be build up, that means an invalid NEMA Data Set was build up or separated. */ DS_code_MULTIPLE_ERROR = DS_CODE_MULTIPLE_ERROR, /* Warning - several errors detected. This means more than one type 1 item values are missing or a type 1 value is missing and one or more other item values are not filled. An invalid NEMA Data Set was build up or separated. */ DS_code_MULTIPLE_FATAL_ERROR = DS_CODE_MULTIPLE_FATAL_ERROR, /* Error - several fatal errors detected. This means more than one error occures to abort the transformation. No external data set is filled. */ DS_code_NOT_ALL_FILLED = DS_CODE_NOT_ALL_FILLED, /* Warning - not all Internal Data Set parameters could be filled or not all required NEMA items could be built up. This parameters are set 'undefined'. That means an valid NEMA Data Set was build up, but it is possible that this data set contains to less information for special CMS internal use. */ DS_code_NORMAL = DS_CODE_NORMAL, /* successful transformation */ DS_code_UNDEFINED = DS_CODE_UNDEFINED /* initialization value */ } transformation_status_t; typedef enum data_set_owner_tag { DS_owner_CMS_NEMA = 1, /* The data set is NEMA defined with CMS expansions. The format is defined in [DS File Format] and [DS Item Format] */ DS_owner_NEMA = 2, /* The data set is NEMA defined. Possible available expansions are unknown and not analyzed. The format is defined in [NEMA Image], [NEMA Image88] and [NEMA Image89]. */ DS_owner_NUMARIS2_FRAMED = 3, /* The data set is Siemens defined with a NEMA frame. The format is defined in [MR Header]. */ DS_owner_SOMARIS1_FRAMED = 4, /* The data set is Siemens defined with a NEMA frame. The format is defined in [CT Header] and [CT Image]. */ DS_owner_SOMARIS1_BLOCKED = 5, /* The data set is Siemens defined as a binary data stream. The format is defined in [CT Header]. */ DS_owner_SOMARIS0_FRAMED = 6, /* The data set is Siemens defined with a NEMA frame. The format is defined in [CT DRH Header] and [CT Image]. */ DS_owner_SOMARIS0_BLOCKED = 7, /* The data set is Siemens defined as a binary data stream. The format is defined in [CT DRH Header]. */ DS_owner_CMS_BLOCKED = 8, /* The data set is CMS defined as a binary data stream. The format is defined in [DS File Format], [DS Item Format] and [DS Reference Manual]. */ DS_owner_NUMARIS1_FRAMED = 9, /* The data set is Siemens defined with a NEMA frame. The format is defined in [MR NUM1 Header]. */ DS_owner_NUMARIS1_BLOCKED = 10, /* The data set is Siemens defined with a binary data stream. The format is defined in [MR NUM1 Header]. */ DS_owner_UNKNOWN = -99, /* The data set format is unknown. No separation is possible */ DS_owner_UNDEFINED = Enum_UNDEFINED /* initialization */ } data_set_owner_t; typedef struct keyword_tag { short GroupNumber; /* item group number */ short ElementNumber; /* item element number */ } keyword_t; typedef struct control_area_ex_tag /* - NEMA Data Set - */ { char *HeadAddress; /* header area address (base address of NEMA Data Set byte stream, is also base address of item key of item Group Length (0008'H, 0000'H)) */ long HeadLength; /* header area length in byte */ char *GraphicAddress; /* graphic area address (base address of item key of item Group Length (6021'H, 0000'H)) */ long GraphicLength; /* graphic area length in byte */ char *DataAddress; /* data area address (base address of item key of item Group Length (, 0000'H)) */ long DataLength; /* data length in byte */ short DataGroup; /* NEMA data group */ } control_area_ex_t; typedef struct control_area_in_tag /* - Internal Data Set - */ { header_t *HeadAddress; /* header area address */ long HeadLength; /* header area length in byte */ char *GraphicAddress; /* graphic area address */ long GraphicLength; /* graphic area length in byte */ void *DataAddress; /* data area address */ long DataLength; /* data length in byte */ } control_area_in_t; typedef struct control_status_tag { data_set_owner_t Owner; /* Format of source data set. This parameter must be initialized by separation with the known owner or with 'DS_owner_UNDEFINED'. If 'DS_owner_UNDEFINED' is used, the data set library will determine the data set owner. If a preset owner not equal the real owner of data set the separation is aborted. During build up the owner is always set by data set library functions to 'DS_owner_CMS_NEMA'. */ modality_t Modality; /* Modality of source data set. This parameter must be initialized by separation with the required modality or with 'Modality_UNDEFINED'. If 'Modality_UNDEFINED' is used, the data set library will determine the data set modality during separation. If a preset owner not equal the real owner of data set the separation is aborted. During build up the modality must be always preset with a supported modality value ('Modality_CT' or 'Modality_MR'). */ DS_STATUS_FILE_TYPE *StatusFileAddress; /* Common Status File address. This parameter can be set if a status file mapped outside of the data set library. Otherwise this parameter must be set to nil (DS_STATUS_FILE_NIL). If acssess to a status file possible the Unique Identifier (0009'H,1015'H) is build up with common status file information otherwise the non PACS presettings are used. */ transformation_status_t Code; /* Detailed status code. This parameter is set by data set library. */ keyword_t StopItem; /* Last handled item. If an fatal error occur during transformation this parameter contains the error produced item group and element number. This two parameters must be initlaized with 0. */ } control_status_t; typedef struct interface_pointer_list_tag { control_area_ex_t Ex; /* Internal Data Set information */ control_area_in_t In; /* NEMA Data Set information */ control_status_t CntStatus; /* common status information */ } interface_pointer_list_t; /*]*/ /*== "NEMA Parser Pointer List" ==========================================================*/ /*[ Description: Entity "NEMA Parser Pointer List" The NEMA data set parser works as an anticipationed scanner. In this manner it is possible to analyse the current work item in his NEMA data set context. Detection of end of data set, group change, subgroup change, generation of subgroup owner list, position control during parsing etc. can be done with the same data set library features. The NEMA parser pointer list contains several pointers (addresses) to special locations in a NEMA data set, like begin of data set, end of data set, end of current worked group, end of group containing next item. In addition the item keys of current worked item and the following next item are stored. But the most importent information for a application programmer is the pointer to the item value of the current worked item. This pointer is used to split, convert or fill the item for internal use. For clearness, the 'current worked item' is the item currently analysed, transfered, tested etc. by the running seperation. The 'next item' is the item directly following the worked item in the Nema data set. Durring item separation it is used to approach to the given (searched) item. But this subject is transperent for a application which works in a level higher as level of function ds_seperate_given_item(). The following table shows a comparision of work and next/this item parameter: parameter parser base parser current location ========================================================================= item group number WorkKey.GroupNumber NextKey.GroupNumber item element number WorkKey.ElementNumber NextKey.ElementNumber item value length WorkKey.ValueLength NextKey.ValueLength begin of item key - BeginOfNextKey begin of item value BeginOfWorkValue - end of group containing item EndOfWorkGroup EndOfThisGroup The work item is the current item used by the separation application but the base of scanning by parser. The next/this item is the item current analyzed by the parser. The parameter 'EndOf- ThisGroup' is not named 'EndOfNextGroup' to avoid confusion during reading source code. The term 'next' discribe not the right situation for this pointer. */ /* DECLARATION: declare the NEMA parser pointer list types*/ typedef struct item_key_tag { short GroupNumber; /* item group number */ short ElementNumber; /* item element number */ long ValueLength; /* item value length */ } item_key_t; typedef struct nema_parser_pointer_list_tag { item_key_t WorkKey; /* current worked item */ item_key_t NextKey; /* next item in data set */ char *BeginOfWorkValue; /* points to first byte of item value */ char *BeginOfNextKey; /* points to first byte of next item key */ char *EndOfWorkGroup; /* points to first byte of group following the current worked group. The last address in this group is EndOfWorkGroup - 1. */ char *EndOfThisGroup; /* points to first byte of group following group containing next item. The last address in this group is EndOfThisGroup - 1. */ char *BeginOfDataSet; /* points to first byte of data set. */ char *EndOfDataSet; /* points to first byte behint the NEMA data set. The last address in the data set is EndOfDataSet - 1. The address EndOfDataSet is not part of the data set. The pointer is only used as stop mark during data set parsing. Don't dereference this pointer to avoid segmentation fault. */ } nema_parser_pointer_list_t; /*]*/ /*== "Shadow Subgroup Owner List" ========================================================*/ /*[ Description: Entity "Shadow Subgroup Owner List" In each odd-numbered group expect the command information shadow group 1, a fixed block of data element numbers is reserved to identify the "ownership" of sets of data elements. The elements 0010'H to 007F'H are reserved to identify the manufacturer sets of elements. Each identifier element (0010'H to 007F'H) is a type 1 free-formatted (FF) single (S) ASCII string (AT) and contains e.g. the OWNER_STRING_SPI (SPI Recognition Code) for blocks of data elements reserved by SPI or the OWNER_STRING_MED for blocks of data elements reserved by Siemens UBMed. The shodow subgroup owner list is used to fit subgroup location order in the NEMA data set byte stream to the CMS program separation order without a parser reset at change from one shadow subgroup to the next. This is necessary for higher performance. The shadow subgroup owner list is a two-dimensional array. In the first column are stored the subgroup numbers found by function ds_get_subgroup_owner_code() in ascent order as item element base number. In the second column are stored the CMS defined subgroup owner numbers. The last element of the shadow subgroup owner list must be marked with the end of list element END_OF_OWNER_LIST. The length of the list must be equal the number of listed shadow owner strings. The following graphic shows the shodow subgroup owner list format and an example for a CMS defined Identifiying Information (0009'H) shodow group: OWNER_NUMBERS SUBGROUP_NUMBERS 'ListCounter' +------------------+------------------+ |ItemElementBase |OwnerNumber | 0 +------------------+------------------+ |ItemElementBase |OwnerNumber | 1 +------------------+------------------+ |ItemElementBase |OwnerNumber | 2 +------------------+------------------+ |END_OF_OWNER_LIST |END_OF_OWNER_LIST | 3 +------------------+------------------+ ..... .. +------------------+------------------+ |END_OF_OWNER_LIST |END_OF_OWNER_LIST | MAX_OWNER +------------------+------------------+ OWNER_NUMBERS SUBGROUP_NUMBERS 'ListCounter' +------------------+------------------+ | 0x1000 |OWNER_NUMBER_SPI_1| 0 +------------------+------------------+ | 0x1100 |OWNER_NUMBER_MED | 1 +------------------+------------------+ | 0x1200 |OWNER_NUMBER_CMS | 2 +------------------+------------------+ | 0x1300 |OWNER_NUMBER_LAB | 3 +------------------+------------------+ |END_OF_OWNER_LIST |END_OF_OWNER_LIST | 4 +------------------+------------------+ ..... .. +------------------+------------------+ |END_OF_OWNER_LIST |END_OF_OWNER_LIST | MAX_OWNER +------------------+------------------+ With this list it is possible to handle the shadow group flexibility defined in [SPI Image] document 4. This means it is possible to parse a NEMA data set without reset the parser at change from one shadow subgroup to the next. */ /*]*/ /* DECLARATION: declare the supported shodow subgroup owner */ #define OWNER_STRING_ACQU "SIEMENS CM VA0 ACQU" #define OWNER_STRING_CMS "SIEMENS CM VA0 CMS " #define OWNER_STRING_CT "SIEMENS CT VA0 GEN " #define OWNER_STRING_CT_COAD "SIEMENS CT VA0 COAD" #define OWNER_STRING_CT_IMA "SIEMENS CT VA0 IMA " #define OWNER_STRING_CT_RAW "SIEMENS CT VA0 RAW " #define OWNER_STRING_LAB "SIEMENS CM VA0 LAB " #define OWNER_STRING_MED "SIEMENS MED " #define OWNER_STRING_MR "SIEMENS MR VA0 GEN " #define OWNER_STRING_MR_COAD "SIEMENS MR VA0 COAD" #define OWNER_STRING_MR_IMA "SIEMENS MR VA0 IMA " #define OWNER_STRING_MR_RAW "SIEMENS MR VA0 RAW " #define OWNER_STRING_MR_SPEC "SIEMENS MR VA0 SPEC" #define OWNER_STRING_SPI_1 "SPI RELEASE 1 " #define OWNER_STRING_SPI_2 "SPI " #define OWNER_STRING_SPI_3 "SPI Release 1 " #define OWNER_STRING_SPI_4 "SPI-VERSION 01.00 " #define OWNER_STRING_SPI_5 "SPI_RELEASE_1_" /* NOTE: shodow subgroup owner list */ /* Don't change the order of the following define-blocks! */ #define PRE 0 #define OWNER_NUMBER_ACQU 0 #define OWNER_NUMBER_CMS 1 #define OWNER_NUMBER_CT 2 #define OWNER_NUMBER_CT_COAD 3 #define OWNER_NUMBER_CT_IMA 4 #define OWNER_NUMBER_CT_RAW 5 #define OWNER_NUMBER_LAB 6 #define OWNER_NUMBER_MED 7 #define OWNER_NUMBER_MR 8 #define OWNER_NUMBER_MR_COAD 9 #define OWNER_NUMBER_MR_IMA 10 #define OWNER_NUMBER_MR_RAW 11 #define OWNER_NUMBER_MR_SPEC 12 #define OWNER_NUMBER_SPI_1 13 #define OWNER_NUMBER_SPI_2 14 #define OWNER_NUMBER_SPI_3 15 #define OWNER_NUMBER_SPI_4 16 #define OWNER_NUMBER_SPI_5 17 #define MAX_OWNER 17 + 2 #define OWNER_STRING_SPI OWNER_STRING_SPI_1 #define END_OF_OWNER_LIST (-1) #define OWNER_NUMBERS 0 #define SUBGROUP_NUMBERS 1 typedef long shadow_owner_list_t[MAX_OWNER][2]; #endif minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicomserver-debug.c0000644000175000000620000000022512574624760025447 0ustar stevestaff#if 0 # define DO_INPUT_TRACING #endif #if 1 # define KEEP_FILES #endif #if 0 # define DO_HIGH_LOGGING #endif #include "dicomserver-nondebug.c" minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/parse_dicom_groups.c0000644000175000000620000001073112574624760025730 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : parse_dicom_groups.c @DESCRIPTION: Routine to parse dicom file - replicates postconditions of save_transferred_object.c @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : * $Log: parse_dicom_groups.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.2 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.1 2001/12/31 17:27:01 rhoge * adding file to repository - works for numa4 non-mos files now * ---------------------------------------------------------------------------- */ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : parse_dicom_groups @INPUT : group_list - list of acr-nema groups that make up object @OUTPUT : data_info - information about data object @RETURNS : (nothing) @DESCRIPTION: Routine to parse dicom object @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ public void parse_dicom_groups(Acr_Group group_list, Data_Object_Info *data_info) { Acr_Group group; Acr_Element element; char patient_name[256]; unsigned short AcqMat[4]; unsigned short freq_rows; unsigned short freq_cols; unsigned short phase_rows; unsigned short phase_cols; int maxlen = sizeof(Cstring) - 1; // Get info to construct unique identifiers for study, series/acq // for file processing get_identification_info(group_list, &(data_info->study_id), &(data_info->acq_id), &(data_info->rec_num), &(data_info->image_type)); // Get number of echos, echo number, number of dynamic scans and // dynamic_scan_number data_info->num_echoes = acr_find_int(group_list, SPI_Number_of_echoes, 999); data_info->echo_number = acr_find_int(group_list, ACR_Echo_number, 999); data_info->num_dyn_scans = acr_find_int(group_list, ACR_Acquisitions_in_series, 999); data_info->dyn_scan_number = acr_find_int(group_list, ACR_Acquisition, 999); data_info->global_image_number = acr_find_int(group_list, ACR_Image, 999); /* rhoge: new info added to data_info by rhoge: nominal number of slices; this is used in detection of a stream of files with the same acquisition ID number in which there are more files than slices. If the number of signal averages is greater than one, we will assume that this means the acquisition loop was used for dynamic scanning. WARNINGS: the same thing may need to be done with `number of partitions' for it to work with 3D scans */ data_info->num_slices_nominal = acr_find_int(group_list, SPI_Number_of_slices_nominal, 999); data_info->slice_number = 999; // identification info needed to generate unique session id // for file names data_info->study_date = acr_find_int(group_list, ACR_Study_date, 999); data_info->study_time = acr_find_int(group_list, ACR_Study_time, 999); data_info->scanner_serialno = acr_find_int(group_list, ACR_Device_serial_number, 999); // identification info needed to determine if mosaics used element = acr_find_group_element(group_list,ACR_Acquisition_matrix); acr_get_element_short_array(element,4,AcqMat); freq_rows = AcqMat[0]; freq_cols = AcqMat[1]; phase_rows = AcqMat[2]; phase_cols = AcqMat[3]; // rows in acq matrix is larger of freq rows and freq columns: data_info->acq_rows = ( freq_rows > freq_cols ? freq_rows : freq_cols ); // all images are square, at this time data_info->acq_cols = data_info->acq_rows; data_info->rec_rows = acr_find_int(group_list,ACR_Rows, 999); data_info->rec_cols = acr_find_int(group_list,ACR_Columns, 999); data_info->num_mosaic_rows=acr_find_int(group_list,EXT_Mosaic_rows, 999); data_info->num_mosaic_cols=acr_find_int(group_list,EXT_Mosaic_columns,999); data_info->num_slices_in_file= acr_find_int(group_list,EXT_Slices_in_file,999); // sequence, protocol names (useful for debugging): (void) strncpy(data_info->sequence_name, acr_find_string(group_list,ACR_Sequence_name,""),maxlen); (void) strncpy(data_info->protocol_name, acr_find_string(group_list,ACR_Protocol_name,""),maxlen); return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/use_the_files.c0000644000175000000620000002312012574624760024656 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : use_the_files.c @DESCRIPTION: Code to do something with the files copied through dicom. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: use_the_files.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.8 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.7 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.6 2002/03/19 22:10:16 rhoge * removed time sorting for N4DCM mosaics - time is random for mosaics * * Revision 1.5 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.4 2001/04/19 19:09:04 rhoge * added macro to turn off echos/frames in one file * * Revision 1.3 2001/02/26 06:16:00 rhoge * modified to allow specification of destination directory on command line * * Revision 1.2 2000/12/11 17:44:49 rhoge * added 1 to indices in debugging statements for consistency with number * of elements * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:52:00 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* Function prototypes */ /* Commented out by rhoge, put back in by Leili */ //int gethostname (char *name, size_t namelen); File_Type file_type; char command_line[512]; char *pname; /* ----------------------------- MNI Header ----------------------------------- @NAME : use_the_files @INPUT : project_name - name to use for project file num_files - number of image files file_list - list of file names UseArgDir - flag to use command line OutDir destination OutDir - destination directory @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to do something with the files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 23, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void use_the_files(char *project_name, int num_files, char *file_list[], Data_Object_Info *data_info[], int UseArgDir,char *OutDir) /* last three args added by rhoge */ { int ifile; int num_acq_files; extern int Do_logging; char **acq_file_list; int *used_file; int found_first; double cur_study; int cur_acq; int cur_rec; int cur_imgtyp; int cur_echo; int cur_dyn_scan; int echoes_in_one_file; int dyn_scans_in_one_file; int exit_status; char *output_file_name; char file_prefix[256]; int output_uid, output_gid; char string[512]; FILE *fp; (void) fprintf(stderr, "The pname is: %s\n", pname); /* The if statment commented out by Leili forcing the old-style project_file to be used since it is invoked by the server*/ //if (!strncmp(pname,"dicomserver",11)) { // use old-style project_file if invoked by server (void) read_project_file(project_name, file_prefix, &output_uid, &output_gid, command_line, (int) sizeof(command_line)); //} if (UseArgDir) { // if an output directory name has been // provided on the command line //(void) fprintf(stderr, "The output directory name has been provided \n"); strcpy(file_prefix,OutDir); } else if (!strncmp(project_name,"/",1)) { // AEtitle is destination //(void) fprintf(stderr, "The output directory is the one specified in the AEtitle \n"); strcpy(file_prefix,project_name); } if (Do_logging > HIGH_LOGGING) { // debugging fprintf(stderr, "project_name: [%s] file_prefix: [%s]\n", project_name,file_prefix); } // Allocate space for acquisition file list acq_file_list = MALLOC(num_files * sizeof(*acq_file_list)); used_file = MALLOC(num_files * sizeof(*used_file)); for (ifile=0; ifile < num_files; ifile++) used_file[ifile] = FALSE; do { // Loop through files, looking for an acquisition // file groups should already have been sorted into acquisitions // in calling program // this code is in a `do while' loop because we may found_first = FALSE; num_acq_files = 0; for (ifile=0; ifile < num_files; ifile++) { if (used_file[ifile]) continue; if (!found_first) { /* found first file: set all current attributes like study id, acq id, rec num(?), image type, echo number, dyn scan number, flag for multiple echoes, flag for multiple time points the flag input file as `used' */ found_first = TRUE; cur_study = data_info[ifile]->study_id; cur_acq = data_info[ifile]->acq_id; cur_rec = data_info[ifile]->rec_num; cur_imgtyp = data_info[ifile]->image_type; cur_echo = data_info[ifile]->echo_number; cur_dyn_scan = data_info[ifile]->dyn_scan_number; /* note that if there are only two echos OR only two time points, we turn off dyn_scans_in_one_file on the presumption that the user is more likely to treat them as separate acquisitions than a scan with a time dimension (turned off!!) */ #define MIN_FRAMES_PER_FILE 1 echoes_in_one_file = (data_info[ifile]->num_echoes > MIN_FRAMES_PER_FILE); dyn_scans_in_one_file = (data_info[ifile]->num_dyn_scans > MIN_FRAMES_PER_FILE); used_file[ifile] = TRUE; } /* otherwise check if attributes of the new input file match those of the current output context and flag input file as `used' */ else if ((data_info[ifile]->study_id == cur_study) && (data_info[ifile]->acq_id == cur_acq) && (data_info[ifile]->rec_num == cur_rec) && (data_info[ifile]->image_type == cur_imgtyp) && ((data_info[ifile]->echo_number == cur_echo) || echoes_in_one_file) && ((data_info[ifile]->dyn_scan_number == cur_dyn_scan) || dyn_scans_in_one_file)) { used_file[ifile] = TRUE; } if (used_file[ifile]) { /* if input file is flagged as `used', then add its index to the list of files for this acquisition (and increment counter) */ acq_file_list[num_acq_files] = file_list[ifile]; num_acq_files++; } } // rhoge: end of loop over files // Use the files for this acquisition if (found_first) { // Print out the file names if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\nFiles copied:\n"); for (ifile=0; ifile < num_acq_files; ifile++) { (void) fprintf(stderr, " %s\n", acq_file_list[ifile]); } } // Create minc file exit_status = siemens_dicom_to_minc(num_acq_files, acq_file_list, NULL, FALSE, file_prefix, &output_file_name); if (exit_status != EXIT_SUCCESS) continue; /* Print log message */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Created minc file %s.\n", output_file_name); } /* Invoke a command on the file (if requested) and get the returned file name */ if (strlen(command_line) > (size_t) 0) { (void) sprintf(string, "%s %s", command_line, output_file_name); printf("-Applying command '%s' to output file... ",command_line); (void) fflush(stdout); if ((fp=popen(string, "r")) != NULL) { (void) fscanf(fp, "%s", output_file_name); if (pclose(fp) != EXIT_SUCCESS) { (void) fprintf(stderr, "Error executing command\n \"%s\"\n", string); } else if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Executed command \"%s\",\nproducing file %s.\n", string, output_file_name); } } else { (void) fprintf(stderr, "Error executing command \"%s\"\n", string); } printf("Done.\n"); } /* Change the ownership */ if ((output_uid != INT_MIN) && (output_gid != INT_MIN)) { (void) chown(output_file_name, (uid_t) output_uid, (gid_t) output_gid); } } } while (found_first); /* Free acquisition file list */ FREE(acq_file_list); FREE(used_file); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_dicom_send.c0000644000175000000620000013417012574624760025677 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_send.c @DESCRIPTION: Program to convert siemens vision internal format files to dicom and send them to a remote server. @METHOD : @GLOBALS : @CREATED : July 8, 1997 (Peter Neelin) re-write by Rick Hoge @MODIFIED : $Log: siemens_dicom_send.c,v $ @MODIFIED : Revision 1.3 2008-01-17 02:33:01 rotor @MODIFIED : * removed all rcsids @MODIFIED : * removed a bunch of ^L's that somehow crept in @MODIFIED : * removed old (and outdated) BUGS file @MODIFIED : @MODIFIED : Revision 1.2 2008/01/12 19:08:14 stever @MODIFIED : Add __attribute__ ((unused)) to all rcsid variables. @MODIFIED : @MODIFIED : Revision 1.1.1.1 2003/08/15 19:52:55 leili @MODIFIED : Leili's dicom server for sonata @MODIFIED : @MODIFIED : Revision 1.9 2001/10/19 13:08:39 rhoge @MODIFIED : - fixed problem for single slice acq loop scans in siemens_dicom_send @MODIFIED : - added diffusion pulse seq in ima2mnc, restored hard-coded path @MODIFIED : @MODIFIED : Revision 1.8 2001/04/19 19:33:10 rhoge @MODIFIED : added multi-echo support, basic testing. Changed TEST to MYDEBUG @MODIFIED : @MODIFIED : Revision 1.7 2000/12/17 01:04:40 rhoge @MODIFIED : clean up log format @MODIFIED : @MODIFIED : Revision 1.6 2000/12/17 01:02:58 rhoge @MODIFIED : Made some debugging statements conditional on TEST macro, @MODIFIED : in course of changes to restore Sun function after @MODIFIED : byte-aligmnent fixes (padding) for Linux port @MODIFIED : @MODIFIED : Revision 1.5 2000/12/14 22:56:40 rhoge @MODIFIED : removed DBL_MAX - not defined on sun (replaced with 1.0 in @MODIFIED : acr_find_double, line 502) @MODIFIED : @MODIFIED : Revision 1.4 2000/12/11 20:04:35 rhoge @MODIFIED : got rid of printf statements for time correction @MODIFIED : @MODIFIED : Revision 1.3 2000/12/11 19:27:25 rhoge @MODIFIED : fix for leading zeros if hms less than two digits in time @MODIFIED : @MODIFIED : Revision 1.2 2000/12/11 17:43:01 rhoge @MODIFIED : added frame time correction - code compiles and runs, but order @MODIFIED : problem may not yet be fixed @MODIFIED : @MODIFIED : Revision 1.1.1.1 2000/11/30 02:05:54 rhoge @MODIFIED : imported sources to CVS repository on amoeba @MODIFIED : * * Revision 1.9 1999/08/03 17:42:55 neelin * Added ability to handle multiple input directories. * * Revision 1.8 1998/11/17 16:13:29 neelin * Changes to log messages to ensure that 100 percent is only displayed * when transfer is complete. * * Revision 1.7 1998/11/17 14:49:22 neelin * Modified help comment. * * Revision 1.6 1998/11/17 14:00:12 neelin * Changed logging to print percentage with overstrike for terminals. * * Revision 1.5 1998/11/16 19:48:43 neelin * Added support for breaking up mosaic images and sending the pieces one * at a time. * * Revision 1.4 1998/11/13 16:02:09 neelin * Modifications to support packed images and asynchronous transfer. * * Revision 1.3 1998/02/20 17:37:55 neelin * Removed debugging statements. * * Revision 1.2 1997/11/04 14:31:30 neelin * *** empty log message *** * * Revision 1.1 1997/08/11 12:50:53 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include /* for slice position hacks */ #include #include #include #include #include #include #include #include /* Constants */ #ifndef public # define public #endif #ifndef private # define private static #endif #ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 #endif #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 #endif #define ALLOC_INCREMENT 100 #define LOG_PERCENT (2.0) /* Dicom definitions */ #define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" #define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" #define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" #define MY_AE_TITLE "MGH_CLIENT" /* rhoge: debugging definitions */ /* #define MYDEBUG */ /* Define element id's */ DEFINE_ELEMENT(static, ACR_Slice_thickness , 0x0018, 0x0050, DS); DEFINE_ELEMENT(static, ACR_Rows , 0x0028, 0x0010, US); DEFINE_ELEMENT(static, ACR_Columns , 0x0028, 0x0011, US); DEFINE_ELEMENT(static, ACR_Pixel_size , 0x0028, 0x0030, DS); DEFINE_ELEMENT(static, ACR_Bits_allocated , 0x0028, 0x0100, US); DEFINE_ELEMENT(static, ACR_Image , 0x7fe0, 0x0010, OW); DEFINE_ELEMENT(static, SPI_Sequence_File_Name , 0x0019, 0x1511, LT); DEFINE_ELEMENT(static, SPI_Image_position , 0x0021, 0x1160, DS); DEFINE_ELEMENT(static, SPI_Image_normal , 0x0021, 0x1161, DS); DEFINE_ELEMENT(static, SPI_Image_distance , 0x0021, 0x1163, DS); DEFINE_ELEMENT(static, SPI_Current_slice_number , 0x0021, 0x1342, IS); DEFINE_ELEMENT(static, SPI_Slice_distance_factor , 0x0021, 0x1344, DS); /* added by rhoge for acquisition loop with mosaic overflow * note: on the Sonata, {acquisition,run,series,scan} is called * Study in the header! beware of confusion... */ DEFINE_ELEMENT(static, ACR_Study , 0x0020, 0x0010, IS); DEFINE_ELEMENT(static, ACR_Series , 0x0020, 0x0011, IS); DEFINE_ELEMENT(static, ACR_Acquisitions_in_series , 0x0020, 0x1001, IS); DEFINE_ELEMENT(static, ACR_Echo_number , 0x0018, 0x0086, IS); DEFINE_ELEMENT(static, SPI_Number_of_averages , 0x0018, 0x0083, DS); DEFINE_ELEMENT(static, SPI_Number_of_slices_nom , 0x0021, 0x1340, IS); DEFINE_ELEMENT(static, SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); DEFINE_ELEMENT(static, SPI_Number_of_fourier_lines_nominal,0x0019, 0x1220, IS); DEFINE_ELEMENT(static, SPI_Number_of_echoes , 0x0021, 0x1370, IS); DEFINE_ELEMENT(static, ACR_Study_time , 0x0008, 0x0030, TM); DEFINE_ELEMENT(static, ACR_Repetition_time , 0x0018, 0x0080, DS); DEFINE_ELEMENT(static, ACR_Acquisition_time , 0x0008, 0x0032, TM); /* Typedefs */ typedef struct { int key; char *name; int dirindex; } Sort_entry; typedef struct { int packed; int mosaic_seq; int size[2]; int big[2]; int grid[2]; int pixel_size; Acr_Element big_image; Acr_Element small_image; int sub_images; int first_image; double normal[3]; double step[3]; double position[3]; } Multi_Image; typedef struct Mosaic_Info { char *match_string; int size[2]; struct Mosaic_Info *next; } *Mosaic_Info; /* Function prototypes */ public Acr_Group siemens_to_dicom(char *filename, int read_image); public void update_coordinate_info(Acr_Group group_list); public int process_mosaic_args(char *dst, char *key, int argc, char **argv); public Mosaic_Info get_mosaic_info(Mosaic_Info mosaic_info_list, char *protocol); public int get_directory(char *dst, char *key, char *nextarg); public void get_file_names(int num_dirs, char **directory_list, int patient_number, int first_image, int last_image, int *num_files, char ***file_list, int **file_dirindex_list); private int sort_function(const void *entry1, const void *entry2); public void free_file_names(char *fullpath, char *file_list[], int num_files); public int multi_image_init(Acr_Group group_list, Multi_Image *multi_image); public void multi_image_modify_group_list(Acr_Group group_list, Multi_Image *multi_image, int iimage); public void multi_image_cleanup(Acr_Group group_list, Multi_Image *multi_image); /* Variables for argument parsing */ Mosaic_Info mosaic_info_list = NULL; int max_outstanding = 10; char **directory_list = NULL; int num_dirs = 0; int num_dirs_alloc = 0; /* Argument table */ ArgvInfo argTable[] = { {"-mosaic", ARGV_GENFUNC, (char *) process_mosaic_args, (char *) &mosaic_info_list, "Add a mosaic protocol and subimage size to the list.\n" "\tUsage: -mosaic \n" "\tIf the protocol name begins with a /, then an exact match is done.\n" "\tIf it contains an extension, then a match on filename alone is done.\n" "\tOtherwise, a prefix match is done.\n" "\tParent directories can be specified for a prefix match.\n" "\tExamples:\n" "\t\t-mosaic /home/user/protcols/mosaic/myproto64_version1.ekc 64 64\n" "\t\t-mosaic myproto64 64 64\n" "\t\t-mosaic myproto64_version1.ekc 64 64\n" "\t\t-mosaic mosaic/myproto64_ 64 64\n" "\t\t-mosaic mosaic/myproto64_version1.ekc 64 64\n" "\t\t-mosaic mosaic/ 64 64\n" }, {"-directory", ARGV_FUNC, (char *) get_directory, (char *) NULL, "Specify a directory to search (instead of positional option)."}, {"-max_outstanding", ARGV_INT, (char *) 1, (char *) &max_outstanding, "Specify max outstanding replies for asynchronous transfer."}, {(char *) NULL, ARGV_END, (char *) NULL, (char *) NULL, (char *) NULL} }; /* Main program */ int main(int argc, char *argv[]) { Acr_Group group_list; char *pname; char *host; char *port; char *AEtitle; int iarg; int patient_number; int first_image; int last_image; char **file_list; int *file_dirindex_list; int ifile, num_files, idir; Acr_File *afpin, *afpout; char *fullpath; int maxlength, maxdirlength, len; int iimage; Multi_Image multi_image; char *log_separator; int needed_args; /* stuff added by rhoge for acquisition loop with mosaic overflow */ int idim; int imosaic; int iecho; int num_mosaics; int acqframe; int num_acqframes; int num_echos; int echo; int num_slices; int num_sub_images; int nimages; int num_special_files; int num_mosaic_elements; double top_slice_position[3]; int current_series; int current_slice_index; double scan_start_time; double start_time_sec; double start_hours; double start_minutes; double start_seconds; double frame_hours; double frame_minutes; double frame_seconds; double frame_hours_oflow; double frame_minutes_oflow; double frame_seconds_oflow; double rel_frame_time_sec; double abs_frame_time_sec; char abs_frame_time_hms[100]; /* Check arguments. */ /* Ugly hack here to keep backwards compatibility, but also allow -directory specification. needed_args stores the minimum number of args needed, depending on whether the -directory option was specified. */ pname = argv[0]; if (ParseArgv(&argc, argv, argTable, 0) || (argc < (needed_args = (directory_list == NULL ? 6:5))) || (argc > needed_args+2)) { (void) fprintf(stderr, "Usage: %s host port AEtitle [directory] patient_num [first [last]]\n", pname); return EXIT_FAILURE; } iarg=1; host = argv[iarg++]; port = argv[iarg++]; AEtitle = argv[iarg++]; if (directory_list == NULL) { num_dirs_alloc = 1; directory_list = malloc(sizeof(*directory_list) * num_dirs_alloc); directory_list[num_dirs] = argv[iarg++]; num_dirs++; } patient_number = atoi(argv[iarg++]); first_image = ((argc < needed_args+1) ? 0 : atoi(argv[iarg++])); last_image = ((argc < needed_args+2) ? INT_MAX : atoi(argv[iarg++])); /* Check range */ if ((last_image < first_image) || (first_image < 0)) { (void) fprintf(stderr, "Bad range of images: use +ve values with last >= first\n"); return EXIT_FAILURE; } /* Get file names */ get_file_names(num_dirs, directory_list, patient_number, first_image, last_image, &num_files, &file_list, &file_dirindex_list); if (num_files <= 0) { (void) fprintf(stderr, "No files to send.\n"); return EXIT_FAILURE; } /* Get space for file names */ maxlength = 0; for (ifile = 0; ifile < num_files; ifile++) { len = strlen(file_list[ifile]); if (len > maxlength) maxlength = len; } maxdirlength = 0; for (idir = 0; idir < num_dirs; idir++) { len = strlen(directory_list[idir]); if (len > maxdirlength) maxdirlength = len; } fullpath = malloc(maxdirlength + maxlength + 2); /* Make dicom connection */ if (!acr_open_dicom_connection(host, port, AEtitle, MY_AE_TITLE, ACR_MR_IMAGE_STORAGE_UID, ACR_IMPLICIT_VR_LITTLE_END_UID, &afpin, &afpout)) { free_file_names(fullpath, file_list, num_files); return EXIT_FAILURE; } /* Set maximum number of outstanding responses */ acr_set_client_max_outstanding(afpin, max_outstanding); /* Loop over the input files, sending them one at a time */ ifile = 0; while (ifile < num_files) { /* get next dicom group */ /************** START of group read unit - function? ********/ /* Get file name */ idir = file_dirindex_list[ifile]; (void) sprintf(fullpath, "%s/%s", directory_list[idir], file_list[ifile]); printf("processing file %s (%d of %d)\n", file_list[ifile], ifile+1,num_files); (void) fflush(stdout); /* Read data */ group_list = siemens_to_dicom(fullpath, TRUE); if (group_list == NULL) continue; /* Look for multi-image files (many images in one) */ num_mosaic_elements = multi_image_init(group_list, &multi_image); #ifdef MYDEBUG printf("num_mosaic_elements = %d\n",num_mosaic_elements); #endif /************** END of group read unit - function? ********/ /* determine number of `acqframes' */ if (multi_image.mosaic_seq) { num_acqframes = acr_find_int(group_list, SPI_Number_of_averages, 1); } else { num_acqframes = 1; } #ifdef MYDEBUG printf("num_acqframes = %d\n",num_acqframes); #endif /* determine real number of slices */ num_slices = acr_find_int(group_list, SPI_Number_of_slices_cur, 1); num_echos = acr_find_int(group_list, SPI_Number_of_echoes, 1); #ifdef MYDEBUG printf("num_echos = %d\n",num_echos); #endif /* determine number of mosaics per volume */ num_mosaics = (int)ceil((float)num_slices/(float)num_mosaic_elements); /* determine total number of `special' files expected for this series */ num_special_files = num_acqframes * num_mosaics * num_echos; #ifdef MYDEBUG printf("num_mosaics = %d\n",num_mosaics); printf("num_special_files = %d\n",num_special_files); #endif current_series = acr_find_int(group_list, ACR_Study, 0); #ifdef MYDEBUG printf("current_series = %d\n",current_series); #endif /* add loop for echos, since diffusion scans may use echo loop for multiple encodings. Loop order will usually be acq, mosaic/slice, echo */ for (iecho = 0; iecho < num_echos; iecho++) { for (imosaic = 0; imosaic < num_mosaics; imosaic++) { current_slice_index = acr_find_int(group_list, SPI_Current_slice_number, 1); #ifdef MYDEBUG printf("current_slice_index = %d\n",current_slice_index); #endif for (acqframe = 0; acqframe < num_acqframes; acqframe++) { /* check if we hit a new series or slice unexpectedly early - this indicates an aborted run */ if ( acr_find_int(group_list,ACR_Study,0) != current_series ) { /* if we hit a new SERIES too early, that's it. We break out of all loops (acqframe and imosaic), clean up the group, and start over again at the top of while(ifile1) { /* Number of dynamic scans */ acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, num_acqframes); /* now we have to fix the frame times, since these may be used in sorting the frames at the receive end */ /* start of scanning run */ scan_start_time = atof(acr_find_string(group_list, ACR_Study_time, "")); /* extract hours, minutes, seconds */ start_hours = (int) (scan_start_time/(double)10000); scan_start_time -= start_hours * (10000.0); start_minutes = (int) (scan_start_time/(double)100); scan_start_time -= start_minutes * (100.0); start_seconds = scan_start_time; /* convert start time to seconds from start of day */ start_time_sec = start_hours*3600.0 + start_minutes*60.0 + start_seconds; /* compute frame time (from start) based on TR */ rel_frame_time_sec = acqframe * (acr_find_double(group_list, ACR_Repetition_time,1.0)/1000); /* now add times in seconds to get absolute frame time */ abs_frame_time_sec = rel_frame_time_sec + start_time_sec; /* convert absolute frame time in sec back to hms */ frame_hours = floor(abs_frame_time_sec/3600.0); abs_frame_time_sec -= frame_hours * 3600.0; frame_minutes = floor(abs_frame_time_sec/60.0); abs_frame_time_sec -= frame_minutes * 60.0; frame_seconds = abs_frame_time_sec; /* check for day overflow (hours>23)*/ frame_hours = ((int)frame_hours)%24; /* write frame time to string */ sprintf(abs_frame_time_hms,"%02.0f%02.0f%06.3f", frame_hours, frame_minutes, frame_seconds); /* now write the proper frame time into the group */ acr_insert_string(&group_list, ACR_Acquisition_time, abs_frame_time_hms); /* current frame index */ acr_insert_numeric(&group_list, ACR_Series,acqframe); } /******** END of acquisition loop correction section *******/ /* determine number of sub-images in mosaic */ if (multi_image.mosaic_seq) { if (imosaic == num_mosaics-1) { num_sub_images = num_slices-(num_mosaics-1)*num_mosaic_elements; } else { num_sub_images = num_mosaic_elements; } #ifdef MYDEBUG printf("num_sub_images = %d\n",num_sub_images); #endif } else { /* non-mosaic */ num_sub_images = 1; } /********* Start of slice position correction Section ********/ if (multi_image.mosaic_seq && num_sub_images > 1) { /* before 7:00pm Tuesday, March 28 2000: position of first mosaic was ok, but overflow was either off by number of overflow slices (for interleaved excitation order) or correct (for ascending) */ /* after 7:00pm Tuesday, March 28 2000: position of .ima file is that of LAST slice in mosaic this must be corrected to give FIRST slice. This was originally done so that SPI_Current_slice_number would give the number of slices in the mosaic. Unfortunately, this only works for ascending order on the new system. The SPL could therefore be modified to just assign the position of the first sub-mosaic to the .ima file. This would probably be more consistent with other sites */ /* note that the code below depends *only* on the slice position of the first mosaic being equal to that of the last slice, so it should work for either case (it is the slice position of subsequent `overflow' mosaics that has been variable) */ if (imosaic==0) { for (idim=0; idim < 3; idim++) { multi_image.position[idim] -= (double) (num_sub_images-1) * multi_image.step[idim]; top_slice_position[idim]=multi_image.position[idim]; } } else { for (idim=0; idim < 3; idim++) { multi_image.position[idim] = top_slice_position[idim] + (double)imosaic*(double)num_mosaic_elements* multi_image.step[idim]; } } } /* end of if (multi_image.mosaic_seq) */ /********* End of slice position correction Section ********/ /* Now send all the sub-images */ for (iimage=0; iimage < num_sub_images; iimage++) { /* Modify the group list for this image */ multi_image_modify_group_list(group_list, &multi_image, iimage); /* correct slice index if mosaic */ if (multi_image.mosaic_seq) { acr_insert_numeric(&group_list, SPI_Current_slice_number, imosaic*num_mosaic_elements+iimage+1); } /* Send the image */ if (!acr_send_group_list(afpin, afpout, group_list, ACR_MR_IMAGE_STORAGE_UID)) { multi_image_cleanup(group_list, &multi_image); acr_delete_group_list(group_list); acr_close_dicom_connection(afpin, afpout); free_file_names(fullpath, file_list, num_files); return EXIT_FAILURE; } } /* Clean up */ multi_image_cleanup(group_list, &multi_image); acr_delete_group_list(group_list); ifile++; if (ifile == num_files) { /* we've hit the last file - let's get out of here! (this might happen if the last run in the file list is aborted) */ acqframe = num_acqframes; imosaic = num_mosaics; break; } else if (num_special_files > 1) { /* get next dicom group */ /************** START of group read unit - function? ********/ /* Get file name */ idir = file_dirindex_list[ifile]; (void) sprintf(fullpath, "%s/%s", directory_list[idir], file_list[ifile]); printf("processing file %s (%d of %d)\n", file_list[ifile], ifile+1,num_files); (void) fflush(stdout); /* Read data */ group_list = siemens_to_dicom(fullpath, TRUE); if (group_list == NULL) continue; /* Look for multi-image files (many images in one) */ num_mosaic_elements = multi_image_init(group_list, &multi_image); /************** END of group read unit - function? ********/ } /* end of if (num_special_files > 1) */ } /* end of loop over acqframes */ } /* end of loop over mosaics */ } /* end of loop over echos */ } /* end of loop over files */ (void) printf("Done sending files.\n"); (void) fflush(stdout); /* Release the association and free stuff*/ acr_close_dicom_connection(afpin, afpout); free_file_names(fullpath, file_list, num_files); return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : process_mosaic_args @INPUT : dst - arguments from ParseArgv key argc argv @OUTPUT : argv @RETURNS : Number of arguments left @DESCRIPTION: Routine to process the arguments for the -mosaic option. @METHOD : @GLOBALS : @CALLS : @CREATED : November 13, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int process_mosaic_args(char *dst, char *key, int argc, char **argv) { Mosaic_Info *mosaic_info_list, tail, new; char *match_string; char *ptr; int size[2]; int iarg; /* Get pointer to mosaic info list */ mosaic_info_list = (Mosaic_Info *) dst; /* Check number of arguments */ if (argc < 3) { (void) fprintf(stderr, "\"%s\" option requires 3 arguments\n", key); return -1; } /* Get arguments */ match_string = argv[0]; if (match_string == NULL) { (void) fprintf(stderr, "Null string passed to \"%s\"\n", key); return -1; } size[0] = strtol(argv[1], &ptr, 0); if ((ptr == argv[1]) || (size[0] < 1)) { (void) fprintf(stderr, "\"%s\" takes a positive integer (%s)\n", key, argv[1]); return -1; } size[1] = strtol(argv[2], &ptr, 0); if ((ptr == argv[2]) || (size[1] < 1)) { (void) fprintf(stderr, "\"%s\" takes a positive integer (%s)\n", key, argv[2]); return -1; } /* Update the argv list. We copy one extra element, to get the NULL */ argc -= 3; for (iarg = 0; iarg <= argc; iarg++) { argv[iarg] = argv[iarg+3]; } /* Create a new entry and save the values */ new = (Mosaic_Info) malloc(sizeof(*new)); new->match_string = match_string; new->size[0] = size[0]; new->size[1] = size[1]; new->next = NULL; /* Add the entry to the list */ if (*mosaic_info_list == NULL) { *mosaic_info_list = new; } else { tail = *mosaic_info_list; while (tail->next != NULL) { tail = tail->next; } tail->next = new; } return argc; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_mosaic_info @INPUT : mosaic_info_list - pointer to beginning of list protocol - protocol name @OUTPUT : (none) @RETURNS : Pointer to matching mosaic info structure or NULL @DESCRIPTION: Routine to find a mosaic info structure that matches the given protocol name. If none is found, then NULL is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : November 13, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Mosaic_Info get_mosaic_info(Mosaic_Info mosaic_info_list, char *protocol) { #define DIRECTORY_SEP '/' #define EXTENSION_SEP '.' Mosaic_Info info; char *ptr; int have_extension; int num_dirs; int nchars_to_match; int protolen; /* Loop over list */ for (info=mosaic_info_list; info != NULL; info = info->next) { /* Check for an exact match */ if (info->match_string[0] == DIRECTORY_SEP) { if (strcmp(protocol, info->match_string) == 0) { break; } } /* Otherwise look for a match with part of the protocol string */ else { /* Figure out how many directories are given in the match string and whether there is an extension */ have_extension = FALSE; num_dirs = 0; for (ptr = &info->match_string[strlen(info->match_string)]; ptr != info->match_string; ptr--) { /* Extension can only occur on filename part */ if ((num_dirs == 0) && (*ptr == EXTENSION_SEP)) { have_extension = TRUE; } /* Count directory separators, but ignore repeats */ if ((*ptr == DIRECTORY_SEP) && (*(ptr+1) != DIRECTORY_SEP)) { num_dirs++; } } /* Figure out where to start match on protocol string by counting directories */ for (ptr = &protocol[strlen(protocol)-1]; ptr != protocol; ptr--) { if ((*ptr == DIRECTORY_SEP) && (*(ptr+1) != DIRECTORY_SEP)) { num_dirs--; } if (num_dirs < 0) break; } if (*ptr == DIRECTORY_SEP) ptr++; /* Match whole string or just prefix, depending on extension */ nchars_to_match = strlen(info->match_string); protolen = strlen(ptr); if (have_extension && (protolen > nchars_to_match)) { nchars_to_match = protolen; } /* Do the match */ if (strncmp(ptr, info->match_string, nchars_to_match) == 0) { break; } } /* If exact match, else */ } /* End of loop */ /* Return the match */ return info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_directory @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 directory name from command line and adds it to the list. @METHOD : @GLOBALS : @CALLS : @CREATED : August 3, 1999 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int get_directory(char *dst, char *key, char *nextarg) /* ARGSUSED */ { /* Check for a following argument */ if (nextarg == NULL) { (void) fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Check whether we need more space for the directory name */ if (num_dirs >= num_dirs_alloc) { num_dirs_alloc += 10; if (directory_list == NULL) directory_list = malloc(sizeof(*directory_list) * num_dirs_alloc); else directory_list = realloc(directory_list, sizeof(*directory_list) * num_dirs_alloc); } /* Add the new directory */ directory_list[num_dirs] = nextarg; num_dirs++; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_names @INPUT : num_dirs - number of directories in list directory_list - list of directories in which to look patient_number - number found at beginning of file name first_image - first number in range last_image - last number in range @OUTPUT : num_files - number of files found file_list - list of files to be sentn @RETURNS : (nothing) @DESCRIPTION: Routine to find all files in directory and of the form "--.ima" and with in the range given by first_image and last_image. The files are sorted by image number. @METHOD : @GLOBALS : @CALLS : @CREATED : July 11, 1997 (Peter Neelin) @MODIFIED : August 3, 1999 (P.N.) ---------------------------------------------------------------------------- */ public void get_file_names(int num_dirs, char **directory_list, int patient_number, int first_image, int last_image, int *num_files, char ***file_list, int **file_dirindex_list) { DIR *dir; struct dirent *dirent; char *filename; int patid, acqid, imgid; char ch; Sort_entry *sorting_files; int num_entries, num_alloc, ifile, idir; /* Set initial values */ *num_files = 0; *file_list = NULL; *file_dirindex_list = NULL; /* Set up array for file names */ num_entries = 0; num_alloc = ALLOC_INCREMENT; sorting_files = malloc(sizeof(*sorting_files) * num_alloc); /* Loop over directories */ for (idir=0; idir < num_dirs; idir++) { /* Open the directory and find appropriate files */ if ((dir = opendir(directory_list[idir])) == NULL) { (void) fprintf(stderr, "Error opening directory \"%s\": ", directory_list[idir]); perror(NULL); continue; } /* Loop over entries */ while ((dirent = readdir(dir)) != NULL) { /* Get filename */ filename = dirent->d_name; /* Parse the file name */ if (sscanf(filename, "%d-%d-%d.ima%c", &patid, &acqid, &imgid, &ch) != 3) continue; /* Check that we want this file */ if ((patid != patient_number) || (imgid < first_image) || (imgid > last_image)) continue; /* Add more space if needed */ if (num_entries >= num_alloc) { num_alloc += ALLOC_INCREMENT; sorting_files = realloc(sorting_files, sizeof(*sorting_files) * num_alloc); } /* Add the entry and its number for sorting */ sorting_files[num_entries].key = imgid; sorting_files[num_entries].name = strdup(dirent->d_name); sorting_files[num_entries].dirindex = idir; /* Increment the number of entries */ num_entries++; } /* End of loop over files in directory */ /* Close the directory */ (void) closedir(dir); } /* End of loop over directories */ /* Check the number of entries */ if (num_entries <= 0) return; /* Sort the files */ qsort(sorting_files, num_entries, sizeof(sorting_files[0]), sort_function); /* Copy the file names into an array */ *num_files = num_entries; *file_list = malloc(sizeof(**file_list) * (num_entries + 1)); *file_dirindex_list = malloc(sizeof(**file_dirindex_list) * (num_entries + 1)); (*file_list)[num_entries] = NULL; (*file_dirindex_list)[num_entries] = -1; for (ifile=0; ifile < num_entries; ifile++) { (*file_list)[ifile] = sorting_files[ifile].name; (*file_dirindex_list)[ifile] = sorting_files[ifile].dirindex; } /* Free the sorting structure */ free(sorting_files); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two numbers for sorting files. @METHOD : @GLOBALS : @CALLS : @CREATED : July 11, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private int sort_function(const void *entry1, const void *entry2) { int value1, value2; value1 = ((Sort_entry *) entry1)->key; value2 = ((Sort_entry *) entry2)->key; if (value1 < value2) return -1; else if (value1 > value2) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_file_names @INPUT : fullpath file_list num_files @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to free file name strings @METHOD : @GLOBALS : @CALLS : @CREATED : September 19, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_file_names(char *fullpath, char *file_list[], int num_files) { int ifile; free(fullpath); for (ifile=0; ifile < num_files; ifile++) { free(file_list[ifile]); } free(file_list); } /* ----------------------------- MNI Header ----------------------------------- @NAME : multi_image_init @INPUT : group_list - group list as read in from file @OUTPUT : multi_image - structure containing information to be used for modifying group list in loop over images @RETURNS : number of images to be processed @DESCRIPTION: Routine to set up loop over multiple images in an input file. @METHOD : @GLOBALS : mosaic_info_list @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int multi_image_init(Acr_Group group_list, Multi_Image *multi_image) { int group_id, element_id; int last_image, grid_size; long new_image_size; void *data; Acr_Element element; char string[256]; int idim; double pixel_spacing[2], separation; char *protocol; Mosaic_Info mosaic_info; /* Find the protocol name and look for a mosaic info structure */ protocol = acr_find_string(group_list, SPI_Sequence_File_Name, ""); mosaic_info = get_mosaic_info(mosaic_info_list, protocol); /* Check whether we need to do anything */ multi_image->mosaic_seq = (mosaic_info != NULL); multi_image->packed = (mosaic_info != NULL); if (!multi_image->packed) return 1; /* Get some basic image information */ multi_image->big[0] = acr_find_int(group_list, ACR_Columns, 1); multi_image->big[1] = acr_find_int(group_list, ACR_Rows, 1); multi_image->pixel_size = (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; /* Get the image size */ multi_image->size[0] = mosaic_info->size[0]; multi_image->size[1] = mosaic_info->size[1]; /* Get the grid shape, checking that it is not too big if specified */ multi_image->grid[0] = multi_image->big[0] / multi_image->size[0]; multi_image->grid[1] = multi_image->big[1] / multi_image->size[1]; if ((multi_image->grid[0] < 1) || (multi_image->grid[0] < 1)) { (void) fprintf(stderr, "Grid too small: %d x %d\n", multi_image->grid[0], multi_image->grid[1]); exit(EXIT_FAILURE); } /* Check whether we need to do anything (1x1 grid may be the whole image) */ grid_size = multi_image->grid[0] * multi_image->grid[1]; if ((grid_size == 1) && (multi_image->size[0] == multi_image->big[0]) && (multi_image->size[1] == multi_image->big[1])) { /* had to remove this as now ANY images acquired with the mosaic sequence need special treatment */ multi_image->packed = FALSE; return 1; } /* Steal the image element from the group list */ multi_image->big_image = acr_find_group_element(group_list, ACR_Image); if (multi_image->big_image == NULL) { (void) fprintf(stderr, "Couldn't find an image\n"); exit(EXIT_FAILURE); } group_id = acr_get_element_group(multi_image->big_image); element_id = acr_get_element_element(multi_image->big_image); acr_group_steal_element(acr_find_group(group_list, group_id), multi_image->big_image); /* Add a small image */ new_image_size = multi_image->size[0] * multi_image->size[1] * multi_image->pixel_size; data = malloc((size_t) new_image_size); multi_image->small_image = acr_create_element(group_id, element_id, acr_get_element_vr(multi_image->big_image), new_image_size, data); acr_set_element_vr(multi_image->small_image, acr_get_element_vr(multi_image->big_image)); acr_set_element_byte_order(multi_image->small_image, acr_get_element_byte_order(multi_image->big_image)); acr_set_element_vr_encoding(multi_image->small_image, acr_get_element_vr_encoding(multi_image->big_image)); acr_insert_element_into_group_list(&group_list, multi_image->small_image); /* Update the pixel size */ acr_insert_short(&group_list, ACR_Rows, multi_image->size[1]); acr_insert_short(&group_list, ACR_Columns, multi_image->size[0]); /* Get image image index info */ last_image = acr_find_int(group_list, SPI_Current_slice_number, 1); /* sub_images is now just the number of mosaic elements, even if they don't all contain slices */ multi_image->sub_images = multi_image->grid[0] * multi_image->grid[1]; /* NOTE: multi_image->first_image is unkown now, since last_image is no longer necessarily correct. We update slice numbers in main() now. multi_image->first_image = last_image - multi_image->sub_images + 1; */ /* Fiddle the pixel size */ element = acr_find_group_element(group_list, ACR_Pixel_size); if ((element != NULL) && (acr_get_element_numeric_array(element, 2, pixel_spacing) == 2)) { pixel_spacing[0] *= (double) multi_image->big[0] / (double) multi_image->size[0]; pixel_spacing[1] *= (double) multi_image->big[1] / (double) multi_image->size[1]; (void) sprintf(string, "%.15g\\%.15g", pixel_spacing[0], pixel_spacing[1]); acr_insert_string(&group_list, ACR_Pixel_size, string); } /* Get step between slices */ separation = acr_find_double(group_list, ACR_Slice_thickness, 1.0) * (1.0 + acr_find_double(group_list, SPI_Slice_distance_factor, 0.0)); element = acr_find_group_element(group_list, SPI_Image_normal); if ((element == NULL) || (acr_get_element_numeric_array(element, 3, multi_image->normal) != 3)) { multi_image->normal[0] = 0.0; multi_image->normal[1] = 0.0; multi_image->normal[2] = 1.0; } for (idim=0; idim < 3; idim++) { multi_image->step[idim] = separation * multi_image->normal[idim]; } /* Get position and correct to first slice */ element = acr_find_group_element(group_list, SPI_Image_position); if ((element == NULL) || (acr_get_element_numeric_array(element, 3, multi_image->position) != 3)) { multi_image->position[0] = 0.0; multi_image->position[1] = 0.0; multi_image->position[2] = 0.0; } /* sub_images may be wrong here if there is overflow, so I'm commenting this out and moving this step to a point in main() where multi_image->sub_images has been corrected */ /* for (idim=0; idim < 3; idim++) { multi_image->position[idim] -= (double) (multi_image->sub_images-1) * multi_image->step[idim]; } */ /* Return number of sub-images in this image */ return multi_image->sub_images; } /* ----------------------------- MNI Header ----------------------------------- @NAME : multi_image_modify_group_list @INPUT : group_list - group list as read in from file multi_image - structure containing information to be used for modifying group list in loop over images iimage - image number to process (counting from zero). @OUTPUT : group_list @RETURNS : (nothing) @DESCRIPTION: Routine to modify the group list to use a new image. @METHOD : @GLOBALS : @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void multi_image_modify_group_list(Acr_Group group_list, Multi_Image *multi_image, int iimage) { int irow, ibyte, idim, nbyte; int isub, jsub; char *new, *old; long old_offset, new_offset; double position[3], distance; char string[256]; /* Check whether we need to do anything */ if (!multi_image->packed) return; /* Check the image number */ if ((iimage < 0) || (iimage > multi_image->sub_images)) { (void) fprintf(stderr, "Invalid image number to send: %d of %d\n", iimage, multi_image->sub_images); exit(EXIT_FAILURE); } /* Figure out the sub-image indices */ isub = iimage % multi_image->grid[0]; jsub = iimage / multi_image->grid[0]; /* Get pointers */ old = acr_get_element_data(multi_image->big_image); new = acr_get_element_data(multi_image->small_image); /* Copy the image */ nbyte = multi_image->size[0] * multi_image->pixel_size; for (irow=0; irow < multi_image->size[1]; irow++) { old_offset = isub * multi_image->size[0] + (jsub * multi_image->size[1] + irow) * multi_image->big[0]; old_offset *= multi_image->pixel_size; new_offset = (irow * multi_image->size[0]) * multi_image->pixel_size; for (ibyte=0; ibyte < nbyte; ibyte++) { new[new_offset + ibyte] = old[old_offset + ibyte]; } } /* Reset the byte order and VR encoding. This will be modified on each send according to what the connection needs. */ acr_set_element_byte_order(multi_image->small_image, acr_get_element_byte_order(multi_image->big_image)); acr_set_element_vr_encoding(multi_image->small_image, acr_get_element_vr_encoding(multi_image->big_image)); /* Update the index */ acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) (iimage + multi_image->first_image)); /* Update the position */ distance = 0.0; for (idim=0; idim < 3; idim++) { position[idim] = multi_image->position[idim] + (double) iimage * multi_image->step[idim]; distance += position[idim] * multi_image->normal[idim]; } (void) sprintf(string, "%.15g\\%.15g\\%.15g", position[0], position[1], position[2]); acr_insert_string(&group_list, SPI_Image_position, string); acr_insert_numeric(&group_list, SPI_Image_distance, distance); update_coordinate_info(group_list); } /* ----------------------------- MNI Header ----------------------------------- @NAME : multi_image_cleanup @INPUT : group_list - group list as read in from file multi_image - structure containing information to be used for modifying group list in loop over images @OUTPUT : multi_image @RETURNS : (nothing) @DESCRIPTION: Routine to clean up after doing multi-image stuff. @METHOD : @GLOBALS : @CALLS : @CREATED : November 6, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void multi_image_cleanup(Acr_Group group_list, Multi_Image *multi_image) /* ARGSUSED */ { if (!multi_image->packed) return; acr_delete_element(multi_image->big_image); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/valid_element_ids0000644000175000000620000001015012574624760025265 0ustar stevestaff0x0008 0x0000 0x0008 0x0008 0x0008 0x0016 0x0008 0x0018 0x0008 0x0020 0x0008 0x0022 0x0008 0x0023 0x0008 0x0030 0x0008 0x0032 0x0008 0x0033 0x0008 0x0040 0x0008 0x0041 0x0008 0x0050 0x0008 0x0060 0x0008 0x0070 0x0008 0x0080 0x0008 0x0090 0x0008 0x1010 0x0008 0x1080 0x0008 0x1090 0x0009 0x0000 0x0009 0x0010 0x0009 0x0012 0x0009 0x0013 0x0009 0x1010 0x0009 0x1015 0x0009 0x1040 0x0009 0x1041 0x0009 0x1210 0x0009 0x1226 0x0009 0x1227 0x0009 0x1316 0x0009 0x1320 0x0010 0x0000 0x0010 0x0010 0x0010 0x0020 0x0010 0x0030 0x0010 0x0040 0x0010 0x1010 0x0010 0x1030 0x0011 0x0000 0x0011 0x0010 0x0011 0x0011 0x0011 0x110a 0x0011 0x1110 0x0011 0x1111 0x0011 0x1122 0x0011 0x1123 0x0018 0x0000 0x0018 0x0010 0x0018 0x0020 0x0018 0x0021 0x0018 0x0022 0x0018 0x0023 0x0018 0x0024 0x0018 0x0025 0x0018 0x0050 0x0018 0x0080 0x0018 0x0081 0x0018 0x0083 0x0018 0x0084 0x0018 0x0085 0x0018 0x0086 0x0018 0x0087 0x0018 0x0088 0x0018 0x0090 0x0018 0x0091 0x0018 0x0093 0x0018 0x0094 0x0018 0x1000 0x0018 0x1020 0x0018 0x1200 0x0018 0x1201 0x0018 0x1250 0x0018 0x1314 0x0018 0x1316 0x0018 0x5100 0x0019 0x0000 0x0019 0x0010 0x0019 0x0012 0x0019 0x0015 0x0019 0x1010 0x0019 0x1020 0x0019 0x1030 0x0019 0x1050 0x0019 0x1060 0x0019 0x1210 0x0019 0x1211 0x0019 0x1212 0x0019 0x1213 0x0019 0x1214 0x0019 0x1220 0x0019 0x1221 0x0019 0x1226 0x0019 0x1228 0x0019 0x1230 0x0019 0x1231 0x0019 0x1240 0x0019 0x1241 0x0019 0x1242 0x0019 0x1245 0x0019 0x1246 0x0019 0x1250 0x0019 0x1260 0x0019 0x1270 0x0019 0x1281 0x0019 0x1282 0x0019 0x1283 0x0019 0x1285 0x0019 0x1287 0x0019 0x1290 0x0019 0x1293 0x0019 0x1294 0x0019 0x1297 0x0019 0x1298 0x0019 0x12a0 0x0019 0x12a1 0x0019 0x1412 0x0019 0x1414 0x0019 0x1416 0x0019 0x1420 0x0019 0x1421 0x0019 0x1422 0x0019 0x1424 0x0019 0x1426 0x0019 0x1450 0x0019 0x1451 0x0019 0x1452 0x0019 0x1454 0x0019 0x1455 0x0019 0x1456 0x0019 0x1460 0x0019 0x1462 0x0019 0x1470 0x0019 0x1471 0x0019 0x1472 0x0019 0x1480 0x0019 0x1482 0x0019 0x1490 0x0019 0x14a0 0x0019 0x14a2 0x0019 0x14a5 0x0019 0x14a6 0x0019 0x14b0 0x0019 0x14c1 0x0019 0x14c2 0x0019 0x14c3 0x0019 0x14c4 0x0019 0x14c5 0x0019 0x14d1 0x0019 0x14d2 0x0019 0x14d3 0x0019 0x14d4 0x0019 0x14d5 0x0019 0x14d6 0x0019 0x14d7 0x0019 0x14d8 0x0019 0x14d9 0x0019 0x14da 0x0019 0x1510 0x0019 0x1511 0x0019 0x1512 0x0019 0x1513 0x0019 0x1514 0x0020 0x0000 0x0020 0x000d 0x0020 0x000e 0x0020 0x0010 0x0020 0x0011 0x0020 0x0012 0x0020 0x0013 0x0020 0x0030 0x0020 0x0032 0x0020 0x0035 0x0020 0x0037 0x0020 0x0050 0x0020 0x0052 0x0020 0x0070 0x0020 0x0080 0x0020 0x1001 0x0020 0x1020 0x0020 0x1040 0x0020 0x1041 0x0020 0x3100 0x0020 0x3401 0x0020 0x3402 0x0020 0x3403 0x0020 0x3404 0x0020 0x3405 0x0020 0x3406 0x0020 0x5000 0x0020 0x5002 0x0021 0x0000 0x0021 0x0010 0x0021 0x0011 0x0021 0x0013 0x0021 0x0023 0x0021 0x1010 0x0021 0x1011 0x0021 0x1020 0x0021 0x1120 0x0021 0x1122 0x0021 0x1124 0x0021 0x1126 0x0021 0x1130 0x0021 0x1132 0x0021 0x1160 0x0021 0x1161 0x0021 0x1163 0x0021 0x1165 0x0021 0x116a 0x0021 0x116b 0x0021 0x1170 0x0021 0x1171 0x0021 0x1180 0x0021 0x1182 0x0021 0x1322 0x0021 0x1324 0x0021 0x1330 0x0021 0x1331 0x0021 0x1334 0x0021 0x1336 0x0021 0x1339 0x0021 0x1340 0x0021 0x1341 0x0021 0x1342 0x0021 0x1343 0x0021 0x1344 0x0021 0x134f 0x0021 0x1356 0x0021 0x1370 0x0021 0x2300 0x0021 0x2301 0x0021 0x2302 0x0021 0x2303 0x0021 0x2304 0x0021 0x2305 0x0021 0x2306 0x0021 0x2307 0x0021 0x2308 0x0021 0x2309 0x0021 0x2310 0x0021 0x2311 0x0021 0x2312 0x0021 0x2313 0x0021 0x2314 0x0021 0x2315 0x0021 0x2316 0x0021 0x2317 0x0021 0x2318 0x0021 0x2331 0x0028 0x0000 0x0028 0x0002 0x0028 0x0004 0x0028 0x0005 0x0028 0x0010 0x0028 0x0011 0x0028 0x0030 0x0028 0x0040 0x0028 0x0050 0x0028 0x0060 0x0028 0x0100 0x0028 0x0101 0x0028 0x0102 0x0028 0x0103 0x0028 0x0200 0x0028 0x1050 0x0028 0x1051 0x0028 0x1052 0x0028 0x1053 0x0029 0x0000 0x0029 0x0011 0x0029 0x1110 0x0029 0x1120 0x0029 0x1152 0x0051 0x0000 0x0051 0x0010 0x0051 0x1010 0x7fe0 0x0000 0x7fe0 0x0010 minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicomserver.h0000644000175000000620000001055712574624760024401 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomserver.h @DESCRIPTION: Header file that includes things needed for dicomserver. @METHOD : @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: dicomserver.h,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.5 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.4 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.3 2000/12/14 21:17:34 rhoge * cleanup of log messages * * Revision 1.2 2000/12/14 21:15:58 rhoge * added ACQ and MEAS constants as flags for type of (non-standard) * dynamic scan looping * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * added num_slices_nominal to Data_Object_Info * (for support of acquisition loop scans) * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include #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 #include #include #include #include #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #define FILE_ALLOC_INCREMENT 10 /* Connection timeout length in seconds */ /* Changed to 10 min instead of 5 by Leili */ #define CONNECTION_TIMEOUT (60*10) /* Time to sleep between image reads when a child process is running. This prevents the server from outrunning its children. */ #define SERVER_SLEEP_TIME 3 /* Define logging constants */ #define NO_LOGGING 0 #define LOW_LOGGING 1 #define HIGH_LOGGING 2 /* added by rhoge for ACQ and MEAS loop handling */ typedef enum { NONE = 0 , ACQ , MEAS } Loop_Type; /* supported file types */ typedef enum { UNDEF, IMA, N3DCM, N4DCM } File_Type; /* File containing defaults for dicomserver */ #define OUTPUT_DEFAULT_FILE_DIR "/usr/local/lib" #define OUTPUT_DEFAULT_FILE_PREFIX "dicomserver." /* System log file (set to NULL for no logging of error) */ #define SYSTEM_LOG "/dev/log" /* Type for carrying around object information */ typedef struct { int file_index; char *file_name; double study_id; // yyyymmdd.hhmmss int study_date; int study_time; int scanner_serialno; int acq_id; int rec_num; int image_type; int num_echoes; int echo_number; int num_dyn_scans; int dyn_scan_number; int global_image_number; int num_slices_nominal; int slice_number; int acq_rows; int acq_cols; int rec_rows; int rec_cols; int num_mosaic_rows; int num_mosaic_cols; int num_slices_in_file; char sequence_name[256]; char protocol_name[256]; } Data_Object_Info; /* Define macro for array size */ #define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) #include #include minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/minc_file.c0000644000175000000620000011266712574624760024004 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_file.c @DESCRIPTION: Code to do minc file handling. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: minc_file.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.12 2002/04/29 15:24:53 rhoge * removed (mode_t) cast in minc_file - would not build on SGI's * * Revision 1.11 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.10 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.9 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.8 2002/03/19 22:10:16 rhoge * removed time sorting for N4DCM mosaics - time is random for mosaics * * Revision 1.7 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.6 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.5 2001/02/26 22:22:37 rhoge * added scanner serial number to minc file naming * * Revision 1.4 2001/02/26 13:38:22 rhoge * made `existing directory' warning conditional on logging * * Revision 1.3 2000/12/15 01:04:46 rhoge * make sure acquisition_id (series no) is 6 digit hhmmss string for meas loop * * Revision 1.2 2000/12/14 21:19:22 rhoge * added code to compute time spacing if measurement loop dynamic * scanning has been detected * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; /* Global for minc history (sorry, but it was kludged in afterwards) */ char *minc_history = NULL; int Anon; char IdStr[512]; int UserIdStr; char Name[512]; int UserName; /* Define mri dimension names */ static char *mri_dim_names[] = { NULL, "echo_time", MItime, "phase_number", "chemical_shift", NULL}; /* Macros */ #define STRLEN(s) ((int) strlen(s)) /* ----------------------------- MNI Header ----------------------------------- @NAME : create_minc_file @INPUT : minc_file - name of file to create. If NULL, a name is generated internally. clobber - if TRUE, any existing file will be overwritten. general_info - information for creating the file. file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : id of image conversion variable (MI_ERROR in case of error). @DESCRIPTION: Routine to create the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : rhoge - modified to create directory for session ---------------------------------------------------------------------------- */ public int create_minc_file(char *minc_file, int clobber, General_Info *general_info, char *file_prefix, char **output_file_name, Loop_Type loop_type) { static char temp_name[256]; char patient_name[256]; char patient_id[256]; char scanner_model[256]; char serial_no[256]; char reg_time[256]; char temp_str[256]; int ix; char *filename; int minc_clobber; int mincid, icvid; Mri_Index imri; char scan_label[MRI_NDIMS][20]; /* added by rhoge for study directory, new naming conventions: */ char full_path[256]; /* Prefixes for creating file name */ static char *scan_prefix[MRI_NDIMS] = {"sl", "e", "d", "p", "cs"}; /* Turn off fatal errors */ ncopts = NCOPTS_DEFAULT; /* Create the file name if needed */ if (minc_file != NULL) { filename = minc_file; } else { /* Get patient name */ /******************************************/ /* Changed by Leili from "string_to_initials" to "string_to_filename" */ /* based on people's request at MNI */ if (UserName) { strcpy(patient_name,Name); } else { string_to_filename(general_info->patient.name, patient_name, sizeof(patient_name)); } if (STRLEN(patient_name) == 0) { (void) strcpy(patient_name, "no_name"); } /*****************************************/ /* Commented out by Leili,based on the request of people at MNI*/ /* Get patient ID */ //if (UserIdStr) { // strcpy(patient_id,IdStr); //} else { // string_to_initials(general_info->patient.identification, patient_id, // sizeof(patient_id)); //} //if (STRLEN(patient_id) == 0) { // (void) strcpy(patient_id, "no_id"); //} /********************************************/ /* Commented out by Leili,based on the request of people at MNI*/ /* Get Scanner model */ //string_to_filename(general_info->study.model, // scanner_model, // sizeof(scanner_model)); //if (STRLEN(scanner_model) == 0) { // (void) strcpy(scanner_model, "no_scanner"); //} /* Get Scanner serial number */ //string_to_filename(general_info->study.serial_no, // serial_no, // sizeof(serial_no)); //if (STRLEN(serial_no) == 0) { // (void) strcpy(serial_no, "no_serial_no"); //} /* get rid of magnetom prefix, if present */ //if (!strncmp(scanner_model,"magnetom_",9)) { // for (ix=0; ixpatient.reg_time, temp_str, sizeof(temp_str)); /* truncate to first 6 chars (hhmmss) */ strncpy(reg_time, temp_str, 6); if (STRLEN(reg_time) == 0) { (void) strcpy(reg_time, "no_time"); } reg_time[6]='\0'; /* terminate with null (strncpy does not) */ /* Get strings for echo number, etc. */ for (imri=0; imri < MRI_NDIMS; imri++) { if ((general_info->size[imri] < general_info->total_size[imri]) && (general_info->size[imri] == 1)) { (void) sprintf(scan_label[imri], "%s%d", scan_prefix[imri], general_info->default_index[imri]); } else { (void) strcpy(scan_label[imri], ""); } } /* rhoge: add session directory to prefix */ (void) strcpy(full_path,file_prefix); /* Changed by leili, omitted the scanner info, changed - to _ , and get rid off the patients ID */ (void) sprintf(temp_name, "%s_%s_%s/", patient_name, //patient_id, //scanner_model, //serial_no, general_info->patient.reg_date, reg_time); strcat(full_path,temp_name); /* if measurement loop, make sure that acquisition_id is a 6 digit (hhmmss) string with leading zero if needed */ if (loop_type == MEAS) { (void) sprintf(general_info->study.acquisition_id, "%06d", general_info->acq_id); } /* Create file name */ /* changed by leili, omitted the scanner info, changed - to _ */ (void) sprintf(temp_name, "%s%s_%s_%s_%s%s%s%s%s%s_mri.mnc", full_path, patient_name, //patient_id, //scanner_model, //serial_no, general_info->patient.reg_date, reg_time, general_info->study.acquisition_id, scan_label[SLICE], scan_label[ECHO], scan_label[TIME], scan_label[PHASE], scan_label[CHEM_SHIFT]); filename = temp_name; if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr,"\nminc file name: %s\n",filename); fprintf(stderr,"\nfile prefix: %s\n",full_path); fprintf(stderr,"\npatient_name: %s\n",patient_name); fprintf(stderr,"\nstudy_id: %s\n", general_info->study.study_id); fprintf(stderr,"\nacquisition_id: %s\n", general_info->study.acquisition_id); fprintf(stderr,"\nRegistration date: %s\n", general_info->patient.reg_date); fprintf(stderr,"\nRegistration time: %s\n", general_info->patient.reg_time); } } /* create the session directory if none exists */ if (mkdir(full_path, 0777) && (Do_logging > HIGH_LOGGING)) { (void) fprintf(stderr, "Directory %s exists...\n",full_path); } /* Set output file name */ if (output_file_name != NULL) *output_file_name = filename; /* Set the clobber value */ if (clobber) minc_clobber = NC_CLOBBER; else minc_clobber = NC_NOCLOBBER; /* Create the file */ mincid = micreate(filename, minc_clobber); if (mincid == MI_ERROR) return MI_ERROR; /* Set up variables */ setup_minc_variables(mincid, general_info,loop_type); /* Put the file in data mode */ (void) ncsetfill(mincid, NC_NOFILL); if (ncendef(mincid) == MI_ERROR) { return MI_ERROR; } /* Create the icv */ icvid = miicv_create(); /* Set the type and range */ (void) miicv_setint(icvid, MI_ICV_TYPE, NC_SHORT); if (general_info->is_signed) (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_SIGNED); else (void) miicv_setstr(icvid, MI_ICV_SIGN, MI_UNSIGNED); (void) miicv_setdbl(icvid, MI_ICV_VALID_MIN, general_info->pixel_min); (void) miicv_setdbl(icvid, MI_ICV_VALID_MAX, general_info->pixel_max); /* Attach the icv */ (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_minc_variables @INPUT : mincid general_info @OUTPUT : general_info @RETURNS : (nothing) @DESCRIPTION: Routine to setup minc variables. @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void setup_minc_variables(int mincid, General_Info *general_info, Loop_Type loop_type) { Mri_Index imri; Volume_Index ivol; World_Index iworld; int ndims; int dim[MAX_VAR_DIMS]; long dimsize; char *dimname; int varid, imgid, dicomvar; double valid_range[2]; char name[MAX_NC_NAME]; int index; int regular; double separation, diff; Acr_Group cur_group; Acr_Element cur_element; int length; char *data; nc_type datatype; int is_char; int ich; /* stuff added by rhoge */ double sum; double avg; /* Define the spatial dimension names */ static char *spatial_dimnames[WORLD_NDIMS] = {MIxspace, MIyspace, MIzspace}; /* Create the dimensions from slowest to fastest */ ndims=0; /* Create the non-spatial dimensions (from slowest to fastest) */ for (imri=MRI_NDIMS-1; (int) imri > SLICE; imri--) { /* for the TIME dimension, check if we have acquisition-loop dynamic scan OR a `corrected' dynamic scan */ if ( (imri==TIME) && ((loop_type!=NONE) || (general_info->acq.num_dyn_scans>1)) ) { /* for Siemens scans using the signal averaging loop for multiple time points we use the TR as the time step */ dimsize = general_info->size[TIME]; if (general_info->size[TIME] > 1) { dimname = mri_dim_names[TIME]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); (void) miattputstr(mincid, varid, MIunits, "s"); if (loop_type == MEAS) { /* if Meas loop, time step is not equal to TR, and frames should have time values (rhoge) */ sum = 0.0; for (index=1; index < general_info->size[TIME]; index++) { sum += general_info->coordinates[TIME][1]- general_info->coordinates[TIME][0]; } /* compute mean */ avg = sum/general_info->size[TIME]; (void) miattputdbl(mincid, varid, MIstep,avg); /* check for uniformity of spacing */ regular = TRUE; for (index=1; index < general_info->size[TIME]; index++) { diff = general_info->coordinates[TIME][1]- general_info->coordinates[TIME][0] - avg; if (diff < 0.0) diff = -diff; if (separation != 0.0) diff /= avg; if (diff > COORDINATE_EPSILON) { regular = FALSE; break; } } if (regular) (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); else (void) miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); } else { /* assume ACQ loop and use TR for time step */ (void) miattputdbl(mincid, varid, MIstep, general_info->acq.rep_time); } (void) miattputdbl(mincid, varid, MIstart,0); general_info->image_index[TIME] = ndims; ndims++; } } else { /* NORMAL CODE */ dimsize = general_info->size[imri]; if (general_info->size[imri] > 1) { dimname = mri_dim_names[imri]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (imri == TIME) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); (void) miattputstr(mincid, varid, MIunits, "s"); } else if (imri == ECHO) { varid = ncvardef(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); (void) miattputstr(mincid, varid, MIvartype, MI_DIMENSION); (void) miattputstr(mincid, varid, MIspacing, MI_IRREGULAR); (void) miattputstr(mincid, varid, MIunits, "s"); } general_info->image_index[imri] = ndims; ndims++; } } } /* Next the spatial dimensions */ for (ivol=0; ivol < VOL_NDIMS; ivol++) { switch (ivol) { case VSLICE: dimsize = general_info->size[SLICE]; iworld = general_info->slice_world; break; case VROW: dimsize = general_info->nrows; iworld = general_info->row_world; break; case VCOLUMN: dimsize = general_info->ncolumns; iworld = general_info->column_world; break; } dimname = spatial_dimnames[iworld]; dim[ndims] = ncdimdef(mincid, dimname, dimsize); if (ivol == VSLICE) { varid = micreate_std_variable(mincid, dimname, NC_DOUBLE, 1, &dim[ndims]); /* Check for regular slices */ regular = TRUE; separation = general_info->step[general_info->slice_world]; for (index=1; index < general_info->size[SLICE]; index++) { diff = general_info->coordinates[SLICE][index] - general_info->coordinates[SLICE][index-1] - separation; if (diff < 0.0) diff = -diff; if (separation != 0.0) diff /= separation; if (diff > COORDINATE_EPSILON) { regular = FALSE; break; } } if (regular) (void) miattputstr(mincid, varid, MIspacing, MI_REGULAR); } else varid = micreate_std_variable(mincid, dimname, NC_LONG, 0, NULL); (void) miattputdbl(mincid, varid, MIstep, general_info->step[iworld]); (void) miattputdbl(mincid, varid, MIstart, general_info->start[iworld]); (void) miattputstr(mincid, varid, MIspacetype, MI_NATIVE); (void) ncattput(mincid, varid, MIdirection_cosines, NC_DOUBLE, WORLD_NDIMS, general_info->dircos[iworld]); if (ivol == VSLICE) { general_info->image_index[SLICE] = ndims; } ndims++; } /* Set up image variable */ imgid = micreate_std_variable(mincid, MIimage, general_info->datatype, ndims, dim); if (general_info->is_signed) (void) miattputstr(mincid, imgid, MIsigntype, MI_SIGNED); else (void) miattputstr(mincid, imgid, MIsigntype, MI_UNSIGNED); valid_range[0] = general_info->pixel_min; valid_range[1] = general_info->pixel_max; (void) ncattput(mincid, imgid, MIvalid_range, NC_DOUBLE, 2, valid_range); (void) miattputstr(mincid, imgid, MIcomplete, MI_FALSE); /* Create image max and min variables */ varid = micreate_std_variable(mincid, MIimagemin, NC_DOUBLE, ndims-2, dim); if (STRLEN(general_info->units) > 0) (void) miattputstr(mincid, varid, MIunits, general_info->units); varid = micreate_std_variable(mincid, MIimagemax, NC_DOUBLE, ndims-2, dim); if (STRLEN(general_info->units) > 0) (void) miattputstr(mincid, varid, MIunits, general_info->units); /* Create the patient variable */ varid = micreate_group_variable(mincid, MIpatient); if (STRLEN(general_info->patient.name) > 0) if (Anon) { (void) miattputstr(mincid, varid, MIfull_name, "anonymous"); } else { (void) miattputstr(mincid, varid, MIfull_name, general_info->patient.name); } if (STRLEN(general_info->patient.identification) > 0) (void) miattputstr(mincid, varid, MIidentification, general_info->patient.identification); if (STRLEN(general_info->patient.birth_date) > 0) (void) miattputstr(mincid, varid, MIbirthdate, general_info->patient.birth_date); if (STRLEN(general_info->patient.age) > 0) (void) miattputstr(mincid, varid, "age", general_info->patient.age); if (STRLEN(general_info->patient.sex) > 0) (void) miattputstr(mincid, varid, MIsex, general_info->patient.sex); if (general_info->patient.weight != -DBL_MAX) (void) miattputdbl(mincid, varid, MIweight, general_info->patient.weight); /* Create the study variable */ varid = micreate_group_variable(mincid, MIstudy); /* rhoge: fixed date/time to reflect study */ if (STRLEN(general_info->patient.reg_date) > 0) (void) miattputstr(mincid, varid, "start_date", general_info->patient.reg_date); if (STRLEN(general_info->patient.reg_time) > 0) (void) miattputstr(mincid, varid, "start_time", general_info->patient.reg_time); if (STRLEN(general_info->study.modality) > 0) (void) miattputstr(mincid, varid, MImodality, general_info->study.modality); if (STRLEN(general_info->study.manufacturer) > 0) (void) miattputstr(mincid, varid, "manufacturer", general_info->study.manufacturer); if (STRLEN(general_info->study.model) > 0) (void) miattputstr(mincid, varid, "model", general_info->study.model); if (general_info->study.field_value != -DBL_MAX) (void) miattputdbl(mincid, varid, "field_value", general_info->study.field_value); if (STRLEN(general_info->study.software_version) > 0) (void) miattputstr(mincid, varid, "software_version", general_info->study.software_version); if (STRLEN(general_info->study.serial_no) > 0) (void) miattputstr(mincid, varid, "serial_no", general_info->study.serial_no); if (STRLEN(general_info->study.calibration_date) > 0) (void) miattputstr(mincid, varid, "calibration_date", general_info->study.calibration_date); if (STRLEN(general_info->study.institution) > 0) (void) miattputstr(mincid, varid, MIinstitution, general_info->study.institution); if (STRLEN(general_info->study.station_id) > 0) (void) miattputstr(mincid, varid, MIstation_id, general_info->study.station_id); if (STRLEN(general_info->study.referring_physician) > 0) (void) miattputstr(mincid, varid, MIreferring_physician, general_info->study.referring_physician); if (STRLEN(general_info->study.performing_physician) > 0) (void) miattputstr(mincid, varid, "performing_physician", general_info->study.referring_physician); if (STRLEN(general_info->study.operator) > 0) (void) miattputstr(mincid, varid, "operator", general_info->study.operator); if (STRLEN(general_info->study.procedure) > 0) (void) miattputstr(mincid, varid, MIprocedure, general_info->study.procedure); if (STRLEN(general_info->study.study_id) > 0) (void) miattputstr(mincid, varid, MIstudy_id, general_info->study.study_id); /* Create acquisition variable */ varid = micreate_group_variable(mincid, MIacquisition); if (STRLEN(general_info->study.acquisition_id) > 0) (void) miattputstr(mincid, varid, "acquisition_id", general_info->study.acquisition_id); if (STRLEN(general_info->study.start_time) > 0) (void) miattputstr(mincid, varid, MIstart_time, general_info->study.start_time); if (STRLEN(general_info->acq.scan_seq) > 0) (void) miattputstr(mincid, varid, MIscanning_sequence, general_info->acq.scan_seq); if (STRLEN(general_info->acq.seq_owner) > 0) (void) miattputstr(mincid, varid, "seq_owner", general_info->acq.seq_owner); if (STRLEN(general_info->acq.seq_descr) > 0) (void) miattputstr(mincid, varid, "seq_description", general_info->acq.seq_descr); if (STRLEN(general_info->acq.protocol_name) > 0) (void) miattputstr(mincid, varid, "protocol_name", general_info->acq.protocol_name); if (STRLEN(general_info->acq.receive_coil) > 0) (void) miattputstr(mincid, varid, "receive_coil", general_info->acq.receive_coil); if (STRLEN(general_info->acq.transmit_coil) > 0) (void) miattputstr(mincid, varid, "transmit_coil", general_info->acq.transmit_coil); if (general_info->acq.rep_time != -DBL_MAX) (void) miattputdbl(mincid, varid, MIrepetition_time, general_info->acq.rep_time); if ((general_info->acq.echo_time != -DBL_MAX) && (general_info->size[ECHO] <= 1)) (void) miattputdbl(mincid, varid, MIecho_time, general_info->acq.echo_time); if (general_info->acq.echo_number != -DBL_MAX) (void) miattputdbl(mincid, varid, "echo_number", general_info->acq.echo_number); if (general_info->acq.inv_time != -DBL_MAX) (void) miattputdbl(mincid, varid, MIinversion_time, general_info->acq.inv_time); if (general_info->acq.flip_angle != -DBL_MAX) (void) miattputdbl(mincid, varid, "flip_angle", general_info->acq.flip_angle); if (general_info->acq.slice_thickness != -DBL_MAX) (void) miattputdbl(mincid, varid, "slice_thickness", general_info->acq.slice_thickness); if (general_info->acq.num_slices != -DBL_MAX) (void) miattputdbl(mincid, varid, "num_slices", general_info->acq.num_slices); if (general_info->acq.b_value != -DBL_MAX) (void) miattputdbl(mincid, varid, "b_value", general_info->acq.b_value); /* add number of dynamic scans (rhoge) */ /* this will be relevant if we are receiving siemens scans that have been `cleaned up' (and hence have the correct number of dynamic scans inserted) */ if (general_info->acq.num_dyn_scans != -DBL_MAX) (void) miattputdbl(mincid, varid, "num_dyn_scans", general_info->acq.num_dyn_scans); if (general_info->acq.num_avg != -DBL_MAX) (void) miattputdbl(mincid, varid, MInum_averages, general_info->acq.num_avg); if (general_info->acq.scan_dur != -DBL_MAX) (void) miattputdbl(mincid, varid, "scan_duration", general_info->acq.scan_dur); if (general_info->acq.ky_lines != -DBL_MAX) (void) miattputdbl(mincid, varid, "ky_lines", general_info->acq.ky_lines); if (general_info->acq.kymax_ix != -DBL_MAX) (void) miattputdbl(mincid, varid, "kymax_ix", general_info->acq.kymax_ix); if (general_info->acq.kymin_ix != -DBL_MAX) (void) miattputdbl(mincid, varid, "kymin_ix", general_info->acq.kymin_ix); if (general_info->acq.kz_lines != -DBL_MAX) (void) miattputdbl(mincid, varid, "kz_lines", general_info->acq.kz_lines); if (general_info->acq.dummy_scans != -DBL_MAX) (void) miattputdbl(mincid, varid, "dummy_excitations", general_info->acq.dummy_scans); if (general_info->acq.imaging_freq != -DBL_MAX) (void) miattputdbl(mincid, varid, MIimaging_frequency, general_info->acq.imaging_freq); if (STRLEN(general_info->acq.imaged_nucl) > 0) (void) miattputstr(mincid, varid, MIimaged_nucleus, general_info->acq.imaged_nucl); if (general_info->acq.adc_voltage != -DBL_MAX) (void) miattputdbl(mincid, varid, "adc_voltage", general_info->acq.adc_voltage); if (general_info->acq.adc_offset != -DBL_MAX) (void) miattputdbl(mincid, varid, "adc_offset", general_info->acq.adc_offset); if (general_info->acq.transmit_ampl != -DBL_MAX) (void) miattputdbl(mincid, varid, "transmit_ampl", general_info->acq.transmit_ampl); if (general_info->acq.rec_amp_gain != -DBL_MAX) (void) miattputdbl(mincid, varid, "rec_amp_gain", general_info->acq.rec_amp_gain); if (general_info->acq.rec_preamp_gain != -DBL_MAX) (void) miattputdbl(mincid, varid, "rec_preamp_gain", general_info->acq.rec_preamp_gain); if (general_info->acq.win_center != -DBL_MAX) (void) miattputdbl(mincid, varid, "window_center", general_info->acq.win_center); if (general_info->acq.win_width != -DBL_MAX) (void) miattputdbl(mincid, varid, "window_width", general_info->acq.win_width); if (general_info->acq.gy_ampl != -DBL_MAX) (void) miattputdbl(mincid, varid, "gy_ampl", general_info->acq.gy_ampl); if (general_info->acq.gx_ampl != -DBL_MAX) (void) miattputdbl(mincid, varid, "gx_ampl", general_info->acq.gx_ampl); if (general_info->acq.gz_ampl != -DBL_MAX) (void) miattputdbl(mincid, varid, "gz_ampl", general_info->acq.gz_ampl); if (general_info->acq.num_phase_enc_steps != -DBL_MAX) (void) miattputdbl(mincid, varid, "num_phase_enc_steps", general_info->acq.num_phase_enc_steps); if (general_info->acq.percent_sampling != -DBL_MAX) (void) miattputdbl(mincid, varid, "percent_sampling", general_info->acq.percent_sampling); if (general_info->acq.percent_phase_fov != -DBL_MAX) (void) miattputdbl(mincid, varid, "percent_phase_fov", general_info->acq.percent_phase_fov); if (general_info->acq.pixel_bandwidth != -DBL_MAX) (void) miattputdbl(mincid, varid, "pixel_bandwidth", general_info->acq.pixel_bandwidth); if (STRLEN(general_info->acq.phase_enc_dir) > 0) (void) miattputstr(mincid, varid, "phase_enc_dir", general_info->acq.phase_enc_dir); if (general_info->acq.sar != -DBL_MAX) (void) miattputdbl(mincid, varid, "SAR", general_info->acq.sar); if (STRLEN(general_info->acq.mr_acq_type) > 0) (void) miattputstr(mincid, varid, "mr_acq_type", general_info->acq.mr_acq_type); if (STRLEN(general_info->acq.image_type) > 0) (void) miattputstr(mincid, varid, "image_type", general_info->acq.image_type); if (STRLEN(general_info->acq.comments) > 0) (void) miattputstr(mincid, varid, MIcomments, general_info->acq.comments); // this is Siemens Numaris 4 specific! if (STRLEN(general_info->acq.MrProt) > 0) (void) miattputstr(mincid, varid, "MrProt_dump", general_info->acq.MrProt); /* Create the dicom info variable */ varid = ncvardef(mincid, "dicominfo", NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI DICOM information variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), varid); if (STRLEN(general_info->image_type_string) > 0) (void) miattputstr(mincid, varid, "image_type", general_info->image_type_string); (void) miattputdbl(mincid, varid, "window_min", general_info->window_min); (void) miattputdbl(mincid, varid, "window_max", general_info->window_max); /* Put group info in header */ cur_group = general_info->group_list; dicomvar = ncvardef(mincid, DICOM_ROOT_VAR, NC_LONG, 0, NULL); (void) miattputstr(mincid, dicomvar, MIvartype, MI_GROUP); (void) miattputstr(mincid, dicomvar, MIvarid, "MNI DICOM variable"); (void) miadd_child(mincid, ncvarid(mincid, MIrootvariable), dicomvar); while (cur_group != NULL) { /* Create variable for group */ (void) sprintf(name, "dicom_0x%04x", acr_get_group_group(cur_group)); varid = ncvardef(mincid, name, NC_LONG, 0, NULL); (void) miattputstr(mincid, varid, MIvartype, MI_GROUP); (void) miattputstr(mincid, varid, MIvarid, "MNI DICOM variable"); (void) miadd_child(mincid, dicomvar, varid); /* Loop through elements of group */ cur_element = acr_get_group_element_list(cur_group); while (cur_element != NULL) { (void) sprintf(name, "el_0x%04x", acr_get_element_element(cur_element)); is_char = TRUE; length = acr_get_element_length(cur_element); data = acr_get_element_data(cur_element); for (ich=0; ich < length; ich++) { if (!isprint((int) data[ich])) { is_char = FALSE; break; } } if (is_char) datatype = NC_CHAR; else datatype = NC_BYTE; ncattput(mincid, varid, name, datatype, length, data); cur_element = acr_get_element_next(cur_element); } cur_group = acr_get_group_next(cur_group); } /* Create the history attribute */ if (minc_history != NULL) { (void) miattputstr(mincid, NC_GLOBAL, MIhistory, minc_history); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : save_minc_image @INPUT : icvid general_info file_info image @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to save the image in the minc file @METHOD : @GLOBALS : CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void save_minc_image(int icvid, General_Info *general_info, File_Info *file_info, Image_Data *image) { int mincid, imgid; long start[MAX_VAR_DIMS], count[MAX_VAR_DIMS]; int file_index, array_index; int idim; Mri_Index imri; char *dimname; unsigned short pvalue, pmax, pmin; double dvalue, maximum, minimum, scale, offset; long ipix, imagepix; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); (void) miicv_inqint(icvid, MI_ICV_VARID, &imgid); /* Create start and count variables */ idim = 0; for (imri=MRI_NDIMS-1; (int) imri >= 0; imri--) { if (general_info->image_index[imri] >= 0) { file_index = general_info->image_index[imri]; if (general_info->size[imri] > 1) { array_index = search_list(file_info->index[imri], general_info->indices[imri], general_info->size[imri], general_info->search_start[imri]); if (array_index < 0) array_index = 0; general_info->search_start[imri] = array_index; } else { array_index = 0; } start[file_index] = array_index; count[file_index] = 1; idim++; } } start[idim] = 0; start[idim+1] = 0; count[idim] = general_info->nrows; count[idim+1] = general_info->ncolumns; /* Write out slice position */ switch (general_info->slice_world) { case XCOORD: dimname = MIxspace; break; case YCOORD: dimname = MIyspace; break; case ZCOORD: dimname = MIzspace; break; default: dimname = MIzspace; } (void) mivarput1(mincid, ncvarid(mincid, dimname), &start[general_info->image_index[SLICE]], NC_DOUBLE, NULL, &file_info->coordinate[SLICE]); /* Write out time of slice, if needed */ if (general_info->size[TIME] > 1) { (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[TIME]), &start[general_info->image_index[TIME]], NC_DOUBLE, NULL, &file_info->coordinate[TIME]); } /* Write out echo time of slice, if needed */ if (general_info->size[ECHO] > 1) { (void) mivarput1(mincid, ncvarid(mincid, mri_dim_names[ECHO]), &start[general_info->image_index[ECHO]], NC_DOUBLE, NULL, &file_info->coordinate[ECHO]); } /* Search image for max and min */ imagepix = general_info->nrows * general_info->ncolumns; pmax = 0; pmin = USHRT_MAX; for (ipix=0; ipix < imagepix; ipix++) { pvalue = image->data[ipix]; if (pvalue > pmax) pmax = pvalue; if (pvalue < pmin) pmin = pvalue; } /* Re-scale the images */ if (pmax > pmin) scale = (general_info->pixel_max - general_info->pixel_min) / ((double) pmax - (double) pmin); else scale = 0.0; offset = general_info->pixel_min - scale * (double) pmin; for (ipix=0; ipix < imagepix; ipix++) { dvalue = image->data[ipix]; image->data[ipix] = dvalue * scale + offset; } /* Calculate new intensity max and min */ if (general_info->pixel_max > general_info->pixel_min) scale = (file_info->slice_max - file_info->slice_min) / (general_info->pixel_max - general_info->pixel_min); else scale = 0.0; // debugging info for slice intensity scaling // printf("general_info->pixel_max = %10.2f file_info->slice_max = %10.2f pmax = %u\n",general_info->pixel_max,file_info->slice_max,pmax); offset = file_info->slice_min - scale * general_info->pixel_min; minimum = (double) pmin * scale + offset; maximum = (double) pmax * scale + offset; /* Write out the max and min values */ (void) mivarput1(mincid, ncvarid(mincid, MIimagemin), start, NC_DOUBLE, NULL, &minimum); (void) mivarput1(mincid, ncvarid(mincid, MIimagemax), start, NC_DOUBLE, NULL, &maximum); /* Write out the image */ (void) miicv_put(icvid, start, count, image->data); return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_file @INPUT : icvid - value returned by create_minc_file @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to close the minc file. @METHOD : @GLOBALS : CALLS : @CREATED : November 30, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void close_minc_file(int icvid) { int mincid; /* Get the minc file id */ (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); /* Write out the complete attribute */ (void) miattputstr(mincid, ncvarid(mincid, MIimage), MIcomplete, MI_TRUE); /* Close the file */ (void) miclose(mincid); (void) miicv_free(icvid); return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_dicom_to_minc.c0000644000175000000620000012466512574624760026406 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc.c @DESCRIPTION: Code to convert a list of Siemens dicom files to minc format. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_to_minc.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.18 2002/09/26 15:24:33 rhoge * Before was only skipping time sort for multi-slice N4 mosaics. Turns out * this was also causing failure on single-slice scans. * Changed slices>1 to slices>0 so that now N4 dicom scans never get sorted * on their (apparently nonsensical) time value. The if statement should * really be reworked, and should keep an eye on this. Seems like EPI * time series sequences never sort properly on 'time'. * * Revision 1.17 2002/09/25 17:25:43 rhoge * changed public void's to public int's * * Revision 1.16 2002/05/08 19:32:40 rhoge * fixed handling of diffusion scans with separate series for each average * * Revision 1.15 2002/05/01 21:29:34 rhoge * removed MrProt from minc header - encountered files with large strings, * causing seg faults * * Revision 1.14 2002/04/30 12:36:35 rhoge * fixes to handle current (and hopefully final) diffusion sequence * * Revision 1.13 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.12 2002/04/08 03:40:56 rhoge * fixed mosaic extraction for non-square scans and 3D scans. * added some new dicom elements * * Revision 1.11 2002/03/27 19:38:08 rhoge * small comment change * * Revision 1.10 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.9 2002/03/23 13:17:53 rhoge * added support for Bourget network pushed dicom files, cleaned up * file check and read_numa4_dicom vr check/assignment * * Revision 1.8 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.7 2002/03/21 13:31:56 rhoge * updated comments * * Revision 1.6 2002/03/19 22:10:16 rhoge * removed time sorting for N4DCM mosaics - time is random for mosaics * * Revision 1.5 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.4 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.3 2000/12/14 21:37:11 rhoge * log message cleanup * * Revision 1.2 2000/12/14 21:36:22 rhoge * changes to restore measurement loop support that was broken by changes * to provide acquisition loop support * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * -now support Siemens acquisition loop scans with and without correction * on sending side * * Revision 6.1 1999/10/29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; char *pname; File_Type file_type; /* type of input files */ int Fork; int N4_OFFSET; /* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc @INPUT : num_files - number of image files file_list - list of file names minc_file - name of output minc file (NULL means make one up) clobber - if TRUE, then open the output with NC_CLOBBER file_prefix - string providing any directory or prefix for internally generated filename (if it is a directory, then it must contain the last "/") @OUTPUT : output_file_name - returns a pointer to an internal area containing the file name of the created file if minc_file is NULL, or simply a pointer to minc_file. If NULL, then nothing is returned. @RETURNS : EXIT_SUCCESS if no error, EXIT_FAILURE on error. @DESCRIPTION: Routine to convert a list of Siemens dicom files to minc format. @METHOD : @GLOBALS : Do_logging @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int siemens_dicom_to_minc(int num_files, char *file_list[], char *minc_file, int clobber, char *file_prefix, char **output_file_name) { Acr_Group group_list; int max_group; File_Info *file_info; General_Info general_info; General_Info general_info_orig; Image_Data image; int icvid; int ifile; Mri_Index imri; char *out_file_name; int isep; // variables added by rhoge int num_files_expected; int num_avg_nominal; int num_frames_nominal; int idim; int frame; int num_frames; int index; Loop_Type loop_type = NONE; int subimage; int iimage; int num_images_allocated; Acr_Element big_image; Acr_Element small_image; Multi_Image multi_image; /* Allocate space for the file information */ file_info = MALLOC(num_files * sizeof(*file_info)); num_images_allocated = num_files; // Last group needed for first pass // max_group = ACR_ACTUAL_IMAGE_GID - 1; // we now have to read up to and including the image, // since image pointers are needed in multi_image_init max_group = ACR_ACTUAL_IMAGE_GID; /* Add all control characters as numeric array separators to handle odd behaviour with Siemens dicom files */ for (isep=0; isep < 31; isep++) { (void) acr_element_numeric_array_separator(isep); } /* Initialize some values for general info */ general_info.initialized = FALSE; general_info.group_list = NULL; for (imri=0; imri < MRI_NDIMS; imri++) { general_info.indices[imri] = NULL; general_info.coordinates[imri] = NULL; } // Loop through file list getting information // (note that we have to duplicate the handling // of multiple images per file in this loop // to accumulate dimension sizes correctly) // need separate counter for images, since some files may // contain more than one image! iimage = 0; for (ifile=0; ifile < num_files; ifile++) { if (!Fork) { progress(ifile, num_files, "-Parsing series info"); } // Read the file if (file_type == N4DCM) { group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (file_type == IMA) { group_list = read_siemens_dicom(file_list[ifile], max_group); } // initialize big and small images, if mosaic if (acr_find_int(group_list, EXT_Slices_in_file,1)>1) { multi_image_init(group_list, &multi_image); // if multi images in file, extend file_info list num_images_allocated += acr_find_int(group_list, EXT_Slices_in_file,1) - 1; file_info = REALLOC(file_info, num_images_allocated * sizeof(*file_info)); } // loop over subimages in mosaic for(subimage = 0; subimage < acr_find_int(group_list, EXT_Slices_in_file,1); subimage++) { // Modify the group list for this image if mosaic if (acr_find_int(group_list, EXT_Slices_in_file,1)>1) { multi_image_modify_group_list(group_list,&multi_image,subimage); } // Get file-specific information get_file_info(group_list, &file_info[iimage], &general_info); // increment iimage here iimage++; } // Delete the group list acr_delete_group_list(group_list); // cleanup multi_image struct if used if (general_info.num_slices_in_file > 1) { multi_image_cleanup(group_list, &multi_image); } } // Sort the dimensions sort_dimensions(&general_info); // Create the output file if (general_info.initialized) { icvid = create_minc_file(minc_file, clobber, &general_info, file_prefix, &out_file_name, loop_type); } if (output_file_name != NULL) *output_file_name = out_file_name; // Check that we found the general info and that the minc file was // created okay if ((!general_info.initialized) || (icvid == MI_ERROR)) { if (general_info.initialized) { (void) fprintf(stderr, "Error creating minc file %s.\n", out_file_name); } free_info(&general_info, file_info, num_files); FREE(file_info); return EXIT_FAILURE; } if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr,"\nAbout to enter minc write loop...\n"); } // Loop through the files again and put images into the minc file iimage = 0; for (ifile=0; ifile < num_files; ifile++) { if (!Fork) { progress(ifile, num_files, "-Creating minc file"); } // Check that we have a valid file if (!file_info[ifile].valid) { continue; } // Read the file if (file_type == N4DCM) { group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (file_type == IMA) { group_list = read_siemens_dicom(file_list[ifile], max_group); } // initialize big and small images, if mosaic if (general_info.num_slices_in_file > 1) { multi_image_init(group_list, &multi_image); } // loop over subimages in mosaic for(subimage = 0; subimage < general_info.num_slices_in_file; subimage++) { // Modify the group list for this image if mosaic if (general_info.num_slices_in_file > 1) { multi_image_modify_group_list(group_list,&multi_image,subimage); } // Get image get_siemens_dicom_image(group_list, &image); // Save the image and any other information save_minc_image(icvid, &general_info, &file_info[iimage], &image); // increment image counter iimage++; } // Delete the group list acr_delete_group_list(group_list); // cleanup multi_image struct if used if (general_info.num_slices_in_file > 1) { multi_image_cleanup(group_list, &multi_image); } /* Free the image data */ if ((image.data != NULL) && (image.free)) FREE(image.data); } /* Close the output file */ close_minc_file(icvid); /* Free the general_info and file_info stuff */ free_info(&general_info, file_info, num_files); FREE(file_info); return EXIT_SUCCESS; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_siemens_dicom @INPUT : filename - name of siemens dicom file to read max_group - maximum group number to read @OUTPUT : (none) @RETURNS : group list read in from file @DESCRIPTION: Routine to read in a group list from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 25, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group read_siemens_dicom(char *filename, int max_group) { FILE *fp; Acr_File *afp; Acr_Group group_list; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; /* Open the file */ fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr,"Error opening file %s!\n",filename); return NULL; } /* Connect to input stream */ afp=acr_file_initialize(fp, 0, acr_stdio_read); /* should be: if file type is numa 4 dicom, and if read from file with first 128 bytes blank, then do required skipping and setup */ if (1) { /* set byte ordering to explicit LE */ byte_order = ACR_LITTLE_ENDIAN; vr_encoding = ACR_EXPLICIT_VR; acr_set_byte_order(afp, byte_order); acr_set_vr_encoding(afp, vr_encoding); /* skip 1st 128 bytes - are empty in storage class + DICM (4 chars) 128+4 = 132 bytes to skip */ acr_skip_input_data(afp, 132); } /* Read in group list */ (void) acr_input_group_list(afp, &group_list, max_group); /* Close the file */ acr_file_free(afp); (void) fclose(fp); return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_numa4_dicom @INPUT : filename - name of siemens Numaris 4 `dicom' file to read max_group - maximum group number to read @OUTPUT : (none) @RETURNS : group list read in from file @DESCRIPTION: Routine to read in a group list from a file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 18, 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group read_numa4_dicom(char *filename, int max_group) { FILE *fp; Acr_File *afp; Acr_Group group_list; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; // needed for group repair - some essential info // only available in ascii dump of MrProt structure Acr_Element Protocol; Acr_Element element; int mosaic_rows, mosaic_cols; short subimage_size[4]; int subimage_rows, subimage_cols; int num_slices, num_partitions; char *field_ptr; int num_encodings, enc_ix; int average, files_per_average; // Open the file fp = fopen(filename, "r"); if (fp == NULL) { fprintf(stderr,"Error opening file %s!\n",filename); return NULL; } // Connect to input stream afp=acr_file_initialize(fp, 0, acr_stdio_read); /* should be: if file type is numa 4 dicom, and if read from file with first 128 bytes blank, then do required skipping and setup */ // set byte ordering to explicit LE byte_order = ACR_LITTLE_ENDIAN; // vr_encoding = ACR_EXPLICIT_VR; // CD format // vr_encoding = ACR_IMPLICIT_VR; // Bourget format vr_encoding = acr_get_vr_encoding(afp); // doesn't seem to work on Export/CD acr_set_byte_order(afp, byte_order); acr_set_vr_encoding(afp, vr_encoding); if (N4_OFFSET) { // skip 1st 128 bytes - are empty in Syngo CD/Local Export files // DICM (4 chars) 128+4 = 132 bytes to skip vr_encoding = ACR_EXPLICIT_VR; // CD format acr_set_vr_encoding(afp, vr_encoding); acr_skip_input_data(afp, 132); } // Read in group list (void) acr_input_group_list(afp, &group_list, max_group); // Close the file acr_file_free(afp); (void) fclose(fp); // now fix the group list to provide essential info // via standard dicom elements // (this lets the rest of the code be more reusable) // Note that these parameters are mostly dimension lengths, // and are not usually supplied in standard DICOM implementations. // We could do without them, except that the use of mosaics for // multi-slice data makes at least the number of slices necessary. // For such elements, we will spoof our own `private' entries // using Numaris 3.5 coordinates. These should not be used elsewhere // in the code unless there's no other way! Basically things // should be done as follows: // 1) use actual element in public dicom group, if possible // 2) if not, then get info from MrProt and insert as // correct public dicom element // 3) if no public element exists, use an SPI element (careful!) // 4) if no SPI element exists, use and EXT element (careful!) // WOULD RETURN HERE FOR NORMAL DICOM // read in Protocol group Protocol = acr_find_group_element(group_list,SPI_Protocol); // add number of dynamic scans: acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, atoi((char*)prot_find_string(Protocol,"lRepetitions"))+1); // add number of echoes: acr_insert_numeric(&group_list, SPI_Number_of_echoes, atoi((char*)prot_find_string(Protocol,"lContrasts"))); // add receiving coil (for some reason this isn't in generic groups) acr_insert_string(&group_list, ACR_Receiving_coil, prot_find_string(Protocol, "sCOIL_SELECT_MEAS.asList[0].sCoilElementID.tCoilID")); // add MrProt dump acr_insert_string(&group_list,EXT_MrProt_dump,dump_protocol_text(Protocol)); // add number of slices: // (called `Partitions' for 3D) num_slices = atoi((char*)prot_find_string(Protocol,"sSliceArray.lSize")); num_partitions = atoi((char*)prot_find_string(Protocol,"sKSpace.lPartitions")); // NOTE: for some reason, lPartitions > 1 even for 2D scans // (e.g. EPI, scouts) if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, ""),"3D",2)) { // use partitions if 3D // (note that this gets more complicated if the 3D scan // is mosaiced - see below) acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, 1); acr_insert_numeric(&group_list, SPI_Number_of_3D_raw_partitions_nominal, num_partitions); } else { // use slices for 2D acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal,num_slices); acr_insert_numeric(&group_list, SPI_Number_of_3D_raw_partitions_nominal,1); } // now figure out mosaic rows and columns, and put in EXT shadow group // check for interpolation - not supported for mosaics yet if (strcmp(prot_find_string(Protocol,"sKSpace.uc2DInterpolation"),"0")){ // if interpolated image, assume no mosaic acr_insert_numeric(&group_list, EXT_Mosaic_rows, 1); acr_insert_numeric(&group_list, EXT_Mosaic_columns, 1); acr_insert_numeric(&group_list, EXT_Slices_in_file, 1); } else { // compute mosaic rows and columns // here is a hack to handle non-square mosaiced images // WARNING: as far as I can tell, the phase-encoding dir // (row/col) is reversed for mosaic EPI scans (don't know // if this is a mosaic thing, an EPI thing, or whatever) // get the array of sizes: // freq row/freq col/phase row/phase col element = acr_find_group_element(group_list, ACR_Acquisition_matrix); acr_get_element_short_array(element, 4, subimage_size); // get subimage dimensions, assuming the OPPOSITE of the // reported phase-encode direction!! if (!strncmp(acr_find_string(group_list,ACR_Phase_encoding_direction,""), "COL",3)) { subimage_rows = subimage_size[3]; subimage_cols = subimage_size[0]; } else if (!strncmp(acr_find_string(group_list, ACR_Phase_encoding_direction,""),"ROW",3)) { subimage_rows = subimage_size[2]; subimage_cols = subimage_size[1]; } mosaic_rows = acr_find_int(group_list,ACR_Rows, 1)/subimage_rows; mosaic_cols = acr_find_int(group_list,ACR_Columns, 1)/subimage_cols; acr_insert_numeric(&group_list, EXT_Mosaic_rows,mosaic_rows); acr_insert_numeric(&group_list, EXT_Mosaic_columns,mosaic_cols); if (mosaic_rows * mosaic_cols > 1) { // if 3D mosaiced scan, write number of partitions to number of // slices in dicom group THIS LOOKS REDUNDANT!!! if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, ""),"3D",2)) { acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, num_partitions); } // assume any mosaiced file contains all slices // (we now support mosaics for 2D and 3D acquisitions, // so we may need to use partitions instead of slices) if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type,""), "2D",2)) { acr_insert_numeric(&group_list, EXT_Slices_in_file, num_slices); acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal,num_slices); } else if (!strncmp(acr_find_string(group_list,ACR_MR_acquisition_type, ""),"3D",2)) { acr_insert_numeric(&group_list, EXT_Slices_in_file, num_partitions); acr_insert_numeric(&group_list, SPI_Number_of_slices_nominal, num_partitions); // also have to provide slice spacing - in case of 3D it's same // as slice thickness (and not provided in dicom header!) acr_insert_numeric(&group_list, ACR_Spacing_between_slices, acr_find_double(group_list,ACR_Slice_thickness,1.0)); } } else { acr_insert_numeric(&group_list, EXT_Slices_in_file, 1); } // correct the rows and columns values - // these will reflect those of the subimages in the mosaics // NOT the total image dimensions acr_insert_short(&group_list, EXT_Sub_image_columns,subimage_cols); acr_insert_short(&group_list, EXT_Sub_image_rows,subimage_rows); // should also correct the image position here? } // correct dynamic scan info if diffusion scan: // // assumptions: // // - diffusion protocol indicated by sDiffusion.ucMode = 0x4 // - there are 7 shots for DTI (b=0 + 6 encodings) // - b=0 scan has sequence name "ep_b0" // - encoded scans have seq names "ep_b700#1, ep_b700#2, ..." etc. // // actions: // // - change number of dynamic scans to 7 // - modify dynamic scan index to encoding index if (!strcmp(prot_find_string(Protocol,"sDiffusion.ucMode"),"0x4")) { // try to get b value acr_insert_numeric(&group_list, EXT_Diffusion_b_value, atoi((char*)prot_find_string(Protocol, "sDiffusion.alBValue[1]"))); // if all averages in one series: if (!strcmp(prot_find_string(Protocol,"ucOneSeriesForAllMeas"),"0x1")){ num_encodings = 7; // for now assume 7 shots in diffusion scan // number of 'time points' acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, num_encodings* acr_find_double(group_list,ACR_Nr_of_averages,1)); // time index of current scan: // In the current scheme, the unencoded // scan has a sequence name like "ep_b0" while the subsequent // six diffusion encodings have names like "ep_b700#1" // we could use this to come up with indices for an encoding dimension field_ptr=strstr(acr_find_string(group_list,ACR_Sequence_name,""),"#"); if (field_ptr == NULL) { enc_ix = 0; } else { enc_ix = atoi(field_ptr+sizeof(char)); } // however with the current sequence, we get usable // time indices from floor(global_image_num/num_slices) acr_insert_numeric(&group_list, ACR_Acquisition, (acr_find_int(group_list, ACR_Image, 1)-1) / num_slices); } else { // averages in different series - no special handling needed? num_encodings = 7; // for now assume 7 shots in diffusion scan // number of 'time points' acr_insert_numeric(&group_list, ACR_Acquisitions_in_series, num_encodings); // For multi-series scans, we DO USE THIS BECAUSE global // image number may be broken!! field_ptr=strstr(acr_find_string(group_list,ACR_Sequence_name,""),"#"); if (field_ptr == NULL) { enc_ix = 0; } else { enc_ix = atoi(field_ptr+sizeof(char)); } acr_insert_numeric(&group_list, ACR_Acquisition, enc_ix); } } // end of diffusion scan handling return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_info @INPUT : general_info file_info num_files @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to free contents of general and file info structures. @METHOD : @GLOBALS : @CALLS : @CREATED : November 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void free_info(General_Info *general_info, File_Info *file_info, int num_files) { Mri_Index imri; /* Free the general info pointers */ for (imri=0; imri < MRI_NDIMS; imri++) { if (general_info->indices[imri] != NULL) { FREE(general_info->indices[imri]); } if (general_info->coordinates[imri] != NULL) { FREE(general_info->coordinates[imri]); } } /* Free the group list */ if (general_info->group_list != NULL) { acr_delete_group_list(general_info->group_list); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : search_list @INPUT : value list list_length starting_point - point from which search should start @OUTPUT : (none) @RETURNS : Index in list where value is found, or -1 is value not found. @DESCRIPTION: Routine to search a list for a value, returning the index into the list. If the value is not found, then -1 is returned. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int search_list(int value, int list[], int list_length, int starting_point) { int index; /* Check list length and starting point */ if (list_length <= 0) return -1; if ((starting_point >= list_length) || (starting_point < 0)) { starting_point = 0; } /* Loop over indices, wrapping at the end of the list */ index = starting_point; do { if (list[index] == value) return index; index++; if (index >= list_length) index = 0; } while (index != starting_point); /* If we get to here, we didn't find the value */ return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : sort_dimensions @INPUT : general_info @OUTPUT : general_info @RETURNS : (nothing) @DESCRIPTION: Routine to sort the MRI dimensions according to their coordinates. It also fills in the step and start values for the SLICE dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void sort_dimensions(General_Info *general_info) { Mri_Index imri; Sort_Element *sort_array; int nvalues, ival, jval; int reverse_array; /* Sort the dimensions, if needed */ for (imri = 0; imri < MRI_NDIMS; imri++) { if (general_info->size[imri] > 1 && !((file_type == N4DCM) && // don't sort on time for N4 mosaics! (imri == TIME) && (general_info->num_slices_in_file > 0))) { // also fails on 1 slice /* Set up the array for sorting */ nvalues = general_info->size[imri]; sort_array = MALLOC(nvalues * sizeof(*sort_array)); for (ival=0; ival < nvalues; ival++) { sort_array[ival].identifier = general_info->indices[imri][ival]; sort_array[ival].original_index = ival; sort_array[ival].value = general_info->coordinates[imri][ival]; } /* Sort the array */ qsort((void *) sort_array, (size_t) nvalues, sizeof(*sort_array), dimension_sort_function); /* Figure out if we should reverse the array to keep something similar to the original ordering */ reverse_array = (sort_array[0].original_index > sort_array[nvalues-1].original_index); /* Copy the information back into the appropriate arrays */ for (ival=0; ival < nvalues; ival++) { jval = (reverse_array ? nvalues - ival - 1 : ival); general_info->indices[imri][ival] = sort_array[jval].identifier; general_info->coordinates[imri][ival] = sort_array[jval].value; } /* Free the array */ FREE(sort_array); /* Update slice step and start */ if (imri == SLICE) { if (general_info->coordinates[imri][0] != general_info->coordinates[imri][nvalues-1]) { general_info->step[general_info->slice_world] = (general_info->coordinates[imri][nvalues-1] - general_info->coordinates[imri][0]) / ((double) general_info->size[imri] - 1.0); } general_info->start[general_info->slice_world] = general_info->coordinates[imri][0]; } } /* If size > 1 */ } /* Loop over dimensions */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : dimension_sort_function @INPUT : v1, v2 - values to compare @OUTPUT : (none) @RETURNS : -1, 0 or 1 if v1 < v2, v1 == v2 or v1 > v2 @DESCRIPTION: Function to compare to array elements for sorting. Elements are compared first on value, then on their original array index (this tries to preserve the original sequence). @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int dimension_sort_function(const void *v1, const void *v2) { Sort_Element *value1, *value2; value1 = (Sort_Element *) v1; value2 = (Sort_Element *) v2; if (value1->value < value2->value) return -1; else if (value1->value > value2->value) return 1; else if (value1->original_index < value2->original_index) return -1; else if (value1->original_index > value2->original_index) return 1; else return 0; } public char *prot_find_string(Acr_Element Protocol, char *Field) { char FieldName[512]; char Separator[512]; char FieldValue[512]; char *Output = calloc(512,sizeof(char)); // return value char ProtHead[] = "### ASCCONV BEGIN ###"; long prot_offset; char *field_ptr; char DefaultValue = '0'; int ix1,ix2; // scan throught the group containing the protocol, to find the // ascii dump of the MrProt structure for (prot_offset = 0; prot_offset < Protocol->data_length - sizeof(ProtHead); prot_offset++) { if (!memcmp(Protocol->data_pointer+prot_offset, ProtHead,sizeof(ProtHead)-1)) { break; } } // bail if we didn't find the protocol if (prot_offset == Protocol->data_length-sizeof(ProtHead)) { (void) fprintf(stderr, "ERROR: could not find protocol dump in group\n"); exit(EXIT_FAILURE); } // try to find the Field name // (should bracket with newline and white space) field_ptr = strstr(Protocol->data_pointer+prot_offset,Field); if (field_ptr != NULL) { sscanf(field_ptr,"%s %s %s",FieldName,Separator,FieldValue); } else { sprintf(FieldValue,"0"); } // copy FieldValue to output, skipping quotation marks in strings ix1 = 0; for (ix2 = 0; ix2 < strlen(FieldValue); ix2++) { if (FieldValue[ix2] != '"') Output[ix1++] = FieldValue[ix2]; } // terminate string Output[ix1] = '\0'; return (char*) Output; } public char *dump_protocol_text(Acr_Element Protocol) { char ProtHead[] = "### ASCCONV BEGIN ###"; char ProtTail[] = "### ASCCONV END ###"; char *Output = calloc(Protocol->data_length,sizeof(char)); // return value int prot_found = FALSE; long prot_offset; int ix1; // scan throught the group containing the protocol, to find the // ascii dump of the MrProt structure ix1 = 0; for (prot_offset = 0; prot_offset < Protocol->data_length - sizeof(ProtHead); prot_offset++) { if (!memcmp(Protocol->data_pointer+prot_offset, ProtHead,sizeof(ProtHead)-1)) { prot_found = TRUE; } if (!memcmp(Protocol->data_pointer+prot_offset, ProtTail,sizeof(ProtTail)-1)) { break; } if (prot_found) { if (*(Protocol->data_pointer+prot_offset) != '"') Output[ix1++] = *(Protocol->data_pointer+prot_offset); } } // terminate string Output[ix1] = '\0'; // bail if we didn't find the protocol if (prot_offset == Protocol->data_length-sizeof(ProtHead)) { (void) fprintf(stderr, "ERROR: could not find protocol dump in group\n"); exit(EXIT_FAILURE); } return (char*) Output; } public int multi_image_init(Acr_Group group_list, Multi_Image *multi_image) { int group_id, element_id; int last_image, grid_size; long new_image_size; void *data; Acr_Element element; char string[256]; int idim; double pixel_spacing[2], separation; char *protocol; double normal[3]; double RowColVec[6]; double dircos[VOL_NDIMS][WORLD_NDIMS]; // Get some basic image information // (big[0/1] is number of columns/rows in whole mosaic) multi_image->big[0] = acr_find_int(group_list, ACR_Columns, 1); multi_image->big[1] = acr_find_int(group_list, ACR_Rows, 1); multi_image->pixel_size = (acr_find_int(group_list, ACR_Bits_allocated, 16)-1) / 8 + 1; // Get the image size // (size[0/1] is number of columns/rows in a single slice) multi_image->size[0] = acr_find_short(group_list,EXT_Sub_image_columns,1); multi_image->size[1] = acr_find_short(group_list,EXT_Sub_image_rows,1); // Get the grid shape, checking that it is not too big if specified multi_image->grid[0] = multi_image->big[0] / multi_image->size[0]; multi_image->grid[1] = multi_image->big[1] / multi_image->size[1]; if ((multi_image->grid[0] < 1) || (multi_image->grid[0] < 1)) { (void) fprintf(stderr, "Grid too small: %d x %d\n", multi_image->grid[0], multi_image->grid[1]); exit(EXIT_FAILURE); } // Check whether we need to do anything (1x1 grid may be the whole image) grid_size = multi_image->grid[0] * multi_image->grid[1]; if ((grid_size == 1) && (multi_image->size[0] == multi_image->big[0]) && (multi_image->size[1] == multi_image->big[1])) { /* had to remove this as now ANY images acquired with the mosaic sequence need special treatment */ multi_image->packed = FALSE; return 1; } // Steal the image element from the group list multi_image->big_image = acr_find_group_element(group_list, ACR_Pixel_data); if (multi_image->big_image == NULL) { (void) fprintf(stderr, "Couldn't find an image\n"); exit(EXIT_FAILURE); } group_id = acr_get_element_group(multi_image->big_image); element_id = acr_get_element_element(multi_image->big_image); acr_group_steal_element(acr_find_group(group_list, group_id), multi_image->big_image); // Add a small image new_image_size = multi_image->size[0] * multi_image->size[1] * multi_image->pixel_size; data = malloc((size_t) new_image_size); multi_image->small_image = acr_create_element(group_id, element_id, acr_get_element_vr(multi_image->big_image), new_image_size, data); acr_set_element_vr(multi_image->small_image, acr_get_element_vr(multi_image->big_image)); acr_set_element_byte_order(multi_image->small_image, acr_get_element_byte_order(multi_image->big_image)); acr_set_element_vr_encoding(multi_image->small_image, acr_get_element_vr_encoding(multi_image->big_image)); acr_insert_element_into_group_list(&group_list, multi_image->small_image); // Update the number of image rows and columns acr_insert_short(&group_list, ACR_Rows, multi_image->size[1]); acr_insert_short(&group_list, ACR_Columns, multi_image->size[0]); // Get image image index info (number of slices in file) last_image = acr_find_int(group_list, EXT_Slices_in_file,1); // sub_images is now just the number of mosaic elements, even if // they don't all contain slices multi_image->sub_images = multi_image->grid[0] * multi_image->grid[1]; // unlike Numaris 3.5, last_image should always be correct multi_image->first_image = last_image - multi_image->sub_images + 1; // get the pixel size element = acr_find_group_element(group_list, ACR_Pixel_size); if ((element != NULL) && (acr_get_element_numeric_array(element, 2, pixel_spacing) == 2)) { // adjust pixel size for old Numaris 3.5 data if (file_type == IMA || file_type == N3DCM) { pixel_spacing[0] *= (double) multi_image->big[0] / (double) multi_image->size[0]; pixel_spacing[1] *= (double) multi_image->big[1] / (double) multi_image->size[1]; (void) sprintf(string, "%.15g\\%.15g", pixel_spacing[0], pixel_spacing[1]); acr_insert_string(&group_list, ACR_Pixel_size, string); } } // Get step between slices separation = acr_find_double(group_list, ACR_Spacing_between_slices, 1.0); // get image normal vector // (need to compute based on dicom field, which gives // unit vectors for row and column direction) element = acr_find_group_element(group_list, ACR_Image_orientation_patient); acr_get_element_numeric_array(element, 6, RowColVec); memcpy(dircos[VCOLUMN],RowColVec,sizeof(RowColVec[0])*3); memcpy(dircos[VROW],&RowColVec[3],sizeof(RowColVec[0])*3); // used to convert x/y flips here... // convert_dicom_coordinate(dircos[VROW]); // convert_dicom_coordinate(dircos[VCOLUMN]); // compute slice normal as cross product of row/column unit vectors // (should check for unit length?) multi_image->normal[0] = dircos[VCOLUMN][1] * dircos[VROW][2] - dircos[VCOLUMN][2] * dircos[VROW][1]; multi_image->normal[1] = dircos[VCOLUMN][2] * dircos[VROW][0] - dircos[VCOLUMN][0] * dircos[VROW][2]; multi_image->normal[2] = dircos[VCOLUMN][0] * dircos[VROW][1] - dircos[VCOLUMN][1] * dircos[VROW][0]; // compute slice-to-slice step vector for (idim=0; idim < 3; idim++) { multi_image->step[idim] = separation * multi_image->normal[idim]; } // Get position and correct to first slice element = acr_find_group_element(group_list, ACR_Image_position_patient); acr_get_element_numeric_array(element, WORLD_NDIMS,multi_image->position); if (file_type == IMA || file_type == N3DCM) { // Numaris 3.5 style correction: // (position in file is for last slice, we want first) for (idim=0; idim < 3; idim++) { multi_image->position[idim] -= (double) (multi_image->sub_images-1) * multi_image->step[idim]; } } else { // Numaris 4 mosaic correction: // - position given is edge of huge slice constructed as if // real slice was at center of mosaic // - multi_image->big[0,1] are number of columns and rows of mosaic // - multi_image->size[0,1] are number of columns and rows of sub-image for (idim=0; idim < 3; idim++) { // correct offset from mosaic Center multi_image->position[idim] += (double) ((dircos[VCOLUMN][idim]*multi_image->big[0]*pixel_spacing[0]/2.0) + (dircos[VROW][idim] * multi_image->big[1] * pixel_spacing[1]/2)); // move from center to corner of slice multi_image->position[idim] -= dircos[VCOLUMN][idim] * multi_image->size[0] * pixel_spacing[0]/2.0 + dircos[VROW][idim] * multi_image->size[1] * pixel_spacing[1]/2.0; } } /* Return number of sub-images in this image */ return multi_image->sub_images; } public int multi_image_modify_group_list(Acr_Group group_list, Multi_Image *multi_image, int iimage) { int irow, ibyte, idim, nbyte; int isub, jsub; char *new, *old; long old_offset, new_offset; double position[3], distance; char string[256]; // slice order in mosaic is bottom->top under Numaris 4, // unlike Numaris 3.5 in which slices were top->bottom if (!(file_type == IMA || file_type == N3DCM)) { iimage = acr_find_int(group_list, EXT_Slices_in_file,1) - iimage - 1; } // Check the image number if ((iimage < 0) || (iimage > multi_image->sub_images)) { (void) fprintf(stderr, "Invalid image number to send: %d of %d\n", iimage, multi_image->sub_images); exit(EXIT_FAILURE); } // Figure out the sub-image indices isub = iimage % multi_image->grid[0]; jsub = iimage / multi_image->grid[0]; // Get pointers old = acr_get_element_data(multi_image->big_image); new = acr_get_element_data(multi_image->small_image); // Copy the image nbyte = multi_image->size[0] * multi_image->pixel_size; for (irow=0; irow < multi_image->size[1]; irow++) { old_offset = isub * multi_image->size[0] + (jsub * multi_image->size[1] + irow) * multi_image->big[0]; old_offset *= multi_image->pixel_size; new_offset = (irow * multi_image->size[0]) * multi_image->pixel_size; for (ibyte=0; ibyte < nbyte; ibyte++) { new[new_offset + ibyte] = old[old_offset + ibyte]; } } // Reset the byte order and VR encoding. This will be modified on each // send according to what the connection needs. acr_set_element_byte_order(multi_image->small_image, acr_get_element_byte_order(multi_image->big_image)); acr_set_element_vr_encoding(multi_image->small_image, acr_get_element_vr_encoding(multi_image->big_image)); // Update the index acr_insert_numeric(&group_list, SPI_Current_slice_number, (double) iimage); // Update the position for (idim=0; idim < 3; idim++) { position[idim] = multi_image->position[idim] + (double) iimage * multi_image->step[idim]; } (void) sprintf(string, "%.15g\\%.15g\\%.15g", position[0], position[1], position[2]); acr_insert_string(&group_list, SPI_Image_position, string); acr_insert_string(&group_list, ACR_Image_position_patient, string); if (file_type == IMA || file_type == N3DCM) { // this will convert Siemens SPI info into dicom compliant info // not needed for Numaris 4 update_coordinate_info(group_list); } return 1; } public int multi_image_cleanup(Acr_Group group_list, Multi_Image *multi_image) /* ARGSUSED */ { if (!multi_image->packed) return; acr_delete_element(multi_image->big_image); return 1; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/save_transferred_object.c0000644000175000000620000001530512574624760026731 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : save_transferred_object.c @DESCRIPTION: Routine to save data object. @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: save_transferred_object.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.2 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:58 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; /* ----------------------------- MNI Header ----------------------------------- @NAME : save_transferred_object @INPUT : group_list - list of acr-nema groups that make up object file_prefix - prefix for file names @OUTPUT : new_file_name - name for newly created file data_info - information about data object @RETURNS : (nothing) @DESCRIPTION: Routine to save the object in a file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void save_transferred_object(Acr_Group group_list, char *file_prefix2, char **new_file_name, Data_Object_Info *data_info) { Acr_Group group; Acr_Element element; char temp_name[256]; char patient_name[256]; double study_id; int acquisition_id, image_id; Acr_File *afp; FILE *fp; Acr_Status status; Acr_VR_encoding_type vr_encoding; Acr_byte_order byte_order; static int file_counter = 0; /* Added by Leili */ char full_path[256]; /* Get the VR encoding state and byte order */ element = acr_get_group_element_list(group_list); vr_encoding = acr_get_element_vr_encoding(element); byte_order = acr_get_element_byte_order(element); /* Get data info */ get_identification_info(group_list, &(data_info->study_id), &(data_info->acq_id), &(data_info->rec_num), &(data_info->image_type)); /* Get number of echos, echo number, number of dynamic scans and dynamic_scan_number */ data_info->num_echoes = acr_find_int(group_list, SPI_Number_of_echoes, 1); data_info->echo_number = acr_find_int(group_list, ACR_Echo_number, 1); data_info->num_dyn_scans = acr_find_int(group_list, ACR_Acquisitions_in_series, 1); data_info->dyn_scan_number = acr_find_int(group_list, ACR_Series, 1); /* rhoge: new info added to data_info by rhoge: nominal number of slices; this is used in detection of a stream of files with the same acquisition ID number in which there are more files than slices. If the number of signal averages is greater than one, we will assume that this means the acquisition loop was used for dynamic scanning. WARNINGS: the same thing may need to be done with `number of partitions' for it to work with 3D scans */ data_info->num_slices_nominal = acr_find_int(group_list, SPI_Number_of_slices_nominal, 1); /* Look for patient name */ element = acr_find_group_element(group_list, ACR_Patient_name); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_name, sizeof(patient_name)); } if ((element == NULL) || (strlen(patient_name) == 0)) (void) strcpy(patient_name, "unknown"); /* Look for study and image numbers */ study_id = data_info->study_id; acquisition_id = data_info->acq_id; image_id = acr_find_int(group_list, ACR_Image, 0); /* Added by Leili */ //(void) strcpy(full_path, "/software/source/dicomserver_test/"); (void) strcpy(full_path, file_prefix2); //(void) strcat(full_path, file_prefix); (void) sprintf(temp_name, "dicom-%s-%f/", patient_name, study_id); strcat (full_path, temp_name); /* Create the new file name */ /* The %s ---> full_path is added by Leili to the begining of the temp_name */ (void) sprintf(temp_name, "%s%s-%04d-%s_%f_%d_%d.dcm", full_path,"dicom", file_counter++, patient_name, study_id, acquisition_id, image_id); /* Added by Leili */ /* create the session directory if none exists */ if (mkdir(full_path, 0777) && (Do_logging> 2)) { (void) fprintf(stderr, "Directory %s exists ...\n", full_path); } /* Create the file and write out the data */ fp = fopen(temp_name, "w"); if (fp == NULL) { (void) fprintf(stderr, "Error opening file for write: %s\n", temp_name); } else { /* Set up the output stream */ afp = acr_file_initialize(fp, 0, acr_stdio_write); acr_set_vr_encoding(afp, vr_encoding); acr_set_byte_order(afp, byte_order); /* Loop over groups */ group = group_list; status = ACR_OK; while ((group != NULL) && (status == ACR_OK)) { /* Write out the group */ status = acr_output_group(afp, group); if (status != ACR_OK) { (void) fprintf(stderr, "Error writing file %s\n", temp_name); } group = acr_get_group_next(group); } /* Close the file */ acr_file_free(afp); (void) fclose(fp); } /* Copy the name */ *new_file_name = strdup(temp_name); return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dcm2mnc.c0000644000175000000620000006652512574624760023403 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dcm2mnc.c @DESCRIPTION: Program to convert dicom files to minc @GLOBALS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : * $Log: dcm2mnc.c,v $ * Revision 1.3 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.5 2002/04/26 12:02:50 rhoge * updated usage statement for new forking defaults * * Revision 1.4 2002/04/26 11:32:48 rhoge * made forking default * * Revision 1.3 2002/03/23 13:17:53 rhoge * added support for Bourget network pushed dicom files, cleaned up * file check and read_numa4_dicom vr check/assignment * * Revision 1.2 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.1 2002/03/22 03:50:02 rhoge * new name for standalone dicom to minc converter * * Revision 1.3 2002/03/22 00:38:08 rhoge * Added progress bar, wait for children at end, updated feedback statements * * Revision 1.2 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.1 2001/12/31 17:26:21 rhoge * adding file to repository- compiles without warning and converts non-mosaic * Numa 4 files. * Will probably not work for Numa 3 files yet. * ---------------------------------------------------------------------------- */ #include #include #include #include #include #include #include "dicomserver.h" extern char *minc_history; // Global for minc history char *pname; // program name File_Type file_type = UNDEF ; // type of input files char command_line[512]; int N4_OFFSET; // function prototypes private int ima_sort_function(const void *entry1, const void *entry2); private int dcm_sort_function(const void *entry1, const void *entry2); private int print_file_info( int ix, Data_Object_Info *info); public int progress(long index, int end, char *message); #define EXTREME_LOGGING 10 int Do_logging = 0; int Fork = 1; int Debug = 0; int Anon = 0; int UserIdStr = 0; char IdStr[512]; int UserName = 0; char Name[512]; // Do we keep files or are they temporary? // (now this is overridden by Cleanup) static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif // Globals for handling connection timeouts // (obsolete?) int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; int main(int argc, char *argv[]) { long ifile; long max_group; Acr_Group group_list; int exit_status; char **file_list; char **acq_file_list; Data_Object_Info **file_info_list; Data_Object_Info **acq_file_info_list; int num_files, num_files_alloc; int num_acq_files; FILE *fptemp; char last_file_name[256]; // delete? char *project_name = NULL; int process_files, have_extra_file; pid_t parent_pid, child_pid; int statptr; int ix; int UseArgDir = 1; char OutDir[128]; int List = 0; int Cleanup = 0; // Added by leili Acr_Element element; char model_name[256]; char patient_name[256]; char patient_id[256]; char reg_time[256]; char reg_date[256]; char out_dir[256]; char temp_name[256]; char *temp_dir; static char file_prefix_string[L_tmpnam+1] = "dicomserver"; char *file_prefix = file_prefix_string; //double study_id; //int acquisition_id, image_id, study_time, study_date; /* Get server process id */ parent_pid = getpid(); /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* get program name */ pname = argv[0]; if (argc<2) { usage(); } /* Added by leili to test the temp_dir */ // temp_dir = NULL; //if (! Keep_files){ // temp_dir = tempnam(NULL, NULL); //if(mkdir(temp_dir, (mode_t) 0777)){ // (void) fprintf(stderr, // "%s: Unable to create directory for temporary files.\n", // pname); // perror(pname); // exit(EXIT_FAILURE); // } // printf("Temp directory for the dcm files:%s",temp_dir); // (void) strcpy(file_prefix, temp_dir); // (void) strcat(file_prefix, "/dicom"); //} /* read in all the input pars and file names */ for (ix = 1; ixstudy_id: \t%.6f", acq_file_info_list[ifile]->study_id); printf("\nthe ifile is: %d", ifile); printf("\nacq_file_info_list[ifile]->study_date: \t%d", acq_file_info_list[ifile]->study_date); printf("\nacq_file_info_list[ifile]->study_time: \t%d", acq_file_info_list[ifile]->study_time); if ((strcmp(model_name, "sonatavision") == 0) && (!strncmp(dicm_test_string,"DICM",4))){ file_type = N4DCM; N4_OFFSET = 1; printf("assuming remaining files are Syngo DICOM (CD/Export).\n"); (void) fclose(fptemp); break; // break out of file checking loop }else if ((strcmp(model_name, "magnetom_vision") == 0)|| ((strcmp(model_name, "unknown")) == 0)) { file_type = IMA; printf("assuming remaining files are IMA!\n"); (void) fclose(fptemp); break; // break out of file checking loop }else { file_type = N4DCM; N4_OFFSET = 0; printf("assuming remaining files are Syngo DICOM.\n"); (void) fclose(fptemp); break; // break out of file checking loop } // end of file type check (void) fclose(fptemp); } // end of loop over files to check for mixed file types // now loop over all files, getting basic info on each for (ifile = 0; ifile < num_files; ifile++) { char message[20]; sprintf(message,"Parsing %d files",num_files); if (!Debug) { progress(ifile, num_files, message); } // allocate space for the current entry to file_info_list file_info_list[ifile] = MALLOC(sizeof(*file_info_list[ifile])); file_info_list[ifile]->file_index = ifile; if (file_type == N4DCM) { // read up to but not including pixel data max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (file_type == IMA) { // Added by leili for the conversion of dicom data from the old vision machine //strcat(out_dir, "/data/fmri/transfer/images/"); group_list = read_siemens_dicom(file_list[ifile],100); element = acr_find_group_element(group_list, ACR_Patient_name); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_name, sizeof(patient_name)); } if ((element == NULL) || (strlen(patient_name) == 0)) (void) strcpy(patient_name, "unknown"); strcat(out_dir, patient_name); strcat(out_dir, "_"); element = acr_find_group_element(group_list, ACR_Patient_identification); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_id, sizeof(patient_id)); } if ((element == NULL) || (strlen(patient_id) == 0)) (void) strcpy(patient_id, "unknown"); strcat(out_dir, patient_id); strcat(out_dir, "_"); element = acr_find_group_element(group_list, ACR_Study_date); if (element != NULL) { string_to_filename(acr_get_element_string(element), reg_date, sizeof(reg_date)); } if ((element == NULL) || (strlen(reg_date) == 0)) (void) strcpy(patient_id, "unknown"); strcat(out_dir, reg_date); strcat(out_dir, "_"); element = acr_find_group_element(group_list, ACR_Study_time); if (element != NULL) { string_to_filename(acr_get_element_string(element), reg_time, sizeof(reg_time)); } if ((element == NULL) || (strlen(reg_time) == 0)) (void) strcpy(patient_id, "unknown"); strcat(out_dir, reg_time); mkdir(out_dir,(mode_t) 0777); // this part works fine but it's an ugly code!!! Perhaps a better solution than the system call? strcat(temp_name,"/software/source/dicomserver_test/conversion/dicomserver/dicom_to_minc"); strcat(temp_name," "); //strcat(temp_name,out_dir); strcat(temp_name, "/data/fmri/transfer/images/leili"); strcat(temp_name,"/."); strcat(temp_name," "); strcat(temp_name,"-compress"); strcat(temp_name," "); strcat(temp_name,"-inputdir"); strcat(temp_name, " "); //strcat(temp_name,temp_dir); strcat(temp_name, "/software/source/dicomserver_test/conversion/dicomserver/dicom_data/Numaris_3/IMAS"); strcat(temp_name,"/."); printf(" the temp_name is : %s\n", temp_name); system (temp_name); exit(0); } // get some preliminary info from group_list // (which should have been `corrected' in read_xxxx_dicom parse_dicom_groups(group_list, file_info_list[ifile]); // put the file name into the info list file_info_list[ifile]->file_name = strdup(file_list[ifile]); /* print out info about file */ print_file_info(ifile,file_info_list[ifile]); // Delete the group list now that we're done with it acr_delete_group_list(group_list); } // end of loop over files to get basic info printf("Sorting files... "); if (file_type == N4DCM) { // sort the files based on acquisition number qsort(file_info_list, num_files, sizeof(file_info_list[0]), dcm_sort_function); } else if (file_type == IMA) { // sort the files based on file name // (could also use dcm_sort_function, but would have // to use ACR_Image instead of ACR_Acquisition qsort(file_list, num_files, sizeof(file_list[0]), ima_sort_function); } printf("Done sorting files.\n"); /* Get space for acquisition file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); /* Loop over files, processing by acquisition */ if (List) { printf("Listing files by series...\n"); } else { printf("Processing files, one series at a time...\n"); } num_acq_files = 1; for (ifile = 0; ifile < num_files; ifile++) { // Wait for any children that have finished while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} // If there are children, slow down the processing if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Extend acquisition file list if necessary */ if (num_acq_files >= num_files_alloc) { num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; acq_file_list = REALLOC(acq_file_list, num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = REALLOC(acq_file_info_list, num_files_alloc * sizeof(*acq_file_info_list)); } acq_file_list[num_acq_files-1] = NULL; acq_file_info_list[num_acq_files-1] = MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); if (file_type == N4DCM) { /* read up to but not including pixel data */ max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); } else if (file_type == IMA) { group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); if (group_list == NULL) { fprintf(stderr,"Error reading groups from file %s!\n", acq_file_list[num_acq_files-1]); exit(EXIT_FAILURE); } } parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); // put the file name into the info list acq_file_info_list[num_acq_files-1]->file_name = strdup(acq_file_list[num_acq_files-1]); // print some file info (junk) print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); // Check whether we have reached the end of a group of files if (num_acq_files > 1) { if ((acq_file_info_list[num_acq_files-1]->study_id != acq_file_info_list[0]->study_id) || (acq_file_info_list[num_acq_files-1]->acq_id != acq_file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; } else if (ifile == num_files-1) { // we're at the last file process_files = TRUE; } } // Delete the group list now that we're done with it acr_delete_group_list(group_list); // Use the files if we have a complete acquisition if (process_files) { // Check for file from next acquisition if (have_extra_file) num_acq_files--; if (List) { printf("Series %4d: %30s (%4d files)\n", acq_file_info_list[0]->acq_id, acq_file_info_list[0]->protocol_name, num_acq_files); } else { printf("Converting data for series %d (%s: %d files)...\n", acq_file_info_list[0]->acq_id, acq_file_info_list[0]->protocol_name, num_acq_files); } if (Fork) { // Fork child to process the files child_pid = fork(); } else { child_pid = 0; } if (child_pid > 0) { // Parent process // printf("[Parent]: Forked process to create minc file.\n"); } // Error forking else if (child_pid < 0) { fprintf(stderr, "Error forking child to create minc file\n"); return; } else { // Child process if (!List) { // process the files (same function as server) use_the_files(project_name, num_acq_files, acq_file_list, acq_file_info_list,UseArgDir,OutDir); // Print message about child finishing printf("-File creation complete for Series %d.\n", acq_file_info_list[0]->acq_id); // Clean up files, if needed if (Cleanup) { if (num_acq_files > 0) { printf("-Removing input files... "); cleanup_files(num_acq_files, acq_file_list); printf("Done removing input files.\n"); } } } if (Fork) { // Exit from child, if forked exit(EXIT_SUCCESS); } } // End of child process // ---------------------------------------------------- // if we get here we must be the parent, who goes // along happily continuing to eat files // ---------------------------------------------------- // Reset the lists for a new series free_list(num_acq_files, acq_file_list, acq_file_info_list); // check to see if the last series ended by // running into a new series: if (have_extra_file) { // move most recent file info to first entry in array, // in preparation for next series // note that num_acq_files has already been decremented, // so we do not need to subtract 1 to convert to array index acq_file_list[0] = acq_file_list[num_acq_files]; acq_file_info_list[0] = acq_file_info_list[num_acq_files]; acq_file_list[num_acq_files] = NULL; acq_file_info_list[num_acq_files] = NULL; } // num_acq_files = (have_extra_file ? 1 : 0); num_acq_files = (have_extra_file ? 2 : 1); } else { // we're not processing the files yet - just increment counter num_acq_files++; } } // end of loop over files // Wait for child processes if we've been forking. // We sleep in the checking loop to reduce parent CPU usage if (Fork) { printf("-(waiting for child processes to finish...)\n"); while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} } if (List) { printf("Done listing files.\n"); } else { printf("Done processing files.\n"); } /* Save name of first file in last set */ if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } FREE(acq_file_list); FREE(acq_file_info_list); /* Print final message */ exit_status = EXIT_SUCCESS; exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; // if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ima_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two ima file names @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int ima_sort_function(const void *entry1, const void *entry2) { char * const *value1 = entry1; char * const *value2 = entry2; int session1,series1,image1; int session2,series2,image2; sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dcm_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two dcm series numbers @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int dcm_sort_function(const void *entry1, const void *entry2) { Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; // make a sort-able session ID number: date.time double session1 = (*file_info_list1)->study_date + (*file_info_list1)->study_time / 1e6; double session2 = (*file_info_list2)->study_date + (*file_info_list2)->study_time / 1e6; // series index int series1 = (*file_info_list1)->acq_id; int series2 = (*file_info_list2)->acq_id; // frame index int frame1 = (*file_info_list1)->dyn_scan_number; int frame2 = (*file_info_list2)->dyn_scan_number; // image index int image1 = (*file_info_list1)->global_image_number; int image2 = (*file_info_list2)->global_image_number; if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (frame1 < frame2) return -1; else if (frame1 > frame2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } private int print_file_info( int ix, Data_Object_Info *info) { if (!Debug) { return 0; } // printf("SPI_Parameter_file_name = %s\n", // acr_find_string(group_list, SPI_Parameter_file_name, "")); // printf("SPI_Order_of_slices = %s\n", // acr_find_string(group_list, SPI_Order_of_slices, "")); printf("%4s %18s\n %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", "ix","file","study id","date","time","serialno","acq", "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", "seq"); /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ printf("%4d: %18s:\n %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", ix, info->file_name, info->study_id, info->study_date, info->study_time, info->scanner_serialno, info->acq_id, info->num_echoes, info->echo_number, info->num_dyn_scans, info->dyn_scan_number, info->num_slices_nominal, info->slice_number, info->acq_cols, info->rec_cols, info->num_mosaic_rows, info->global_image_number, info->sequence_name); } void usage (void) { fprintf(stderr, "\nUsage: dcm2mnc [options] file1 file2 file3 ... destdir\n"); fprintf(stderr,"\noptions:\n"); fprintf(stderr," -help : print this informative message\n"); fprintf(stderr," -list : print list of series (don't create files)\n"); fprintf(stderr," -anon : exclude subject name from file header\n"); fprintf(stderr," -descr : use str as session descriptor (default = patient initials)\n"); fprintf(stderr," -idstr : use str as subject id string (default = patient ID)\n"); fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); fprintf(stderr," -cleanup : delete input files when done (careful!)\n"); fprintf(stderr," -cmd : apply prog to output files (e.g. gzip)\n"); fprintf(stderr," -fork : fork subprocesses to create minc files (now default)\n"); fprintf(stderr," -nofork : don't fork subprocs, do show progress for individual files\n"); fprintf(stderr," -debug : print debugging info\n"); fprintf(stderr,"\n"); fprintf(stderr,"Files are named according to the following convention:\n\n"); fprintf(stderr," Directory: descr-idstr-scanner-serialno-date-time/\n"); fprintf(stderr," Files: descr-idstr-scanner-serialno-date-time-series-modality.mnc\n\n"); exit(EXIT_FAILURE); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicom_prototypes.h0000644000175000000620000001114612574624760025455 0ustar stevestaffpublic void timeout_handler(int sig); public Acr_Group skip_command_groups(Acr_Group group_list); public void cleanup_files(int num_files, char *file_list[]); public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list); public int create_minc_file(char *minc_file, int clobber, General_Info *general_info, char *file_prefix, char **output_file_name, Loop_Type loop_type); public void setup_minc_variables(int mincid, General_Info *general_info, Loop_Type loop_type); public void save_minc_image(int icvid, General_Info *general_info, File_Info *file_info, Image_Data *image); public void close_minc_file(int icvid); public void open_connection(int argc, char *argv[], Acr_File **afpin, Acr_File **afpout); public int read_project_file(char *project_name, char *file_prefix, int *output_uid, int *output_gid, char *command_line, int maxlen_command); public void get_project_option_string(char *project_option_string, int maxlen_project_option); public Acr_Message associate_reply(Acr_Message input_message, char **project_name, int *pres_context_id, Acr_byte_order *byte_order, Acr_VR_encoding_type *vr_encoding, long *maximum_length); public Acr_Message associate_reply_reject(Acr_Message input_message, int reason); public Acr_Message release_reply(Acr_Message input_message); public Acr_Message abort_reply(Acr_Message input_message); public Acr_Message data_reply(Acr_Message input_message); public void save_transferred_object(Acr_Group group_list, char *file_prefix, char **new_file_name, Data_Object_Info *data_info); public void get_file_info(Acr_Group group_list, File_Info *file_info, General_Info *general_info); public void get_identification_info(Acr_Group group_list, double *study_id, int *acq_id, int *rec_num, int *image_type); public void get_intensity_info(Acr_Group group_list, File_Info *file_info); public void get_coordinate_info(Acr_Group group_list, File_Info *file_info, Orientation *orientation, World_Index volume_to_world[VOL_NDIMS], int sizes[VOL_NDIMS], double dircos[VOL_NDIMS][WORLD_NDIMS], double steps[VOL_NDIMS], double starts[VOL_NDIMS], double coordinate[WORLD_NDIMS]); public void convert_numa3_coordinate(double coordinate[WORLD_NDIMS]); public void convert_dicom_coordinate(double coordinate[WORLD_NDIMS]); public void get_general_header_info(Acr_Group group_list, General_Info *general_info); public double convert_time_to_seconds(double dicom_time); public void get_siemens_dicom_image(Acr_Group group_list, Image_Data *image); public int siemens_dicom_to_minc(int num_files, char *file_list[], char *minc_file, int clobber, char *file_prefix, char **output_file_name); public Acr_Group read_siemens_dicom(char *filename, int max_group); public void free_info(General_Info *general_info, File_Info *file_info, int num_files); public int search_list(int value, int list[], int list_length, int starting_point); public void usage(void); public void sort_dimensions(General_Info *general_info); public int dimension_sort_function(const void *v1, const void *v2); public void string_to_filename(char *string, char *filename, int maxlen); public void use_the_files(char *project_name, int num_files, char *file_list[], Data_Object_Info *data_info[], int UseArgDir,char *OutDir); public Acr_Group siemens_to_dicom(char *filename, int read_image); // MGH specific stuff public void string_to_initials(char *string, char *filename, int maxlen); // Numaris 4 specific stuff public Acr_Group read_numa4_dicom(char *filename, int max_group); public char *prot_find_string(Acr_Element Protocol, char *Field); public char *dump_protocol_text(Acr_Element Protocol); minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicom_element_defs.h0000644000175000000620000001642212574624760025661 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_element_defs.h @DESCRIPTION: Element definitions for dicom @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ /* Define standard UID's */ #define FAVORITE_ABSTRACT_SYNTAX ACR_MR_IMAGE_STORAGE_UID #define ACR_MR_IMAGE_STORAGE_UID "1.2.840.10008.5.1.4.1.1.4" #define ACR_EXPLICIT_VR_BIG_END_UID "1.2.840.10008.1.2.2" #define ACR_EXPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2.1" #define ACR_IMPLICIT_VR_LITTLE_END_UID "1.2.840.10008.1.2" #define ACR_APPLICATION_CONTEXT_UID "1.2.840.10008.3.1.1.1" /* Define constants for accepting association */ #define ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC 7 #define ACR_ASSOC_RJ_NO_REASON 1 #define ACR_ASSOC_RJ_PERM 1 #define ACR_ASSOC_RJ_USER 1 #define ACR_ASSOC_PR_CN_ACCEPT 0 #define ACR_ASSOC_PR_CN_REJECT 1 #define ACR_PDU_ITEM_USER_INFORMATION 0x50 /* Define group numbers */ #define ACR_MESSAGE_GID 0 #define ACR_ACTUAL_IMAGE_GID 0x7fe0 /* Define commands */ #define ACR_C_STORE_RQ 0x0001 #define ACR_C_STORE_RSP 0x8001 #define ACR_C_ECHO_RQ 0x0030 #define ACR_C_ECHO_RSP 0x8030 /* Define dataset type */ #define ACR_NULL_DATASET 0x0101 /* Define status codes */ #define ACR_SUCCESS 0x0000 /* Define data object types */ #define ACR_IMAGE_OBJECT 0x0000 #define ACR_OTHER_OBJECT 0x0100 /* Define acr-nema constants */ #define ACR_MODALITY_MR "MR" /* Element id's for DICOM */ GLOBAL_ELEMENT(ACR_Affected_SOP_class_UID , 0x0000, 0x0002, UI); GLOBAL_ELEMENT(ACR_Command , 0x0000, 0x0100, US); GLOBAL_ELEMENT(ACR_Message_id , 0x0000, 0x0110, US); GLOBAL_ELEMENT(ACR_Message_id_brt , 0x0000, 0x0120, US); GLOBAL_ELEMENT(ACR_Priority , 0x0000, 0x0700, US); GLOBAL_ELEMENT(ACR_Dataset_type , 0x0000, 0x0800, US); GLOBAL_ELEMENT(ACR_Status , 0x0000, 0x0900, US); GLOBAL_ELEMENT(ACR_Affected_SOP_instance_UID , 0x0000, 0x1000, UI); GLOBAL_ELEMENT(ACR_Move_originator_AE_title , 0x0000, 0x1031, AE); GLOBAL_ELEMENT(ACR_Image_type , 0x0008, 0x0008, CS); GLOBAL_ELEMENT(ACR_Study_date , 0x0008, 0x0020, DA); GLOBAL_ELEMENT(ACR_Series_date , 0x0008, 0x0021, DA); GLOBAL_ELEMENT(ACR_Acquisition_date , 0x0008, 0x0022, DA); GLOBAL_ELEMENT(ACR_Study_time , 0x0008, 0x0030, TM); GLOBAL_ELEMENT(ACR_Series_time , 0x0008, 0x0031, TM); GLOBAL_ELEMENT(ACR_Acquisition_time , 0x0008, 0x0032, TM); GLOBAL_ELEMENT(ACR_Modality , 0x0008, 0x0060, CS); GLOBAL_ELEMENT(ACR_Manufacturer , 0x0008, 0x0070, LO); GLOBAL_ELEMENT(ACR_Institution_id , 0x0008, 0x0080, LO); GLOBAL_ELEMENT(ACR_Referring_physician , 0x0008, 0x0090, PN); GLOBAL_ELEMENT(ACR_Station_id , 0x0008, 0x1010, SH); GLOBAL_ELEMENT(ACR_Procedure_description , 0x0008, 0x1030, LO); GLOBAL_ELEMENT(ACR_Performing_physician , 0x0008, 0x1050, PN); GLOBAL_ELEMENT(ACR_Operators_name , 0x0008, 0x1070, PN); GLOBAL_ELEMENT(ACR_Manufacturer_model , 0x0008, 0x1090, LO); GLOBAL_ELEMENT(ACR_Patient_name , 0x0010, 0x0010, PN); GLOBAL_ELEMENT(ACR_Patient_identification, 0x0010, 0x0020, LO); GLOBAL_ELEMENT(ACR_Patient_birth_date , 0x0010, 0x0030, DA); GLOBAL_ELEMENT(ACR_Patient_sex , 0x0010, 0x0040, CS); GLOBAL_ELEMENT(ACR_Patient_age , 0x0010, 0x1010, AS); GLOBAL_ELEMENT(ACR_Patient_weight , 0x0010, 0x1030, DS); GLOBAL_ELEMENT(ACR_Scanning_sequence , 0x0018, 0x0020, CS); GLOBAL_ELEMENT(ACR_MR_acquisition_type , 0x0018, 0x0023, CS); GLOBAL_ELEMENT(ACR_Sequence_name , 0x0018, 0x0024, CS); GLOBAL_ELEMENT(ACR_Slice_thickness , 0x0018, 0x0050, DS); GLOBAL_ELEMENT(ACR_Repetition_time , 0x0018, 0x0080, DS); GLOBAL_ELEMENT(ACR_Echo_time , 0x0018, 0x0081, DS); GLOBAL_ELEMENT(ACR_Inversion_time , 0x0018, 0x0082, DS); GLOBAL_ELEMENT(ACR_Nr_of_averages , 0x0018, 0x0083, DS); GLOBAL_ELEMENT(ACR_Imaging_frequency , 0x0018, 0x0084, DS); GLOBAL_ELEMENT(ACR_Imaged_nucleus , 0x0018, 0x0085, SH); GLOBAL_ELEMENT(ACR_Echo_number , 0x0018, 0x0086, IS); GLOBAL_ELEMENT(ACR_Magnetic_field_strength,0x0018, 0x0087, DS); GLOBAL_ELEMENT(ACR_Spacing_between_slices, 0x0018, 0x0088, DS); GLOBAL_ELEMENT(ACR_Number_of_phase_encoding_steps, 0x0018, 0x0089, IS); GLOBAL_ELEMENT(ACR_Echo_train_length , 0x0018, 0x0091, IS); GLOBAL_ELEMENT(ACR_Percent_sampling , 0x0018, 0x0093, DS); GLOBAL_ELEMENT(ACR_Percent_phase_field_of_view, 0x0018, 0x0094, DS); GLOBAL_ELEMENT(ACR_Pixel_bandwidth , 0x0018, 0x0095, DS); GLOBAL_ELEMENT(ACR_Device_serial_number , 0x0018, 0x1000, LO); GLOBAL_ELEMENT(ACR_Software_versions , 0x0018, 0x1020, LO); GLOBAL_ELEMENT(ACR_Protocol_name , 0x0018, 0x1030, LO); GLOBAL_ELEMENT(ACR_Receiving_coil , 0x0018, 0x1250, SH); GLOBAL_ELEMENT(ACR_Transmitting_coil , 0x0018, 0x1251, SH); GLOBAL_ELEMENT(ACR_Acquisition_matrix , 0x0018, 0x1310, US); GLOBAL_ELEMENT(ACR_Phase_encoding_direction, 0x0018, 0x1312, CS); GLOBAL_ELEMENT(ACR_Flip_angle , 0x0018, 0x1314, DS); GLOBAL_ELEMENT(ACR_SAR , 0x0018, 0x1316, DS); GLOBAL_ELEMENT(ACR_Acq_comments , 0x0018, 0x4000, LT); GLOBAL_ELEMENT(ACR_Patient_position , 0x0018, 0x5100, CS); GLOBAL_ELEMENT(ACR_Study , 0x0020, 0x0010, SH); GLOBAL_ELEMENT(ACR_Series , 0x0020, 0x0011, IS); GLOBAL_ELEMENT(ACR_Acquisition , 0x0020, 0x0012, IS); GLOBAL_ELEMENT(ACR_Image , 0x0020, 0x0013, IS); GLOBAL_ELEMENT(ACR_Image_position_patient, 0x0020, 0x0032, DS); GLOBAL_ELEMENT(ACR_Image_orientation_patient, 0x0020, 0x0037, DS); GLOBAL_ELEMENT(ACR_Acquisitions_in_series, 0x0020, 0x1001, IS); GLOBAL_ELEMENT(ACR_Rows , 0x0028, 0x0010, US); GLOBAL_ELEMENT(ACR_Columns , 0x0028, 0x0011, US); GLOBAL_ELEMENT(ACR_Pixel_size , 0x0028, 0x0030, DS); GLOBAL_ELEMENT(ACR_Bits_allocated , 0x0028, 0x0100, US); GLOBAL_ELEMENT(ACR_Bits_stored , 0x0028, 0x0101, US); GLOBAL_ELEMENT(ACR_Smallest_pixel_value , 0x0028, 0x0106, US); GLOBAL_ELEMENT(ACR_Largest_pixel_value , 0x0028, 0x0107, US); GLOBAL_ELEMENT(ACR_Image_location , 0x0028, 0x0200, US); GLOBAL_ELEMENT(ACR_Window_centre , 0x0028, 0x1050, DS); GLOBAL_ELEMENT(ACR_Window_width , 0x0028, 0x1051, DS); //GLOBAL_ELEMENT(ACR_Pixel_data, ACR_ACTUAL_IMAGE_GID, 0x0010, UNKNOWN); GLOBAL_ELEMENT(ACR_Pixel_data , 0x7fe0, 0x0010, OW); #include #include minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_header_table.h0000644000175000000620000003723712574624760026205 0ustar stevestaffSiemens_header_entry Siemens_header_table[] = { {0x0008, 0x0020, &Siemens_header.G08.Ide.StudyDate, create_ds_date_t_element, 1}, {0x0008, 0x0022, &Siemens_header.G08.Ide.AcquisitionDate, create_ds_date_t_element, 1}, {0x0008, 0x0023, &Siemens_header.G08.Ide.ImageDate, create_ds_date_t_element, 1}, {0x0008, 0x0030, &Siemens_header.G08.Ide.StudyTime, create_ds_time_t_element, 1}, {0x0008, 0x0032, &Siemens_header.G08.Ide.AcquisitionTime, create_ds_time_t_element, 1}, {0x0008, 0x0033, &Siemens_header.G08.Ide.ImageTime, create_ds_time_t_element, 1}, {0x0008, 0x0041, &Siemens_header.G08.Ide.DataSetSubtype, create_data_set_subtype_t_element, 1}, {0x0008, 0x0060, &Siemens_header.G08.Ide.Modality, create_modality_t_element, 1}, {0x0008, 0x0070, &Siemens_header.G08.Ide.Manufacturer, create_char_element, LENGTH_MANUFACTURER + 1}, {0x0008, 0x0080, &Siemens_header.G08.Ide.InstitutionID, create_char_element, LENGTH_LABEL + 1}, {0x0008, 0x0090, &Siemens_header.G08.Ide.ReferringPhysician, create_char_element, LENGTH_LABEL + 1}, {0x0008, 0x1010, &Siemens_header.G08.Ide.StationID, create_char_element, LENGTH_LABEL + 1}, {0x0008, 0x1080, &Siemens_header.G08.Ide.AdmittingDiagnosis, create_char_element, LENGTH_DIAGNOSIS + 1}, {0x0008, 0x1090, &Siemens_header.G08.Ide.ManufacturerModel, create_char_element, LENGTH_LABEL + 1}, {0x0009, 0x1041, &Siemens_header.G09.Ide.DataObjectSubtype, create_data_object_subtype_t_element, 1}, {0x0009, 0x1210, &Siemens_header.G09.Ide.StorageMode, create_storage_mode_t_element, 1}, {0x0009, 0x1226, &Siemens_header.G09.Ide.LastMoveDate, create_ds_date_t_element, 1}, {0x0009, 0x1227, &Siemens_header.G09.Ide.LastMoveTime, create_ds_time_t_element, 1}, {0x0009, 0x1316, &Siemens_header.G09.Ide.CPUIdentificationLabel, create_char_element, LENGTH_LABEL + 1}, {0x0009, 0x1320, &Siemens_header.G09.Ide.HeaderVersion, create_char_element, LENGTH_HEADER_VERSION + 1}, {0x0010, 0x0010, &Siemens_header.G10.Pat.PatientName, create_char_element, LENGTH_LABEL + 1}, {0x0010, 0x0020, &Siemens_header.G10.Pat.PatientId, create_char_element, LENGTH_PATIENT_ID + 1}, {0x0010, 0x0030, &Siemens_header.G10.Pat.PatientBirthdate, create_ds_date_t_element, 1}, {0x0010, 0x0040, &Siemens_header.G10.Pat.PatientSex, create_sex_t_element, 1}, {0x0010, 0x1010, &Siemens_header.G10.Pat.PatientAge, create_char_element, LENGTH_AGE + 1}, {0x0010, 0x1030, &Siemens_header.G10.Pat.PatientWeight, create_long_element, 1}, {0x0011, 0x1110, &Siemens_header.G11.Pat.RegistrationDate, create_ds_date_t_element, 1}, {0x0011, 0x1111, &Siemens_header.G11.Pat.RegistrationTime, create_ds_time_t_element, 1}, {0x0011, 0x1123, &Siemens_header.G11.Pat.UsedPatientWeight, create_long_element, 1}, {0x0018, 0x0010, &Siemens_header.G18.Acq.Contrast, create_contrast_t_element, 1}, {0x0018, 0x0050, &Siemens_header.G18.Acq.SliceThickness, create_double_element, 1}, {0x0018, 0x0080, &Siemens_header.G18.Acq.RepetitionTime, create_double_element, 1}, {0x0018, 0x0081, &Siemens_header.G18.Acq.EchoTime, create_double_element, 1}, {0x0018, 0x0083, &Siemens_header.G18.Acq.NumberOfAverages, create_long_element, 1}, {0x0018, 0x0084, &Siemens_header.G18.Acq.ImagingFrequency, create_double_element, 1}, {0x0018, 0x0085, &Siemens_header.G18.Acq.ImagedNucleus, create_char_element, LENGTH_NUCLEUS + 1}, {0x0018, 0x0086, &Siemens_header.G18.Acq.EchoNumber, create_long_element, 1}, {0x0018, 0x0090, &Siemens_header.G18.Acq.DataCollectionDiameter, create_long_element, 1}, {0x0018, 0x1000, &Siemens_header.G18.Acq.DeviceSerialNumber, create_char_element, LENGTH_LABEL + 1}, {0x0018, 0x1020, &Siemens_header.G18.Acq.SoftwareVersion, create_char_element, LENGTH_SOFTWARE_VERSION + 1}, {0x0018, 0x1200, &Siemens_header.G18.Acq.CalibrationDate, create_ds_date_t_element, 1}, {0x0018, 0x1201, &Siemens_header.G18.Acq.CalibrationTime, create_ds_time_t_element, 1}, {0x0018, 0x1250, &Siemens_header.G18.Acq.ReceivingCoil, create_char_element, LENGTH_LABEL + 1}, {0x0018, 0x5100, &Siemens_header.G18.Acq.PatientPosition, create_patient_position_t_element, 1}, {0x0019, 0x1010, &Siemens_header.G19.Acq1.CM.NetFrequency, create_long_element, 1}, {0x0019, 0x1020, &Siemens_header.G19.Acq1.CM.MeasurementMode, create_measurement_mode_t_element, 1}, {0x0019, 0x1030, &Siemens_header.G19.Acq1.CM.CalculationMode, create_calculation_mode_t_element, 1}, {0x0019, 0x1050, &Siemens_header.G19.Acq1.CM.NoiseLevel, create_long_element, 1}, {0x0019, 0x1060, &Siemens_header.G19.Acq1.CM.NumberOfDataBytes, create_long_element, 1}, {0x0019, 0x1210, &Siemens_header.G19.Acq2.Mr.TotalMeasurementTime, create_double_element, 1}, {0x0019, 0x1211, &Siemens_header.G19.Acq2.Mr.TotalMeasurementTimeCur, create_double_element, 1}, {0x0019, 0x1212, &Siemens_header.G19.Acq2.Mr.StartDelayTime, create_double_element, 1}, {0x0019, 0x1213, &Siemens_header.G19.Acq2.Mr.DwellTime, create_double_element, 1}, {0x0019, 0x1214, &Siemens_header.G19.Acq2.Mr.NumberOfPhases, create_long_element, 1}, {0x0019, 0x1220, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesNominal, create_long_element, 1}, {0x0019, 0x1221, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesCurrent, create_long_element, 1}, {0x0019, 0x1226, &Siemens_header.G19.Acq2.Mr.NumberOfFourierLinesAfterZero, create_long_element, 1}, {0x0019, 0x1228, &Siemens_header.G19.Acq2.Mr.FirstMeasuredFourierLine, create_long_element, 1}, {0x0019, 0x1230, &Siemens_header.G19.Acq2.Mr.AcquisitionColumns, create_long_element, 1}, {0x0019, 0x1231, &Siemens_header.G19.Acq2.Mr.ReconstructionColumns, create_long_element, 1}, {0x0019, 0x1250, &Siemens_header.G19.Acq2.Mr.NumberOfAverages, create_long_element, 1}, {0x0019, 0x1260, &Siemens_header.G19.Acq2.Mr.FlipAngle, create_double_element, 1}, {0x0019, 0x1270, &Siemens_header.G19.Acq2.Mr.NumberOfPrescans, create_long_element, 1}, {0x0019, 0x1281, &Siemens_header.G19.Acq2.Mr.FilterTypeRawData, create_filter_type_t_element, 1}, {0x0019, 0x1282, &Siemens_header.G19.Acq2.Mr.FilterParameterRawData, create_filter_parameter_t_element, 1}, {0x0019, 0x1283, &Siemens_header.G19.Acq2.Mr.FilterTypeImageData, create_filter_type_image_t_element, 1}, {0x0019, 0x1285, &Siemens_header.G19.Acq2.Mr.FilterTypePhaseCorrection, create_filter_type_t_element, 1}, {0x0019, 0x1290, &Siemens_header.G19.Acq2.Mr.NumberOfSaturationRegions, create_long_element, 1}, {0x0019, 0x1294, &Siemens_header.G19.Acq2.Mr.ImageRotationAngle, create_double_element, 1}, {0x0019, 0x1298, &Siemens_header.G19.Acq2.Mr.CoilPosition, create_image_location_t_element, 1}, {0x0019, 0x1412, &Siemens_header.G19.Acq3.Mr.MagneticFieldStrength, create_double_element, 1}, {0x0019, 0x1414, &Siemens_header.G19.Acq3.Mr.ADCVoltage, create_double_element, 1}, {0x0019, 0x1416, &Siemens_header.G19.Acq3.Mr.ADCOffset, create_double_element, 2}, {0x0019, 0x1420, &Siemens_header.G19.Acq3.Mr.TransmitterAmplitude, create_double_element, 1}, {0x0019, 0x1421, &Siemens_header.G19.Acq3.Mr.NumberOfTransmitterAmplitudes, create_long_element, 1}, {0x0019, 0x1422, &Siemens_header.G19.Acq3.Mr.TransmitterAttenuator, create_double_element, 1}, {0x0019, 0x1424, &Siemens_header.G19.Acq3.Mr.TransmitterCalibration, create_double_element, 1}, {0x0019, 0x1426, &Siemens_header.G19.Acq3.Mr.TransmitterReference, create_double_element, 1}, {0x0019, 0x1450, &Siemens_header.G19.Acq3.Mr.ReceiverTotalGain, create_double_element, 1}, {0x0019, 0x1451, &Siemens_header.G19.Acq3.Mr.ReceiverAmplifierGain, create_double_element, 1}, {0x0019, 0x1452, &Siemens_header.G19.Acq3.Mr.ReceiverPreamplifierGain, create_double_element, 1}, {0x0019, 0x1454, &Siemens_header.G19.Acq3.Mr.ReceiverCableAttenuation, create_double_element, 1}, {0x0019, 0x1455, &Siemens_header.G19.Acq3.Mr.ReceiverReferenceGain, create_double_element, 1}, {0x0019, 0x1456, &Siemens_header.G19.Acq3.Mr.ReceiverFilterFrequency, create_long_element, 1}, {0x0019, 0x1460, &Siemens_header.G19.Acq3.Mr.ReconstructionScaleFactor, create_double_element, 1}, {0x0019, 0x1462, &Siemens_header.G19.Acq3.Mr.ReferenceScaleFactor, create_double_element, 1}, {0x0019, 0x1470, &Siemens_header.G19.Acq3.Mr.PhaseGradientAmplitude, create_double_element, 1}, {0x0019, 0x1471, &Siemens_header.G19.Acq3.Mr.ReadoutGradientAmplitude, create_double_element, 1}, {0x0019, 0x1472, &Siemens_header.G19.Acq3.Mr.SelectionGradientAmplitude, create_double_element, 1}, {0x0019, 0x1480, &Siemens_header.G19.Acq3.Mr.GradientDelayTime, create_gradient_delay_time_t_element, 1}, {0x0019, 0x1482, &Siemens_header.G19.Acq3.Mr.TotalGradientDelayTime, create_double_element, 1}, {0x0019, 0x1490, &Siemens_header.G19.Acq3.Mr.SensitivityCorrectionLabel, create_char_element, LENGTH_LABEL + 1}, {0x0019, 0x14a0, &Siemens_header.G19.Acq3.Mr.RfWatchdogMask, create_long_element, 1}, {0x0019, 0x14a2, &Siemens_header.G19.Acq3.Mr.RfPowerErrorIndicator, create_double_element, 1}, {0x0019, 0x14a5, &Siemens_header.G19.Acq3.Mr.SarWholeBody, create_sar_sed_t_element, 1}, {0x0019, 0x14a6, &Siemens_header.G19.Acq3.Mr.Sed, create_sar_sed_t_element, 1}, {0x0019, 0x14b0, &Siemens_header.G19.Acq3.Mr.AdjustmentStatusMask, create_long_element, 1}, {0x0019, 0x1510, &Siemens_header.G19.Acq4.CM.ParameterFileName, create_char_element, LENGTH_FILE_NAME + 1}, {0x0019, 0x1511, &Siemens_header.G19.Acq4.CM.SequenceFileName, create_char_element, LENGTH_FILE_NAME + 1}, {0x0019, 0x1512, &Siemens_header.G19.Acq4.CM.SequenceFileOwner, create_char_element, LENGTH_SEQUENCE_INFO + 1}, {0x0019, 0x1513, &Siemens_header.G19.Acq4.CM.SequenceDescription, create_char_element, LENGTH_SEQUENCE_INFO + 1}, {0x0020, 0x0010, &Siemens_header.G20.Rel.Study, create_long_element, 1}, {0x0020, 0x0012, &Siemens_header.G20.Rel.Acquisition, create_long_element, 1}, {0x0020, 0x0013, &Siemens_header.G20.Rel.Image, create_long_element, 1}, {0x0020, 0x0050, &Siemens_header.G20.Rel.Location, create_long_element, 1}, {0x0020, 0x0070, &Siemens_header.G20.Rel.ImageGeometryType, create_geometry_t_element, 1}, {0x0020, 0x1001, &Siemens_header.G20.Rel.AcquisitionsInSeries, create_long_element, 1}, {0x0020, 0x1020, &Siemens_header.G20.Rel.Reference, create_reference_t_element, 1}, {0x0021, 0x1011, &Siemens_header.G21.Rel1.CM.Target, create_target_point_t_element, 1}, {0x0021, 0x1020, &Siemens_header.G21.Rel1.CM.RoiMask, create_short_element, 1}, {0x0021, 0x1120, &Siemens_header.G21.Rel1.CM.FoV, create_field_of_view_t_element, 1}, {0x0021, 0x1122, &Siemens_header.G21.Rel1.CM.ImageMagnificationFactor, create_double_element, 1}, {0x0021, 0x1130, &Siemens_header.G21.Rel1.CM.ViewDirection, create_view_direction_t_element, 1}, {0x0021, 0x1132, &Siemens_header.G21.Rel1.CM.RestDirection, create_rest_direction_t_element, 1}, {0x0021, 0x1160, &Siemens_header.G21.Rel1.CM.ImagePosition, create_image_location_t_element, 1}, {0x0021, 0x1161, &Siemens_header.G21.Rel1.CM.ImageNormal, create_image_location_t_element, 1}, {0x0021, 0x1163, &Siemens_header.G21.Rel1.CM.ImageDistance, create_double_element, 1}, {0x0021, 0x1165, &Siemens_header.G21.Rel1.CM.ImagePositioningHistoryMask, create_short_element, 1}, {0x0021, 0x116a, &Siemens_header.G21.Rel1.CM.ImageRow, create_image_location_t_element, 1}, {0x0021, 0x116b, &Siemens_header.G21.Rel1.CM.ImageColumn, create_image_location_t_element, 1}, {0x0021, 0x1170, &Siemens_header.G21.Rel1.CM.PatientOrientationSet1, create_patient_orientation_t_element, 1}, {0x0021, 0x1171, &Siemens_header.G21.Rel1.CM.PatientOrientationSet2, create_patient_orientation_t_element, 1}, {0x0021, 0x1180, &Siemens_header.G21.Rel1.CM.StudyName, create_char_element, LENGTH_LABEL + 1}, {0x0021, 0x1182, &Siemens_header.G21.Rel1.CM.StudyType, create_study_type_t_element, 1}, {0x0021, 0x1322, &Siemens_header.G21.Rel2.Mr.PhaseCorRowRec, create_long_element, 1}, {0x0021, 0x1324, &Siemens_header.G21.Rel2.Mr.PhaseCorColRec, create_long_element, 1}, {0x0021, 0x1330, &Siemens_header.G21.Rel2.Mr.NumberOf3DRawPartNom, create_long_element, 1}, {0x0021, 0x1331, &Siemens_header.G21.Rel2.Mr.NumberOf3DRawPartCur, create_long_element, 1}, {0x0021, 0x1334, &Siemens_header.G21.Rel2.Mr.NumberOf3DImaPart, create_long_element, 1}, {0x0021, 0x1336, &Siemens_header.G21.Rel2.Mr.Actual3DImaPartNumber, create_long_element, 1}, {0x0021, 0x1339, &Siemens_header.G21.Rel2.Mr.SlabThickness, create_double_element, 1}, {0x0021, 0x1340, &Siemens_header.G21.Rel2.Mr.NumberOfSlicesNom, create_long_element, 1}, {0x0021, 0x1341, &Siemens_header.G21.Rel2.Mr.NumberOfSlicesCur, create_long_element, 1}, {0x0021, 0x1342, &Siemens_header.G21.Rel2.Mr.CurrentSliceNumber, create_long_element, 1}, {0x0021, 0x1343, &Siemens_header.G21.Rel2.Mr.CurrentGroupNumber, create_long_element, 1}, {0x0021, 0x1344, &Siemens_header.G21.Rel2.Mr.CurrentSliceDistanceFactor, create_double_element, 1}, {0x0021, 0x134f, &Siemens_header.G21.Rel2.Mr.OrderOfSlices, create_order_of_slices_t_element, 1}, {0x0021, 0x1356, &Siemens_header.G21.Rel2.Mr.RepetitionTime, create_double_element, 1}, {0x0021, 0x1370, &Siemens_header.G21.Rel2.Mr.NumberOfEchoes, create_long_element, 1}, {0x0028, 0x0005, &Siemens_header.G28.Pre.ImageDimension, create_short_element, 1}, {0x0028, 0x0010, &Siemens_header.G28.Pre.Rows, create_short_element, 1}, {0x0028, 0x0011, &Siemens_header.G28.Pre.Columns, create_short_element, 1}, {0x0028, 0x0030, &Siemens_header.G28.Pre.PixelSize, create_pixel_size_t_element, 1}, {0x0028, 0x0040, &Siemens_header.G28.Pre.ImageFormat, create_image_format_t_element, 1}, {0x0028, 0x0060, &Siemens_header.G28.Pre.CompressionCode, create_compression_code_t_element, 1}, {0x0028, 0x0100, &Siemens_header.G28.Pre.BitsAllocated, create_short_element, 1}, {0x0028, 0x0101, &Siemens_header.G28.Pre.BitsStored, create_short_element, 1}, {0x0028, 0x0102, &Siemens_header.G28.Pre.HighBit, create_short_element, 1}, {0x0028, 0x0103, &Siemens_header.G28.Pre.PixelRepresentation, create_short_element, 1}, {0x0028, 0x1050, &Siemens_header.G28.Pre.WindowCenter, create_windows_t_element, 1}, {0x0028, 0x1051, &Siemens_header.G28.Pre.WindowWidth, create_windows_t_element, 1}, {0x0028, 0x1052, &Siemens_header.G28.Pre.RescaleIntercept, create_long_element, 1}, {0x0028, 0x1053, &Siemens_header.G28.Pre.RescaleSlope, create_long_element, 1}, {0x0029, 0x1110, &Siemens_header.G29.Pre.WindowStyle, create_window_style_t_element, 1}, {0x0029, 0x1120, &Siemens_header.G29.Pre.PixelQualityCode, create_pixel_quality_code_t_element, 1}, {0x0029, 0x1152, &Siemens_header.G29.Pre.SortCode, create_long_element, 1}, {0, 0, NULL, NULL, 0} }; /* Functions needed for this table: create_calculation_mode_t_element create_char_element create_compression_code_t_element create_contrast_t_element create_data_object_subtype_t_element create_data_set_subtype_t_element create_double_element create_ds_date_t_element create_ds_time_t_element create_field_of_view_t_element create_filter_parameter_t_element create_filter_type_image_t_element create_filter_type_t_element create_gate_phase_t_element create_geometry_t_element create_gradient_delay_time_t_element create_image_format_t_element create_image_location_t_element create_laterality_t_element create_long_element create_measurement_mode_t_element create_modality_t_element create_object_orientation_t_element create_object_threshold_t_element create_order_of_slices_t_element create_patient_orientation_t_element create_patient_phase_t_element create_patient_position_t_element create_patient_region_t_element create_pixel_quality_code_t_element create_pixel_quality_value_t_element create_pixel_size_t_element create_reference_t_element create_rest_direction_t_element create_rotation_direction_t_element create_sar_sed_t_element create_save_code_t_element create_sex_t_element create_short_element create_storage_mode_t_element create_study_type_t_element create_target_point_t_element create_view_direction_t_element create_window_style_t_element create_windows_t_element */ minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/spi_element_defs.h0000644000175000000620000001154012574624760025355 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : spi_element_defs.h @DESCRIPTION: Element definitions for spi @METHOD : @GLOBALS : @CALLS : @CREATED : November 23, 1993 (Peter Neelin) @MODIFIED : @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. ---------------------------------------------------------------------------- */ /* Define siemens-specific stuff for associations and messages */ #define SPI_VISION_AE_TITLE "MRC16505" /* changed by Leili from OC1SA1MR010000 to MRC16505 */ #define SPI_VISION_IMPLEMENTATION_UID "1.3.12.2.1107.5.2" /* Chaged by leili from 2.16.840.1.113669.2.931128 to 1.3.12.2.1107.5.2 */ /* Element number for images */ #define SPI_IMAGE_ELEMENT 0x0010 /* Define spi constants */ #define SPI_TRANSVERSE_ORIENTATION 1 #define SPI_SAGITTAL_ORIENTATION 2 #define SPI_CORONAL_ORIENTATION 3 /* Element id's for SPI */ GLOBAL_ELEMENT(SPI_Acquisition_columns , 0x0019, 0x1230, LO); GLOBAL_ELEMENT(SPI_Reconstruction_columns , 0x0019, 0x1231, LO); GLOBAL_ELEMENT(SPI_Sequence_file_name , 0x0019, 0x1511, LO); GLOBAL_ELEMENT(SPI_Image_position , 0x0021, 0x1160, DS); GLOBAL_ELEMENT(SPI_Image_normal , 0x0021, 0x1161, DS); GLOBAL_ELEMENT(SPI_Image_row , 0x0021, 0x116a, DS); GLOBAL_ELEMENT(SPI_Image_column , 0x0021, 0x116b, DS); GLOBAL_ELEMENT(SPI_Number_of_3D_raw_partitions_nominal, 0x0021, 0x1330, IS); GLOBAL_ELEMENT(SPI_Number_of_3D_image_partitions , 0x0021, 0x1334, IS); GLOBAL_ELEMENT(SPI_Actual_3D_partition_number , 0x0021, 0x1336, IS); GLOBAL_ELEMENT(SPI_Number_of_slices_nominal , 0x0021, 0x1340, IS); GLOBAL_ELEMENT(SPI_Current_slice_number , 0x0021, 0x1342, IS); GLOBAL_ELEMENT(SPI_Number_of_echoes , 0x0021, 0x1370, IS); // added by rhoge: GLOBAL_ELEMENT(SPI_Registration_date , 0x0011, 0x1110, DA); GLOBAL_ELEMENT(SPI_Registration_time , 0x0011, 0x1111, TM); GLOBAL_ELEMENT(SPI_Parameter_file_name , 0x0019, 0x1510, CS); GLOBAL_ELEMENT(SPI_Protocol , 0x0029, 0x1020, CS); // (these appear to be public groups, and should be moved // to dicom_element_defs.h ? ) GLOBAL_ELEMENT(SPI_Manufacturer , 0x0008, 0x0070, CS); GLOBAL_ELEMENT(SPI_Manufacturer_model , 0x0008, 0x1090, CS); GLOBAL_ELEMENT(SPI_Device_serial_number , 0x0018, 0x1000, CS); GLOBAL_ELEMENT(SPI_Software_version , 0x0018, 0x1020, CS); GLOBAL_ELEMENT(SPI_Receiving_coil , 0x0018, 0x1250, CS); GLOBAL_ELEMENT(SPI_Calibration_date , 0x0018, 0x1200, DA); GLOBAL_ELEMENT(SPI_Total_measurement_time_cur , 0x0019, 0x1211, DS); GLOBAL_ELEMENT(SPI_Nominal_number_of_fourier_lines , 0x0019, 0x1220, IS); GLOBAL_ELEMENT(SPI_Number_of_fourier_lines_current , 0x0019, 0x1221, IS); GLOBAL_ELEMENT(SPI_Number_of_fourier_lines_after_zero , 0x0019, 0x1226, IS); GLOBAL_ELEMENT(SPI_Number_of_3d_raw_part_cur , 0x0021, 0x1331, IS); GLOBAL_ELEMENT(SPI_Order_of_slices , 0x0021, 0x134f, IS); GLOBAL_ELEMENT(SPI_First_measured_fourier_line , 0x0019, 0x1228, IS); GLOBAL_ELEMENT(SPI_Number_of_prescans , 0x0019, 0x1270, IS); GLOBAL_ELEMENT(SPI_Magnetic_field_strength , 0x0019, 0x1412, DS); GLOBAL_ELEMENT(SPI_ADC_voltage , 0x0019, 0x1414, DS); GLOBAL_ELEMENT(SPI_ADC_offset , 0x0019, 0x1416, DS); GLOBAL_ELEMENT(SPI_Transmitter_amplitude , 0x0019, 0x1420, DS); GLOBAL_ELEMENT(SPI_Receiver_amplifier_gain , 0x0019, 0x1451, DS); GLOBAL_ELEMENT(SPI_Receiver_preamplifier_gain , 0x0019, 0x1452, DS); GLOBAL_ELEMENT(SPI_Phase_gradient_amplitude , 0x0019, 0x1470, DS); GLOBAL_ELEMENT(SPI_Readout_gradient_amplitude , 0x0019, 0x1471, DS); GLOBAL_ELEMENT(SPI_Selection_gradient_amplitude , 0x0019, 0x1472, DS); GLOBAL_ELEMENT(SPI_Sequence_file_owner , 0x0019, 0x1512, CS); GLOBAL_ELEMENT(SPI_Sequence_description , 0x0019, 0x1513, CS); GLOBAL_ELEMENT(SPI_Number_of_slices_cur , 0x0021, 0x1341, IS); GLOBAL_ELEMENT(SPI_Window_center , 0x0028, 0x1050, IS); GLOBAL_ELEMENT(SPI_Window_width , 0x0028, 0x1051, IS); minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/Makefile0000644000175000000620000000320012574624760023331 0ustar stevestaff# -------------------------------------------------------------------- # # $Id: Makefile,v 1.6 2003-08-15 20:07:16 leili Exp $ # # $Log: Makefile,v $ # Revision 1.6 2003-08-15 20:07:16 leili # testing CVS again # # # # # MINC Makefile # Written by leili Nov. 2002 # ROOT = ../../minc ACR_LIB_DIR = ../Acr_nema include $(ACR_LIB_DIR)/Make_acrdefs include $(ROOT)/Make_machine_specific include $(ROOT)/Make_configuration # Executable names # Couple of things have been added by Leili on April 30, 2003 # dicomserver-nondebug-test , save_transferred_object_test.o PROGS = dicomserver-nondebug dcm2mnc dicomserver-debug dicomserver-nondebug2 dicomserver-nondebug-old EXTRA_OBJS = dicom_element_defs.o open_connection.o reply.o \ save_transferred_object.o use_the_files.o \ siemens_dicom_to_minc.o siemens_dicom_read.o minc_file.o \ string_to_filename.o project_file.o \ parse_dicom_groups.o siemens_to_dicom.o progress.o HEADERS = dicomserver.h dicom_prototypes.h dicom_element_defs.h \ spi_element_defs.h siemens_dicom_to_minc.h CDEFINES = -DDEBUG# cpp defines INCLUDES = -I/usr/include -I. -I$(ACR_LIB_DIR) -I$(PROG_LIB_DIR) \ -I$(VOLIO_LIB_DIR)/Include -Isiemens_include \ -I$(MINC_LIB_DIR) -I$(NETCDF_INCLUDE_DIR) LDOPT = $(CC_ACR_LIB) $(PROG_LDOPT) MANSECT = 1 #MANPAGES = $(PROGS).$(MANSECT) include $(ROOT)/progs/Make_progs dicomserver-debug.o: dicomserver-nondebug.c dicomserver-nondebug.o: dicomserver-nondebug.c #Added by Leili dicomserver-nondebug2.o: dicomserver-nondebug2.c dicomserver-nondebug-old.o: dicomserver-nondebug-old.c minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/project_file.c0000644000175000000620000001713312574624760024514 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : project_file.c @DESCRIPTION: Code to do manipulate the project files (files containing info on what to do with files for each project). @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: project_file.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:56 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include /* Function prototypes */ /************************************************/ /* Commented out by rhoge, put back in by leili */ int gethostname (char *name, size_t namelen); /************************************************/ /* ----------------------------- MNI Header ----------------------------------- @NAME : read_project_file @INPUT : project_name - name to use for project file @OUTPUT : file_prefix - string used as prefix for output files (can be NULL) output_uid - uid for created files (can be NULL). Set to INT_MIN if file not found. output_gid - gid for created files (can be NULL). Set to INT_MIN if file not found. command_line - command to execute on new file (can be NULL) maxlen_command - maximum length for command_line @RETURNS : TRUE if an error occurs, FALSE otherwise. @DESCRIPTION: Routine to read in default information for a given project. @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public int read_project_file(char *project_name, char *file_prefix, int *output_uid, int *output_gid, char *command_line, int maxlen_command) { char project_string[256]; char output_default_file[256]; char temp_file_prefix[256]; int temp_uid, temp_gid; char temp_command_line[4]; int ichar, ochar; int length, index; FILE *fp; char string[512]; int project_name_given; /* Check that the user actually wants return values */ if (file_prefix == NULL) file_prefix = temp_file_prefix; if (output_uid == NULL) output_uid = &temp_uid; if (output_gid == NULL) output_gid = &temp_gid; if ((command_line == NULL) || (maxlen_command <= 0)) { command_line = temp_command_line; maxlen_command = sizeof(temp_command_line); } /* Set some default values */ file_prefix[0] = '\0'; command_line[0] = '\0'; *output_uid = *output_gid = INT_MIN; /* Copy the project name, removing spaces */ if (project_name != NULL) length = strlen(project_name); else length = 0; for (ichar=0, ochar=0; (ichar < length) && (ochar < sizeof(project_string)-1); ichar++) { if (isprint((int) project_name[ichar]) && !isspace((int) project_name[ichar])) { project_string[ochar] = (char) toupper((int) project_name[ichar]); ochar++; } } project_string[ochar] = '\0'; /* Get the host name if there is no project string */ project_name_given = (strlen(project_string) > (size_t) 0); if (!project_name_given) (void) gethostname(project_string, sizeof(project_string) - 1); (void) sprintf(output_default_file, "%s/%s%s", OUTPUT_DEFAULT_FILE_DIR, OUTPUT_DEFAULT_FILE_PREFIX, project_string); /* Open and read the defaults file - if it isn't there then return TRUE if the caller gave a project name */ if ((fp=fopen(output_default_file, "r")) == NULL) { return project_name_given; } if (fgets(string, (int) sizeof(string), fp) == NULL) { return TRUE; } if (sscanf(string, "%s %d %d", file_prefix, output_uid, output_gid) != 3) { return TRUE; } (void) fgets(command_line, maxlen_command, fp); index = strlen(command_line) - 1; if ((index >= 0) && (command_line[index] == '\n')) command_line[index] = '\0'; (void) fclose(fp); return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_project_option_string @INPUT : (none) @OUTPUT : project_option_string - string containing list of options for project name maxlen_project_option - maximum length for the string (including '\0' at end) @RETURNS : (nothing) @DESCRIPTION: Routine to get a list of possibilities for the project name (looking for appropriately named files). @METHOD : @GLOBALS : @CALLS : @CREATED : February 14, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void get_project_option_string(char *project_option_string, int maxlen_project_option) { DIR *dirp; struct dirent *dp; int length; char *name, *filler; int compare_length; /* Set up the string */ if (maxlen_project_option > 0) { project_option_string[0] = '\0'; length = 1; } /* Open directory */ if ((dirp = opendir(OUTPUT_DEFAULT_FILE_DIR)) == NULL) return; /* Loop through directory entries */ compare_length = strlen(OUTPUT_DEFAULT_FILE_PREFIX); while ((dp = readdir(dirp)) != NULL) { /* Check for an entry with the right prefix */ if (strncmp(OUTPUT_DEFAULT_FILE_PREFIX, dp->d_name, compare_length) == 0) { /* Check for an uppercase letter */ if ((strlen(dp->d_name) > (size_t) compare_length) && (isupper(dp->d_name[compare_length]))) { name = &dp->d_name[compare_length]; /* Check that we can read the project file */ if (!read_project_file(name, NULL, NULL, NULL, NULL, 0)) { if (length > 1) filler = ", "; else filler = ""; if ((strlen(name) + length + strlen(filler)) < (size_t) maxlen_project_option) { (void) strcat(strcat(project_option_string, filler), name); length += strlen(filler) + strlen(name); } } /* We can read the project file */ } /* Found uppercase letter */ } /* Found file matching prefix */ } /* Loop over files */ } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_to_dicom.c0000644000175000000620000006100612574624760025365 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_to_dicom.c @DESCRIPTION: File containing routines to read in a Siemens vision internal file and convert it to dicom. @METHOD : @GLOBALS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : $Log: siemens_to_dicom.c,v $ @MODIFIED : Revision 1.3 2008-01-17 02:33:01 rotor @MODIFIED : * removed all rcsids @MODIFIED : * removed a bunch of ^L's that somehow crept in @MODIFIED : * removed old (and outdated) BUGS file @MODIFIED : @MODIFIED : Revision 1.2 2008/01/12 19:08:14 stever @MODIFIED : Add __attribute__ ((unused)) to all rcsid variables. @MODIFIED : @MODIFIED : Revision 1.1.1.1 2003/08/15 19:52:55 leili @MODIFIED : Leili's dicom server for sonata @MODIFIED : @MODIFIED : Revision 1.1 2001/12/31 17:28:34 rhoge @MODIFIED : adding file to repos - now needed for reading .ima files in directly @MODIFIED : @MODIFIED : Revision 1.2 2000/12/17 01:05:24 rhoge @MODIFIED : temporary activation of offset table printing macro @MODIFIED : @MODIFIED : Revision 1.1.1.1 2000/11/30 02:05:54 rhoge @MODIFIED : imported sources to CVS repository on amoeba @MODIFIED : * Revision 1.4 1998/11/16 19:54:15 neelin * Added definitions for SunOS. * * Revision 1.3 1998/11/13 16:02:09 neelin * Modifications to support packed images and asynchronous transfer. * * Revision 1.2 1997/11/04 14:31:30 neelin * *** empty log message *** * * Revision 1.1 1997/08/11 12:50:53 neelin * Initial revision * ---------------------------------------------------------------------------- */ #include #include #include #define DS_FUNCTIONS #include #include /* Constants */ #ifndef public # define public #endif #ifndef private # define private static #endif #ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 #endif #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 #endif #ifndef SEEK_SET # define SEEK_SET 0 #endif #define ACR_IMAGE_GID 0x7fe0 #define ACR_IMAGE_EID 0x10 #define SIEMENS_IMAGE_OFFSET (6*1024) #define WORLD_NDIMS 3 #define IMAGE_NDIMS 2 /* Macros */ #define MALLOC(size) ((void *) malloc(size)) #define FREE(ptr) free(ptr) #define REALLOC(ptr, size) ((void *) realloc(ptr, size)) #define CALLOC(nelem, elsize) ((void *) calloc(nelem, elsize)) /* Conversion functions that are not defined */ #define create_calculation_mode_t_element NULL #define create_compression_code_t_element NULL #define create_contrast_t_element NULL #define create_data_object_subtype_t_element NULL #define create_data_set_subtype_t_element NULL #define create_field_of_view_t_element NULL #define create_filter_parameter_t_element NULL #define create_filter_type_image_t_element NULL #define create_filter_type_t_element NULL #define create_gate_phase_t_element NULL #define create_geometry_t_element NULL #define create_gradient_delay_time_t_element NULL #define create_image_format_t_element NULL #define create_laterality_t_element NULL #define create_measurement_mode_t_element NULL #define create_object_orientation_t_element NULL #define create_object_threshold_t_element NULL /* #define create_order_of_slices_t_element NULL */ #define create_patient_orientation_t_element NULL #define create_patient_phase_t_element NULL #define create_patient_position_t_element NULL #define create_patient_region_t_element NULL #define create_pixel_quality_code_t_element NULL #define create_pixel_quality_value_t_element NULL #define create_reference_t_element NULL #define create_rest_direction_t_element NULL #define create_rotation_direction_t_element NULL #define create_sar_sed_t_element NULL #define create_save_code_t_element NULL #define create_storage_mode_t_element NULL #define create_study_type_t_element NULL #define create_target_point_t_element NULL #define create_view_direction_t_element NULL #define create_window_style_t_element NULL /* Define element id's */ DEFINE_ELEMENT(static, ACR_Rows , 0x0028, 0x0010, US); DEFINE_ELEMENT(static, ACR_Columns , 0x0028, 0x0011, US); DEFINE_ELEMENT(static, ACR_Flip_angle , 0x0018, 0x1314, DS); DEFINE_ELEMENT(static, SPI_Flip_angle , 0x0019, 0x1260, DS); DEFINE_ELEMENT(static, ACR_Series , 0x0020, 0x0011, IS); DEFINE_ELEMENT(static, ACR_Image_position , 0x0020, 0x0032, DS); DEFINE_ELEMENT(static, ACR_Image_orientation , 0x0020, 0x0037, DS); DEFINE_ELEMENT(static, ACR_Image_location , 0x0028, 0x0200, IS); DEFINE_ELEMENT(static, SPI_Image_position , 0x0021, 0x1160, DS); DEFINE_ELEMENT(static, SPI_Image_row , 0x0021, 0x116a, DS); DEFINE_ELEMENT(static, SPI_Image_column , 0x0021, 0x116b, DS); DEFINE_ELEMENT(static, ACR_Pixel_spacing , 0x0028, 0x0030, DS); /* Types */ typedef Acr_Element (*Create_Element_Function) (int group_id, int element_id, void *data, int length); typedef struct { int group_id; int element_id; void *data; Create_Element_Function function; int length; } Siemens_header_entry; /* Functions */ public Acr_Group siemens_to_dicom(char *filename, int read_image); public void update_coordinate_info(Acr_Group group_list); private Acr_Element_Id get_elid(int group_id, int element_id, Acr_VR_Type vr_code); private Acr_Element create_char_element(int group_id, int element_id, void *data, int length); private Acr_Element create_long_element(int group_id, int element_id, void *data, int length); private Acr_Element create_short_element(int group_id, int element_id, void *data, int length); private Acr_Element create_double_element(int group_id, int element_id, void *data, int length); private Acr_Element create_ds_date_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_ds_time_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_modality_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_sex_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_order_of_slices_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_pixel_size_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_windows_t_element(int group_id, int element_id, void *data, int length); private Acr_Element create_image_location_t_element(int group_id, int element_id, void *data, int length); /* Define the table of header values */ header_t Siemens_header; #include /* flag to print offset table, useful in debugging byte pad issues with Linux/sun porting */ /* #define PRINT_OFFSET_TABLE */ /* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_to_dicom @INPUT : filename - name of Siemens internal file read_image - if TRUE, then the image is added to the group list, otherwise it is not read in. @OUTPUT : (none) @RETURNS : Acr-nema group list containing contents of Siemens file @DESCRIPTION: Function to read in a siemens internal format file and store it in an ACR-NEMA group list @METHOD : @GLOBALS : @CALLS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group siemens_to_dicom(char *filename, int read_image) { FILE *fp; Siemens_header_entry *entry; Acr_Group group_list; Acr_Element element; long image_size, pixel_size; void *image; double flip_angle; short rows_in; short cols_in; short rows; short cols; #ifdef PRINT_OFFSET_TABLE void *header_ptr; /* debug junk */ void *data_ptr; /* debug junk */ long offset; /* debug junk */ #endif /* Open the file */ if ((fp = fopen(filename, "r")) == NULL) { (void) fprintf(stderr, "Error opening file %s\n", filename); return NULL; } /* Read in the header */ if (fread(&Siemens_header, sizeof(Siemens_header), 1, fp) != 1) { (void) fprintf(stderr, "Error reading header in %s\n", filename); (void) fclose(fp); return NULL; } /* Get the image if it is needed */ if (read_image) { /* Figure out how much space we need for the image */ pixel_size = 2; rows_in = Siemens_header.G28.Pre.Rows; acr_get_short(ACR_BIG_ENDIAN,1, (short *) &rows_in, &rows); cols_in = Siemens_header.G28.Pre.Columns; acr_get_short(ACR_BIG_ENDIAN,1, (short *) &cols_in, &cols); /* need to byte swap row/col values if needed */ image_size = rows*cols; image = MALLOC((size_t) pixel_size * image_size); /* Read in the image */ if (fseek(fp, (long) SIEMENS_IMAGE_OFFSET, SEEK_SET)) { (void) fprintf(stderr, "Error finding image in %s\n", filename); (void) fclose(fp); return NULL; } if (fread(image, pixel_size, image_size, fp) != image_size) { (void) fprintf(stderr, "Error reading image in %s\n", filename); (void) fclose(fp); return NULL; } } /* If read_image */ /* Close the file */ (void) fclose(fp); /* Loop through the header table, creating a header */ group_list = NULL; for (entry = Siemens_header_table; entry->data != NULL; entry++) { #ifdef PRINT_OFFSET_TABLE data_ptr = entry->data; header_ptr = &Siemens_header; offset = (long) data_ptr - (long) header_ptr; printf("DEBUG: group = 0x%x, element = 0x%x, offset = 0x%x, length = 0x%x\n", entry->group_id,entry->element_id,offset,entry->length); #endif if (entry->function == NULL) { continue; } element = entry->function(entry->group_id, entry->element_id, entry->data, entry->length); if (element == NULL) { continue; } acr_insert_element_into_group_list(&group_list, element); } /* Insert flip angle element */ element = acr_find_group_element(group_list, SPI_Flip_angle); if (element != NULL) { flip_angle = acr_get_element_numeric(element); if (flip_angle >= 0.0) { acr_insert_numeric(&group_list, ACR_Flip_angle, flip_angle); } } /* Insert a series number */ acr_insert_numeric(&group_list, ACR_Series, 1.0); /* Insert appropriate image position and orientation information */ update_coordinate_info(group_list); /* Add the image if it is needed */ if (read_image) { /* Insert the image location */ acr_insert_short(&group_list, ACR_Image_location, ACR_IMAGE_GID); /* Add the image. We don't byte-swap here since it will be done automatically when the data is written out. */ element = acr_create_element(ACR_IMAGE_GID, ACR_IMAGE_EID, ACR_VR_OW, image_size * pixel_size, image); acr_insert_element_into_group_list(&group_list, element); /* explicitly label image data as big-endian */ acr_set_element_byte_order(element, ACR_BIG_ENDIAN); } /* If read_image */ /* Return the group list */ return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_coordinate_info @INPUT : group_list @OUTPUT : group_list @RETURNS : (nothing) @DESCRIPTION: Function to modify the DICOM coordinate information to match the Siemens info. @METHOD : @GLOBALS : @CALLS : @CREATED : November 9, 1998 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void update_coordinate_info(Acr_Group group_list) { Acr_Element element; int nrows, ncolumns, idim; double coord[WORLD_NDIMS], row[WORLD_NDIMS], column[WORLD_NDIMS]; double pixel_spacing[IMAGE_NDIMS]; char string[256]; /* Look for the row vector */ element = acr_find_group_element(group_list, SPI_Image_row); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, row) != WORLD_NDIMS)) { row[0] = 1.0; row[1] = 0.0; row[2] = 0.0; } /* Look for the column vector */ element = acr_find_group_element(group_list, SPI_Image_column); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, column) != WORLD_NDIMS)) { column[0] = 0.0; column[1] = 1.0; column[2] = 0.0; } /* Put in the dicom orientation (patient) field */ (void) sprintf(string, "%.15g\\%.15g\\%.15g\\%.15g\\%.15g\\%.15g", row[0], -row[1], -row[2], column[0], -column[1], -column[2]); acr_insert_string(&group_list, ACR_Image_orientation, string); /* Look for the position */ element = acr_find_group_element(group_list, SPI_Image_position); if ((element == NULL) || (acr_get_element_numeric_array(element, WORLD_NDIMS, coord) != WORLD_NDIMS)) { coord[0] = 0.0; coord[1] = 0.0; coord[2] = 0.0; } /* Get the number of rows and columns */ nrows = acr_find_int(group_list, ACR_Rows, 0); ncolumns = acr_find_int(group_list, ACR_Columns, 0); if ((nrows <= 0) || (ncolumns <= 0)) { (void) fprintf(stderr, "Illegal image size in Siemens file\n"); exit(EXIT_FAILURE); } /* Get the pixel size */ element = acr_find_group_element(group_list, ACR_Pixel_spacing); if ((element == NULL) || (acr_get_element_numeric_array(element, IMAGE_NDIMS, pixel_spacing) != IMAGE_NDIMS)) { pixel_spacing[0] = pixel_spacing[1] = 1.0; } /* Calculate the position of the first pixel. This coordinate is still in the Siemens space, not dicom space and will need to be flipped. Note that ncolumns is used with row, since they are the size and unit vector of the same dimension. */ for (idim = 0; idim < WORLD_NDIMS; idim++) { coord[idim] -= pixel_spacing[0] * ((double) ncolumns - 1.0) / 2.0 * row[idim] + pixel_spacing[1] * ((double) nrows - 1.0) / 2.0 * column[idim]; } (void) sprintf(string, "%.15g\\%.15g\\%.15g", coord[0], -coord[1], -coord[2]); acr_insert_string(&group_list, ACR_Image_position, string); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create__element @INPUT : group_id element_id data - pointer to data in Siemens header length - number of values in array (if appropriate) @OUTPUT : (none) @RETURNS : New element containing data @DESCRIPTION: Series of functions to convert Siemens vision header types to ACR-NEMA elements @METHOD : @GLOBALS : @CALLS : @CREATED : July 8, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private Acr_Element_Id get_elid(int group_id, int element_id, Acr_VR_Type vr_code) { static struct Acr_Element_Id elid_struct = {0, 0, ACR_VR_UNKNOWN}; elid_struct.group_id = group_id; elid_struct.element_id = element_id; elid_struct.vr_code = vr_code; return &elid_struct; } private Acr_Element create_char_element(int group_id, int element_id, void *data, int length) /* ARGSUSED */ { char *old, *new; int oldsize, newsize, i; /* Get a pointer to the old string */ old = (char *) data; /* Figure out the length of the new string up to the first NUL. Make sure that there is a room for an additional NUL if necessary */ for (i=0; (i < length-1) && (old[i] != '\0'); i++) {} newsize = ((old[i] == '\0') ? i+1 : length + 1); oldsize = newsize - 1; if ((newsize % 2) != 1) newsize++; /* Copy the string, making sure that there is a NUL on the end */ new = MALLOC(newsize); for (i=0; i < newsize-1; i++) { if (i < oldsize) new[i] = old[i]; else new[i] = ' '; } new[newsize-1] = '\0'; /* Create the element */ return acr_create_element(group_id, element_id, ACR_VR_ST, (long) newsize-1, (void *) new); } private Acr_Element create_long_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { long data_out; acr_get_long(ACR_BIG_ENDIAN,1, data, &data_out); return acr_create_element_numeric (get_elid(group_id, element_id, ACR_VR_IS), (long) data_out); } private Acr_Element create_short_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { unsigned short data_out; acr_get_short(ACR_BIG_ENDIAN,1, data, &data_out); return acr_create_element_short (get_elid(group_id, element_id, ACR_VR_US), (unsigned short) data_out); } private Acr_Element create_double_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { double data_out; acr_get_double(ACR_BIG_ENDIAN,1, data, &data_out); return acr_create_element_numeric (get_elid(group_id, element_id, ACR_VR_DS), (double) data_out); } private Acr_Element create_ds_date_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { char string[20]; ds_date_t *ptr; long year; long month; long day; long year_in; long month_in; long day_in; ptr = (ds_date_t *) data; year_in = ptr->Year; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &year_in, &year); month_in = ptr->Month; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &month_in, &month); day_in = ptr->Day; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &day_in, &day); if ((year < 0) || (year > 9999)) return NULL; if ((month < 0) || (month > 12)) return NULL; if ((day < 0) || (day > 40)) return NULL; (void) sprintf(string, "%04d%02d%02d", (int) year, (int) month, (int) day); return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_DA), string); } private Acr_Element create_ds_time_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { char string[20]; ds_time_t *ptr; long hour; long minute; long second; long fraction; long hour_in; long minute_in; long second_in; long fraction_in; ptr = (ds_time_t *) data; hour_in = ptr->Hour; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &hour_in, &hour); minute_in = ptr->Minute; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &minute_in, &minute); second_in = ptr->Second; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &second_in, &second); fraction_in = ptr->Fraction; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &fraction_in, &fraction); if ((hour < 0) || (hour > 24)) return NULL; if ((minute < 0) || (minute > 60)) return NULL; if ((second < 0) || (second > 60)) return NULL; if ((fraction < 0) || (fraction > 999)) return NULL; (void) sprintf(string, "%02d%02d%02d.%03d", hour, minute, second, fraction); return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_TM), string); } private Acr_Element create_modality_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { char *string; modality_t *ptr_in; modality_t modality; /* modality_t *ptr; */ /* Get the appropriate string */ ptr_in = (modality_t *) data; acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &modality); switch (modality) { case Modality_CT: string = "CT"; break; case Modality_MR: string = "MR"; break; default: return NULL; } /* Return a new element */ return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_CS), string); } private Acr_Element create_sex_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { char *string; sex_t *ptr_in; sex_t sex; /* Get the appropriate string */ ptr_in = (sex_t *) data; acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &sex); switch (sex) { case Sex_F: string = "F "; break; case Sex_M: string = "M "; break; case Sex_O: string = "O "; break; default: return NULL; } /* Return a new element */ return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_CS), string); } private Acr_Element create_order_of_slices_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { char *string; order_of_slices_t *ptr_in; order_of_slices_t order_of_slices; /* Get the appropriate string */ ptr_in = (order_of_slices_t *) data; acr_get_long(ACR_BIG_ENDIAN,1, (long *) ptr_in, (long *) &order_of_slices); switch (order_of_slices) { case Slice_Order_ASCENDING: string = "ASCENDING "; break; case Slice_Order_DECREASING: string = "DESCENDING "; break; case Slice_Order_INTERLEAVED: string = "INTERLEAVED "; break; case Slice_Order_NONE: string = "NONE "; break; case Slice_Order_UNDEFINED: string = "UNDEFINED "; break; default: return NULL; } /* Return a new element */ return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_CS), string); } private Acr_Element create_pixel_size_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { pixel_size_t *ptr; char string[64]; double row_in; double col_in; double row; double col; /* Get the pixel sizes */ ptr = (pixel_size_t *) data; row_in = ptr->Row; acr_get_double(ACR_BIG_ENDIAN,1, (double *) &row_in, &row); col_in = ptr->Col; acr_get_double(ACR_BIG_ENDIAN,1, (double *) &col_in, &col); (void) sprintf(string, "%.15g\\%.15g", row, col); return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_DS), string); } private Acr_Element create_windows_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { windows_t *ptr; char string[64]; long x_in; long y_in; long x; long y; /* Get the window info */ ptr = (windows_t *) data; x_in = ptr->X; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &x_in, &x); y_in = ptr->Y; acr_get_long(ACR_BIG_ENDIAN,1, (long *) &y_in, &y); (void) sprintf(string, "%ld\\%ld", x, y); return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_IS), string); } private Acr_Element create_image_location_t_element(int group_id, int element_id, void *data, int length) /* modified by rhoge to byte swap if needed */ /* ARGSUSED */ { image_location_t *ptr; char string[64]; double sag; double cor; double tra; double sag_in; double cor_in; double tra_in; /* Get the coordinate */ ptr = (image_location_t *) data; sag_in = ptr->Sag; acr_get_double(ACR_BIG_ENDIAN,1, (double *) &sag_in, &sag); cor_in = ptr->Cor; acr_get_double(ACR_BIG_ENDIAN,1, (double *) &cor_in, &cor); tra_in = ptr->Tra; acr_get_double(ACR_BIG_ENDIAN,1, (double *) &tra_in, &tra); (void) sprintf(string, "%.15g\\%.15g\\%.15g", sag, cor, tra); return acr_create_element_string (get_elid(group_id, element_id, ACR_VR_DS), string); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/reply.c0000644000175000000620000005311412574624760023201 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : reply.c @DESCRIPTION: Routines for dealing with dicom messages. @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: reply.c,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.2 2001/02/26 06:14:39 rhoge * modified to allow target directory to be passed as AE title (only 16 chars) * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.3 1999/10/29 17:51:57 neelin * Fixed Log keyword * * Revision 6.2 1999/08/05 20:01:16 neelin * Check for broken Siemens software using a list of implementation UIDs. * * Revision 6.1 1998/05/19 19:27:43 neelin * Test for Siemens Vision machine by looking for implementation uid * rather than AE title * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.1 1997/07/08 23:15:09 neelin * Added support for C_ECHO command. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 extern int Do_logging; /* List of implementation UIDs for Siemens Vision scanners with broken handling of transfer syntax. These should be in ascending order of software version, since the UID for version VB33A is used to identify a change in element use. */ static char *SPI_Vision_Implementation_UIDs[] = { "2.16.840.1.113669.2.931128", "1.3.12.2.1107.5.1995.1", /* Version VB33A */ /* Added By Leili */ "1.3.12.2.1107.5.2", /* Version MREASE_VA21A */ NULL }; /* Index into above array for version VB33A */ #define SPI_VISION_VB33A_INDEX 1 /* Global to indicate whether we have a pre VB33A version */ int SPI_Vision_version_pre33A = TRUE; /* Macros */ #define SAVE_SHORT(group, elid, value) \ acr_group_add_element(group, \ acr_create_element_short(elid, (unsigned short) (value))) /* ----------------------------- MNI Header ----------------------------------- @NAME : uid_equal @INPUT : uid1 uid2 @OUTPUT : (nothing) @RETURNS : TRUE if uid's are equal, FALSE otherwise @DESCRIPTION: Responds to READYq message @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private int uid_equal(char *uid1, char *uid2) { int len1, len2, i; len1 = strlen(uid1); len2 = strlen(uid2); /* Skip leading blanks */ while (isspace(*uid1)) {uid1++;} while (isspace(*uid2)) {uid2++;} /* Skip trailing blanks */ for (i=len1-1; (i >= 0) && isspace(uid1[i]); i++) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; for (i=len2-1; (i >= 0) && isspace(uid1[i]); i++) {} if (isspace(uid1[i+1])) uid1[i+1] = '\0'; /* Compare the strings */ return (strcmp(uid1, uid2) == 0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_message @INPUT : group_list @OUTPUT : (nothing) @RETURNS : output message. @DESCRIPTION: Convert a group list into a message. @METHOD : @GLOBALS : @CALLS : @CREATED : November 24, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ private Acr_Message make_message(Acr_Group group_list) { Acr_Group next_group, group; Acr_Message output_message; /* Create the output message */ output_message = acr_create_message(); /* Loop through groups, adding them to the message */ group = group_list; while (group != NULL) { next_group = acr_get_group_next(group); acr_set_group_next(group, NULL); acr_message_add_group(output_message, group); group = next_group; } return output_message; } /* ----------------------------- MNI Header ----------------------------------- @NAME : associate_reply @INPUT : input_message @OUTPUT : project_name - name to use for project file pres_context_id - presentation context id to use for output. If this is < 0, then an error occurred. byte_order - byte order to use for messages vr_encoding - VR encoding to use for messages maximum_length - maximum length of output PDU's @RETURNS : output_message @DESCRIPTION: Responds to an associate request message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message associate_reply(Acr_Message input_message, char **project_name, int *pres_context_id, Acr_byte_order *byte_order, Acr_VR_encoding_type *vr_encoding, long *maximum_length) { Acr_Group group, input_group; Acr_Element element, item, subitem, sublist; Acr_Element out_item, out_subitem, out_sublist, out_list; /***********************************/ /* Added by Leili */ Acr_Element my_element_test; /***********************************/ int found_best; int best_pres_context_id, cur_pres_context_id; int best_transfer_syntax_priority; int use_implicit_little_endian; char *best_abstract_syntax, *best_transfer_syntax, *cur_syntax; int impuid; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived associate request message:\n"); acr_dump_message(stderr, input_message); } /* Set default presentation context id to flag error */ *pres_context_id = -1; /* Free project_name string if needed */ if (*project_name != NULL) FREE(*project_name); /* Get the group list */ input_group = acr_get_message_group_list(input_message); /* Get the project name from the DICOM application name */ *project_name = strdup(acr_find_string(input_group, DCM_PDU_Called_Ap_title, "")); /* Check that the project file is okay. If it is not found, try the host name */ /* changed by rhoge so as not to clobber the aetitle in the unlikely event that it is being used to pass a file name (starts with `/' or `~') - this functionality is of limited usefulness, since the AE title can not be longer than 16 characters */ if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0) && !(!strncmp(*project_name,"/",1)||!strncmp(*project_name,"~",1))) { FREE(*project_name); *project_name = NULL; if (read_project_file(*project_name, NULL, NULL, NULL, NULL, 0)) { return associate_reply_reject(input_message, ACR_ASSOC_RJ_CALLED_AP_TITLE_UNREC); } } /* Check for Siemens Vision implementation that lies about its transfer syntaxes */ /**********************************************/ /* The if and else statement is commented out by leili */ // if (0) { use_implicit_little_endian = FALSE; for (impuid=0; SPI_Vision_Implementation_UIDs[impuid] != NULL; impuid++) { if (uid_equal(acr_find_string(input_group, DCM_PDU_Implementation_class_uid, ""), SPI_Vision_Implementation_UIDs[impuid])) { use_implicit_little_endian = TRUE; (void) fprintf(stderr, "just made the implicit little endian true\n "); SPI_Vision_version_pre33A = (impuid < SPI_VISION_VB33A_INDEX); break; } } // } else { //use_implicit_little_endian = FALSE; //} /**********************************************/ /* Get maximum length */ *maximum_length = acr_find_long(input_group, DCM_PDU_Maximum_length, 0L); /* Get presentation context list */ best_pres_context_id = -1; best_abstract_syntax = NULL; element = acr_find_group_element(input_group, DCM_PDU_Presentation_context_list); if ((element == NULL) || !acr_element_is_sequence(element)) { (void) fprintf(stderr, "No presentation context list found\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Loop over presentation contexts */ found_best = FALSE; for (item = (Acr_Element) acr_get_element_data(element); (item != NULL) && acr_element_is_sequence(item) && !found_best; item = acr_get_element_next(item)) { /* Get presentation context info */ sublist = (Acr_Element) acr_get_element_data(item); /* Get abstract syntax */ subitem = acr_find_element_id(sublist, DCM_PDU_Abstract_syntax); if (subitem == NULL) continue; cur_syntax = acr_get_element_string(subitem); /* Check whether this is either MR abstract syntax or we have already found one (we take the first one if we cannot find the one that we want) */ if (uid_equal(cur_syntax, FAVORITE_ABSTRACT_SYNTAX)) found_best = TRUE; else if (best_abstract_syntax != NULL) continue; /* Save the abstract syntax */ best_abstract_syntax = cur_syntax; /* Get presentation context id */ subitem = acr_find_element_id(sublist, DCM_PDU_Presentation_context_id); if (subitem != NULL) best_pres_context_id = acr_get_element_short(subitem); else { (void) fprintf(stderr, "No presentation context - internal error\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /* Look for an appropriate transfer syntax */ best_transfer_syntax = NULL; best_transfer_syntax_priority = 0; for (subitem = sublist; subitem != NULL; subitem=acr_find_element_id(acr_get_element_next(subitem), DCM_PDU_Transfer_syntax)) { /* Check for syntaxes in descending order of preference */ cur_syntax = acr_get_element_string(subitem); if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { if (best_transfer_syntax_priority < 3) { best_transfer_syntax_priority = 3; best_transfer_syntax = cur_syntax; } } else if (uid_equal(cur_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { if (best_transfer_syntax_priority < 2) { best_transfer_syntax_priority = 2; best_transfer_syntax = cur_syntax; } } else if (uid_equal(cur_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { if (best_transfer_syntax_priority < 1) { best_transfer_syntax_priority = 1; best_transfer_syntax = cur_syntax; } } } /* Loop over transfer syntaxes */ } /* Loop over presentation contexts */ /* Check for machines that lie about their ability to do different transfer syntaxes */ if (use_implicit_little_endian) { best_transfer_syntax = ACR_IMPLICIT_VR_LITTLE_END_UID; } /* Check that we found something useful */ if ((best_pres_context_id < 0) || (best_abstract_syntax == NULL) || (best_transfer_syntax == NULL)) { (void) fprintf(stderr, "Did not find understandable presentation context\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /****************************************************************************/ /* Set the transfer syntax information */ /* This part was commented out by rick, put it back in the program by leili */ if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_BIG_END_UID)) { *byte_order = ACR_BIG_ENDIAN; *vr_encoding = ACR_EXPLICIT_VR; } else if (uid_equal(best_transfer_syntax, ACR_EXPLICIT_VR_LITTLE_END_UID)) { /****************************************/ /* this two lines were in rick's version */ *byte_order = ACR_LITTLE_ENDIAN; *vr_encoding = ACR_EXPLICIT_VR; /******************************************/ } else if (uid_equal(best_transfer_syntax, ACR_IMPLICIT_VR_LITTLE_END_UID)) { *byte_order = ACR_LITTLE_ENDIAN; *vr_encoding = ACR_IMPLICIT_VR; } else { (void) fprintf(stderr, "Did not understand transfer syntax.\n"); return associate_reply_reject(input_message, ACR_ASSOC_RJ_NO_REASON); } /*************************************************************************/ /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_AC); /* Save the caller and calling AE titles */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Called_Ap_title, acr_find_string(input_group, DCM_PDU_Called_Ap_title, ""))); /****************************************/ /* Added by Leili */ my_element_test =acr_create_element_string(DCM_PDU_Called_Ap_title,acr_find_string(input_group, DCM_PDU_Called_Ap_title, "")); (void) fprintf(stderr, "This is the called Ap_title: %s \n", my_element_test->data_pointer); /*****************************************/ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Calling_Ap_title, acr_find_string(input_group, DCM_PDU_Calling_Ap_title, ""))); /****************************************/ /* Added by Leili */ my_element_test =acr_create_element_string(DCM_PDU_Calling_Ap_title,acr_find_string(input_group, DCM_PDU_Calling_Ap_title, "")); (void) fprintf(stderr, "This is the calling Ap_title: %s \n", my_element_test->data_pointer); /*****************************************/ /* Add the application context name */ acr_group_add_element(group, acr_create_element_string(DCM_PDU_Application_context, acr_find_string(input_group, DCM_PDU_Application_context, ACR_APPLICATION_CONTEXT_UID))); /* Loop over presentation contexts */ item = (Acr_Element) acr_get_element_data (acr_find_group_element(input_group, DCM_PDU_Presentation_context_list)); out_list = NULL; for (;(item != NULL); item = acr_get_element_next(item)) { if (!acr_element_is_sequence(item)) continue; /* Get presentation context info */ sublist = (Acr_Element) acr_get_element_data(item); /* Save the id */ subitem = acr_find_element_id(sublist, DCM_PDU_Presentation_context_id); if (subitem == NULL) continue; cur_pres_context_id = acr_get_element_short(subitem); /* Create the presentation context */ out_sublist = NULL; out_subitem = acr_create_element_short(DCM_PDU_Presentation_context_id, cur_pres_context_id); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Accept or reject */ out_subitem = acr_create_element_short(DCM_PDU_Result, ((cur_pres_context_id == best_pres_context_id) ? ACR_ASSOC_PR_CN_ACCEPT : ACR_ASSOC_PR_CN_REJECT)); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Add the transfer syntax */ out_subitem = acr_create_element_string(DCM_PDU_Transfer_syntax, best_transfer_syntax); out_sublist = acr_element_list_add(out_sublist, out_subitem); /* Add this context to the list */ out_item = acr_create_element_sequence(DCM_PDU_Presentation_context_reply, out_sublist); out_list = acr_element_list_add(out_list, out_item); } /* Create the presentation context list element */ element = acr_create_element_sequence(DCM_PDU_Presentation_context_reply_list, out_list); acr_group_add_element(group, element); /* Add the user information */ acr_group_add_element(group, acr_create_element_long(DCM_PDU_Maximum_length, 0L)); /* Set the presentation context id to indicate success */ *pres_context_id = best_pres_context_id; return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : associate_reply_reject @INPUT : input_message @OUTPUT : reason @RETURNS : output_message @DESCRIPTION: Responds to an associate request message with a rejection @METHOD : @GLOBALS : @CALLS : @CREATED : February 21, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public Acr_Message associate_reply_reject(Acr_Message input_message, int reason) { Acr_Group group; /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_ASSOC_RJ); /* Give the result, source and reason */ SAVE_SHORT(group, DCM_PDU_Result, ACR_ASSOC_RJ_PERM); SAVE_SHORT(group, DCM_PDU_Source, ACR_ASSOC_RJ_USER); SAVE_SHORT(group, DCM_PDU_Reason, reason); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : release_reply @INPUT : input_message @OUTPUT : (nothing) @RETURNS : output_message @DESCRIPTION: Responds to READYq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message release_reply(Acr_Message input_message) { Acr_Group group; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived release request message:\n"); acr_dump_message(stderr, input_message); } /* Create the reply */ group = acr_create_group(DCM_PDU_GRPID); /* Save the PDU type */ SAVE_SHORT(group, DCM_PDU_Type, ACR_PDU_REL_RP); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : abort_reply @INPUT : input_message @OUTPUT : (nothing) @RETURNS : output_message @DESCRIPTION: Responds to GCENDq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message abort_reply(Acr_Message input_message) { Acr_Group group; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived abort message:\n"); acr_dump_message(stderr, input_message); } /* Create the reply */ group = acr_copy_group_list(acr_get_message_group_list(input_message)); return make_message(group); } /* ----------------------------- MNI Header ----------------------------------- @NAME : data_reply @INPUT : input_message file_prefix @OUTPUT : new_file_name (must be freed by caller) @RETURNS : output_message @DESCRIPTION: Responds to SENDq message @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Message data_reply(Acr_Message input_message) { Acr_Group group, input_group; int reply_command; /* Print log message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "\n\nReceived data message:\n"); acr_dump_message(stderr, input_message); } /* Get the input group list */ input_group = acr_get_message_group_list(input_message); /* Figure out the reply that we need */ switch (acr_find_short(input_group, ACR_Command, -1)) { case ACR_C_STORE_RQ: reply_command = ACR_C_STORE_RSP; break; case ACR_C_ECHO_RQ: reply_command = ACR_C_ECHO_RSP; break; default: reply_command = ACR_C_ECHO_RSP; break; } /* Create the reply */ group = acr_create_group(ACR_MESSAGE_GID); /* Save appropriate stuff */ acr_group_add_element(group, acr_create_element_string(ACR_Affected_SOP_class_UID, acr_find_string(input_group, ACR_Affected_SOP_class_UID, ""))); SAVE_SHORT(group, ACR_Command, reply_command); SAVE_SHORT(group, ACR_Message_id_brt, acr_find_short(input_group, ACR_Message_id, 0)); SAVE_SHORT(group, ACR_Dataset_type, ACR_NULL_DATASET); SAVE_SHORT(group, ACR_Status, ACR_SUCCESS); if (reply_command == ACR_C_STORE_RSP) { acr_group_add_element(group, acr_create_element_string(ACR_Affected_SOP_instance_UID, acr_find_string(input_group, ACR_Affected_SOP_instance_UID, ""))); } return make_message(group); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/ext_element_defs.h0000644000175000000620000000303712574624760025364 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : ext_element_defs.h @DESCRIPTION: Element definitions for extra elements needed for mosaics, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : December 2001 (Rick Hoge) @MODIFIED : @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. ---------------------------------------------------------------------------- */ /* Element id's for EXT */ GLOBAL_ELEMENT(EXT_Mosaic_rows , 0x0023, 0x0001, LO); GLOBAL_ELEMENT(EXT_Mosaic_columns , 0x0023, 0x0002, LO); GLOBAL_ELEMENT(EXT_Slices_in_file , 0x0023, 0x0003, LO); GLOBAL_ELEMENT(EXT_Sub_image_rows , 0x0023, 0x0004, US); GLOBAL_ELEMENT(EXT_Sub_image_columns , 0x0023, 0x0005, US); GLOBAL_ELEMENT(EXT_MrProt_dump , 0x0023, 0x0006, LO); GLOBAL_ELEMENT(EXT_Diffusion_b_value , 0x0023, 0x0007, LO); minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicomserver-nondebug2.c0000644000175000000620000010603112574624760026246 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomserver.c @DESCRIPTION: Program to receive images from Siemens Vision. @GLOBALS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : Modified by R. Hoge Feb. 2000 to handle acquisition loop dynamic scans on Siemens Sonata system * $Log: dicomserver-nondebug2.c,v $ * Revision 1.3 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.9 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.8 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.7 2001/08/29 20:55:53 rhoge * added -help option * * Revision 1.6 2001/07/19 17:43:46 rhoge * added -nofork command line option to prevent forking * * Revision 1.5 2001/02/26 13:37:59 rhoge * redirect sderr to /dev/null if no logging - prevents hangups due to * printfs dumping text into connection stream? * * Revision 1.4 2001/02/26 06:17:42 rhoge * changed tmp dir assignment to be consistent, always uses stdio.h stuff. * also added command-line flags for destination dir, logging level, and * retention of dicom files (are put in dest dir - must be specified) * * Revision 1.3 2000/12/14 21:14:58 rhoge * added macro (NO_FORK) to prevent forking if uncommented * * Revision 1.2 2000/12/13 13:22:18 rhoge * experimental changes to forking code for minc file creation - should * be no change * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:55 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.2 1997/07/10 17:35:35 neelin * Changed error handling and fixed message deletion. * * Revision 4.1 1997/07/08 23:15:09 neelin * Added support for C_ECHO command. * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.2 1997/03/11 13:10:48 neelin * Working version of dicomserver. * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 #include #include #include #include /* Global for minc history */ extern char *minc_history; /* State of server. Note that DISCONNECTING is used for a high-level protocol error and TERMINATING is used for a low-level error or end of input */ typedef enum { WAITING_FOR_ASSOCIATION, WAITING_FOR_DATA, DISCONNECTING, TERMINATING } Server_state; /* stuff added by rhoge */ #define EXTREME_LOGGING 10 /* rhoge */ /* Do we do logging? */ /********************/ //int Do_logging = 0; /* Belong to rhoge, commented out by leili*/ /*added by Leili:*/ int Do_logging = #ifndef DO_HIGH_LOGGING LOW_LOGGING; # else HIGH_LOGGING; # endif /******************/ /* Changed by leili from False to True */ int Fork = TRUE; /*****************/ /* Do we keep files or are they temporary? */ static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif /******************/ /* added by Leili */ /* In what directory do we run? */ static char *run_dir = "/var/tmp"; /******************/ /* Globals for handling connection timeouts */ int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; File_Type file_type = N4DCM ; /* type of input files */ int N4_OFFSET; char *pname; int main(int argc, char *argv[]) { char *pname; Acr_File *afpin, *afpout; Acr_Status status; Server_state state; int acr_command; Acr_Group group_list; Acr_Message input_message, output_message; int exit_status; char exit_string[256]; char **file_list; Data_Object_Info **file_info_list; int num_files, num_files_alloc; static char file_prefix_string[L_tmpnam+1] = "dicomserver"; char *file_prefix = file_prefix_string; char *temp_dir; int continue_looping; FILE *fptemp; char last_file_name[256]; char *project_name = NULL; char logfilename[256]; int pdu_type; int process_files, have_extra_file; Acr_byte_order byte_order; Acr_VR_encoding_type vr_encoding; int pres_context_id; long maximum_length; pid_t server_pid, child_pid; int statptr; /* added by rhoge */ int ix; int UseArgDir = 0; char OutDir[128]; /******************/ /* Added by Leili */ long ifile; Acr_Element element; char model_name[256]; char patient_name[256]; char patient_id[256]; char reg_time[256]; char reg_date[256]; char out_dir[256]; char temp_name[256]; /***********************/ /* Get server process id */ server_pid = getpid(); /*************************/ /* Change to tmp directory - note that this will be default file destination if no aetitle or command-line specification are received (rhoge) */ /* commented out by leili */ //(void) chdir(P_tmpdir); /* added by leili */ /* change to tmp directory */ if (run_dir != NULL) { (void) chdir(run_dir); } /*****************************/ /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* read in all the input pars and file names */ for (ix = 1; ix]\nOptions:\n\t-log \n\t-fork\n\t-keep_dcm\n\t-help\n"); exit(EXIT_SUCCESS); } else { (void) fprintf(stderr,"Usage: dicomserver []\nOptions:\n\t-log \n\t-fork\n\t-keep_dcm\n\t-help\n"); exit(EXIT_FAILURE); } } /* Re-open stderr if we are logging */ if (Do_logging > NO_LOGGING) { (void) sprintf(logfilename, "dicomserver-%d.log", (int) getpid()); (void) freopen(logfilename, "w", stderr); setbuf(stderr, NULL); } else { (void) sprintf(logfilename, "/dev/null"); (void) freopen(logfilename, "w", stderr); setbuf(stderr, NULL); } // set up type of connection (Syngo?) file_type = N4DCM; // default N4_OFFSET = 0; /* Print message at start */ pname = argv[0]; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "%s: Started dicom server.\n", pname); } /* Make connection */ open_connection(argc, argv, &afpin, &afpout); /* Check that the connection was made */ if ((afpin == NULL) || (afpout == NULL)) { (void) fprintf(stderr, "%s: Error opening connection.\n", pname); exit(EXIT_FAILURE); } /* Print connection message */ if (Do_logging >= HIGH_LOGGING) { (void) fprintf(stderr, "%s: Connection accepted.\n", pname); } #ifdef DO_INPUT_TRACING /* Enable input tracing */ acr_dicom_enable_trace(afpin); acr_dicom_enable_trace(afpout); #endif /* Create file prefix. Create the temporary file to avoid file name clashes */ /*****************************/ /* Commented out by leili */ //temp_dir = NULL; //if ( !Keep_files) { // (void) tmpnam(file_prefix); // if (mkdir(file_prefix, (mode_t) 0777)) { // (void) fprintf(stderr, // "%s: Unable to create directory for temporary files.\n", // pname); // perror(pname); // exit(EXIT_FAILURE); // } // temp_dir = strdup(file_prefix); // (void) strcat(file_prefix, "/dicom"); //} // else { // file_prefix=strdup(OutDir); // if (mkdir(file_prefix, (mode_t) 0777)) { // (void) fprintf(stderr, // "Directory %s exists...\n",file_prefix); // } // temp_dir = strdup(file_prefix); // (void) strcat(file_prefix, "/dicom"); // } /****************************/ /* Added by Leili */ temp_dir = NULL; if (! Keep_files){ temp_dir = tempnam(NULL, NULL); if(mkdir(temp_dir, (mode_t) 0777)){ (void) fprintf(stderr, "%s: Unable to create directory for temporary files.\n", pname); perror(pname); exit(EXIT_FAILURE); } (void) fprintf(stderr, "Temp directory for the dcm files:%s",temp_dir); (void) strcpy(file_prefix, temp_dir); (void) strcat(file_prefix, "/dicom"); } /*****************************/ (void) fprintf(stderr, "About to begin the space allocation \n"); /* Leili */ /* Get space for file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; file_list = MALLOC((size_t) num_files_alloc * sizeof(*file_list)); file_info_list = MALLOC(num_files_alloc * sizeof(*file_info_list)); /* Loop while reading messages */ state = WAITING_FOR_ASSOCIATION; continue_looping = TRUE; num_files = 0; (void) fprintf(stderr, "About to start reading a message \n"); /* Leili */ while (continue_looping) { /* Wait for any children that have finished */ while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} /* If there are children, slow down the processing */ (void) fprintf(stderr, "If we have children, slow down \n"); /* Leili */ if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } (void) fprintf(stderr, "Start reading the message\n"); /* Leili */ /* Read in the message */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); if (Do_logging > HIGH_LOGGING) fprintf(stderr,"\nWaiting for dicom message...\n"); /* rhoge */ /* chokes here at end of asynchronous transfer */ status=acr_input_dicom_message(afpin, &input_message); (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr,"\nGot message.\n"); fprintf(stderr,"\nCalling `alarm'...\n"); } (void) alarm(0); if (Do_logging > HIGH_LOGGING) fprintf(stderr,"\nAlarm called.\n"); /* rhoge */ (void) fprintf(stderr,"Checking for an error \n"); /* Leili */ /* Check for error */ if (status != ACR_OK) { continue_looping = FALSE; state = TERMINATING; break; } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Get group list */ group_list = acr_get_message_group_list(input_message); /* Get PDU type. Default is data transfer */ pdu_type = acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF); (void) fprintf(stderr,"We got the pdu and it is: %d\n", pdu_type); /* Leili */ /* Deal with PDU state */ switch (pdu_type) { /* Associate request */ case ACR_PDU_ASSOC_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\n Associate request is ACR_PDU_ASSOC_RQ\n"); if (state != WAITING_FOR_ASSOCIATION) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Work out reply and get connection info */ (void) fprintf(stderr,"About to go to reply program\n"); /* Leili */ output_message = associate_reply(input_message, &project_name, &pres_context_id, &byte_order, &vr_encoding, &maximum_length); (void) fprintf(stderr,"We finished with the reply program\n"); /* Leili */ /* Modify the input and output streams according to the connection info */ acr_set_byte_order(afpin, byte_order); acr_set_vr_encoding(afpin, vr_encoding); acr_set_byte_order(afpout, byte_order); acr_set_vr_encoding(afpout, vr_encoding); acr_set_dicom_pres_context_id(afpout, pres_context_id); acr_set_dicom_maximum_length(afpout, maximum_length); /* Get ready for files */ num_files = 0; state = WAITING_FOR_DATA; break; /* Release */ case ACR_PDU_REL_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_REL_RQ\n"); if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } output_message = release_reply(input_message); state = TERMINATING; process_files = TRUE; if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nReceived release request; process flag set.\n"); break; /* Abort */ case ACR_PDU_ABORT_RQ: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_ABORT_RQ\n"); output_message = abort_reply(input_message); state = TERMINATING; break; /* Data transfer */ case ACR_PDU_DATA_TF: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is ACR_PDU_DATA_TF\n"); /* Check state */ if (state != WAITING_FOR_DATA) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Check command and compose a reply */ acr_command = acr_find_short(group_list, ACR_Command, -1); switch (acr_command) { case ACR_C_STORE_RQ: case ACR_C_ECHO_RQ: output_message = data_reply(input_message); break; default: status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Carry on only if we have a store command */ if (acr_command != ACR_C_STORE_RQ) break; /* Get rid of the command groups */ group_list = skip_command_groups(group_list); /* Was the data attached to the command? If not, read in the next message - it should contain the data */ if (group_list == NULL) { /* Delete the previous message */ if (input_message != NULL) acr_delete_message(input_message); /* Read the data and check the status */ Alarmed_afp = afpin; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status=acr_input_dicom_message(afpin, &input_message); (void) alarm(0); if (status != ACR_OK) { state = DISCONNECTING; break; } /* Check that we have a data PDU */ group_list = acr_get_message_group_list(input_message); if (acr_find_short(group_list, DCM_PDU_Type, ACR_PDU_DATA_TF) != ACR_PDU_DATA_TF) { status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* Skip command groups and check for no data */ group_list = skip_command_groups(group_list); if (group_list == NULL) break; } (void) fprintf(stderr,"End of while loop, we are out of the loop \n"); /* Leili */ /* Extend file list if necessary */ if (num_files >= num_files_alloc) { num_files_alloc = num_files + FILE_ALLOC_INCREMENT; file_list = REALLOC(file_list, num_files_alloc * sizeof(*file_list)); file_info_list = REALLOC(file_info_list, num_files_alloc * sizeof(*file_info_list)); } file_list[num_files] = NULL; file_info_list[num_files] = MALLOC(sizeof(*file_info_list[num_files])); /* Save the object */ save_transferred_object(group_list, file_prefix, &file_list[num_files], file_info_list[num_files]); (void) fprintf(stderr,"Finished saving transfered object number %d \n", (num_files)+1); /* Leili */ num_files++; if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, " Copied %s\n", file_list[num_files-1]); } (void) fprintf(stderr,"Number of files are: %d \n", num_files); /* Leili */ if (Do_logging > HIGH_LOGGING) { /* rhoge */ fprintf(stderr, "About to check if we're done file group\n"); fprintf(stderr,"\nnum_files: \t%d",num_files); fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", file_info_list[num_files-1]->study_id); fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", file_info_list[0]->study_id); fprintf(stderr,"\nfile_info_list[num_files-1]->acq_id: \t%d", file_info_list[num_files-1]->acq_id); fprintf(stderr,"\nfile_info_list[0]->acq_id: \t%d", file_info_list[0]->acq_id); } /* Check whether we have reached the end of a group of files */ if (num_files > 1) { if ((file_info_list[num_files-1]->study_id != file_info_list[0]->study_id) || (file_info_list[num_files-1]->acq_id != file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nend of file group detected, process flag set.\n"); } } if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\ndone checking for end of file group\n"); break; /* Unknown command */ default: if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nAssociate request is default (an error)\n"); status = ACR_HIGH_LEVEL_ERROR; state = DISCONNECTING; break; } /* End of switch on pdu_type */ (void) fprintf(stderr,"We break and came out since we didn't have the store command\n"); /* Leili */ /* Delete input message */ if (input_message != NULL) acr_delete_message(input_message); /* Use the files if we have a complete acquisition */ if (process_files) { if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr,"\nEntered the `process files' block...\n"); /* Log the fact */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\nCopied one acquisition.\n"); } /* Check for file from next acquisition */ if (have_extra_file) num_files--; /* uncomment to prevent forking */ /* #define NO_FORK */ if (Fork) { /* Fork child to process the files */ child_pid = fork(); } else { child_pid = 0; } if (child_pid > 0) { /* Parent process */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Forked process to create minc files.\n"); } } /* Error forking */ else if (child_pid < 0) { (void) fprintf(stderr, "Error forking child to create minc file\n"); return; } else { /* Child process */ /*************************************************/ /* Added by Leili */ /* Close file descriptors to avoid buffering problems. STDERR is sometimes left open, therefore this command maybe needed */ if(Fork){ int fd; for(fd=getdtablesize()-1; fd>= 0; fd--) { if(fd != 2){ (void) close(fd); } } } /*******************************************************/ /* Added by leili to check the manfucaturer model names */ printf("Checking file types... "); for (ifile = 0; ifile < num_files; ifile++) { char dicm_test_string[5]; fptemp = fopen(file_list[ifile], "r"); if (fptemp == NULL) { fprintf(stderr,"Error opening file %s!\n",file_list[ifile]); exit(EXIT_FAILURE); } /* Numaris 4 DICOM CD/Export file? if so, bytes 129-132 will contain the string `DICM' */ fseek(fptemp,128,SEEK_SET); fread(dicm_test_string,1,4,fptemp); dicm_test_string[4] = (char) '\0'; // Added by leili to make the program works for data form the old vision system // To do this, we should figure out the manufacturer model from the dicom header // if the machine is the old vision, create the output directory // then call the dicom_to_minc script to do the conversion group_list = read_siemens_dicom(file_list[ifile],100); element = acr_find_group_element(group_list, ACR_Manufacturer_model); if (element != NULL) { string_to_filename(acr_get_element_string(element), model_name, sizeof(model_name));} if ((element == NULL) || (strlen(model_name) == 0)) (void) strcpy(model_name, "unknown"); (void) fprintf(stderr," The model_name is %s\n", model_name); //************************************************************* //(void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_id: \t%.6f", // file_info_list[num_files-1]->study_id); (void) fprintf(stderr,"\nfile_info_list[0]->study_id: \t%.6f", file_info_list[0]->study_id); (void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_date: \t%5d", file_info_list[num_files-1]->study_date); (void) fprintf(stderr,"\nfile_info_list[num_files-1]->study_time: \t%5d", file_info_list[num_files-1]->study_time); if ((strcmp(model_name, "sonatavision") == 0) && (!strncmp(dicm_test_string,"DICM",4)) || ((file_info_list[0]->study_id > 20020601.0000) && (!strncmp(dicm_test_string,"DICM",4)))){ file_type = N4DCM; N4_OFFSET = 1; (void) fprintf(stderr,"assuming remaining files are Syngo DICOM (CD/Export).\n"); (void) fclose(fptemp); break; // break out of file checking loop }else if ((strcmp(model_name, "magnetom_vision") == 0) || (file_info_list[0]->study_id <= 20020601.0000)) { file_type = IMA; (void) fprintf(stderr,"assuming remaining files are IMA!\n"); (void) fclose(fptemp); break; // break out of file checking loop }else { file_type = N4DCM; N4_OFFSET = 0; (void) fprintf(stderr,"assuming remaining files are Syngo DICOM.\n"); (void) fclose(fptemp); break; // break out of file checking loop } // end of file type check (void) fclose(fptemp); } // end of loop over files to check for mixed file types if (file_type == N4DCM) { // read up to but not including pixel data (void) fprintf(stderr, "We are about to do something with the files.\n"); /* Leili */ (void) fprintf(stderr, "The project name is:%s\n", project_name); /* Leili */ (void) fprintf(stderr, "The pname is:%s\n", pname); /* Leili */ use_the_files(project_name, num_files, file_list, file_info_list,UseArgDir,OutDir); } /******************************************************************************/ /* Added by leili for the conversion of dicom data from the old vision machine */ else if (file_type == IMA) { strcat(out_dir, "/data/fmri/transfer/images/"); (void) sprintf(out_dir,"/data/fmri/transfer/images/MagnetomVision_%f",file_info_list[0]->study_id); group_list = read_siemens_dicom(file_list[ifile],100); element = acr_find_group_element(group_list, ACR_Patient_name); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_name, sizeof(patient_name)); strcat(out_dir, "_"); strcat(out_dir, patient_name); strcat(out_dir, "_"); } if ((element == NULL) || (strlen(patient_name) == 0)) (void) strcpy(patient_name, "unknown"); element = acr_find_group_element(group_list, ACR_Patient_identification); if (element != NULL) { string_to_filename(acr_get_element_string(element), patient_id, sizeof(patient_id)); strcat(out_dir, patient_id); strcat(out_dir, "_"); } if ((element == NULL) || (strlen(patient_id) == 0)) (void) strcpy(patient_id, "unknown"); element = acr_find_group_element(group_list, ACR_Study_date); if (element != NULL) { string_to_filename(acr_get_element_string(element), reg_date, sizeof(reg_date)); strcat(out_dir, reg_date); strcat(out_dir, "_"); } if ((element == NULL) || (strlen(reg_date) == 0)) (void) strcpy(reg_date, "unknown"); element = acr_find_group_element(group_list, ACR_Study_time); if (element != NULL) { string_to_filename(acr_get_element_string(element), reg_time, sizeof(reg_time)); strcat(out_dir, reg_time); } if ((element == NULL) || (strlen(reg_time) == 0)) (void) strcpy(reg_time, "unknown"); mkdir(out_dir,(mode_t) 0777); //execl("/sbin/cat", "/sbin/cat", "/usr/local/mni/bin", NULL); //system ("set path=($path /usr/local/mni/bin)"); // this part works fine but it's pretty ugly!!! Perhaps a better solution to the system call? strcat(temp_name,"/software/source/dicomserver_test/conversion/dicomserver/dicom_to_minc"); //strcat(temp_name,"/usr/local/mni/bin/dicom_to_minc"); strcat(temp_name," "); strcat(temp_name,out_dir); //strcat(temp_name,"/."); strcat(temp_name," "); strcat(temp_name,"-compress"); strcat(temp_name, " "); strcat(temp_name,"-inputdir"); strcat(temp_name, " "); strcat(temp_name,temp_dir); //strcat(temp_name,"/."); (void) fprintf(stderr," The temp_name is: %s\n", temp_name); system (temp_name); /* Remove the temporary files */ cleanup_files(num_files, file_list); /* Remove the temporary directory if the server has finished */ cleanup_files(1, &temp_dir); exit(0); } /* Remove the temporary files */ cleanup_files(num_files, file_list); /* Remove the temporary directory if the server has finished */ /* (this does not seem to work all the time) */ if ((temp_dir != NULL) && (kill(server_pid, 0) != 0)) { cleanup_files(1, &temp_dir); } /* Print message about child finishing */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "Minc creation process finished.\n"); } if (Fork) { /* Exit from child */ exit(EXIT_SUCCESS); } } /* End of child process */ /* Put blank line in log file */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n"); } /* Reset the lists */ free_list(num_files, file_list, file_info_list); if (have_extra_file) { file_list[0] = file_list[num_files]; file_info_list[0] = file_info_list[num_files]; file_list[num_files] = NULL; file_info_list[num_files] = NULL; } num_files = (have_extra_file ? 1 : 0); } // end of if(process files) if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\n just passed `process files' loop\n"); /* Check for disconnection */ if (state == DISCONNECTING) { continue_looping = FALSE; break; } /* Send reply */ Alarmed_afp = afpout; (void) signal(SIGALRM, timeout_handler); (void) alarm(CONNECTION_TIMEOUT); status = acr_output_dicom_message(afpout, output_message); (void) alarm(0); /* Delete output message */ if (output_message != NULL) acr_delete_message(output_message); if (status != ACR_OK) { state = TERMINATING; break; } if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\nbottom of loop over messages\n"); } /* End of loop over messages */ if (Do_logging > HIGH_LOGGING) /* rhoge */ fprintf(stderr, "\njust out of loop over messages\n"); /* Free the input and output streams */ acr_close_dicom_file(afpin); acr_close_dicom_file(afpout); /* Save name of first file in last set transferred */ if ((num_files > 0) && (file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name, file_list[0], sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } /* Clean up files, if needed */ if (num_files > 0) { cleanup_files(num_files, file_list); free_list(num_files, file_list, file_info_list); num_files = 0; } FREE(file_list); FREE(file_info_list); /* Remove the file prefix directory (this only happens if it is empty). */ cleanup_files(1, &temp_dir); FREE(temp_dir); /* Check for connection timeout */ if (Connection_timeout) { (void) fprintf(stderr, "Connection timed out.\n"); } /* Print final message */ if ((status == ACR_OK) || (status == ACR_END_OF_INPUT)) { (void) sprintf(exit_string, "Finished transfer."); exit_status = EXIT_SUCCESS; } else { (void) sprintf(exit_string, "%s. Disconnecting.", acr_status_string(status)); exit_status = EXIT_FAILURE; } (void) fprintf(stderr, "The logging set to be %d\n", Do_logging); /* Leili */ (void) fprintf(stderr, "The status of the message is: %s \n", acr_status_string(status)); /* Leili */ if (Do_logging >= LOW_LOGGING) { (void) fprintf(stderr, "\n%s: %s\n", pname, exit_string); } if ((status != ACR_OK) && (status != ACR_END_OF_INPUT)) { if (SYSTEM_LOG != NULL) { if ((fptemp = fopen(SYSTEM_LOG, "w")) != NULL) { if ((int) strlen(last_file_name) > 0) { (void) fprintf(fptemp, "%s: File \"%s\"\n", pname, last_file_name); } (void) fprintf(fptemp, "%s: %s\n", pname, exit_string); (void) fclose(fptemp); } } } /* Free the project_name string */ if (project_name != NULL) FREE(project_name); exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : timeout_handler @INPUT : @OUTPUT : (none) @RETURNS : @DESCRIPTION: Routine to handle connection timeouts. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ /* ARGSUSED */ public void timeout_handler(int sig) { Connection_timeout = TRUE; if (Alarmed_afp != NULL) { acr_dicom_set_eof(Alarmed_afp); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : skip_command_groups @INPUT : group_list @OUTPUT : (none) @RETURNS : Pointer to head of group list @DESCRIPTION: Skips over command groups in a group list, returning the rest of the list. @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1997 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public Acr_Group skip_command_groups(Acr_Group group_list) { while ((group_list != NULL) && ((acr_get_group_group(group_list) == DCM_PDU_GRPID) || (acr_get_group_group(group_list) == ACR_MESSAGE_GID))) { group_list = acr_get_group_next(group_list); } return group_list; } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dcm2mnc2.c0000644000175000000620000005517512574624760023464 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dcm2mnc.c @DESCRIPTION: Program to convert dicom files to minc @GLOBALS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : * $Log: dcm2mnc2.c,v $ * Revision 1.3 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.5 2002/04/26 12:02:50 rhoge * updated usage statement for new forking defaults * * Revision 1.4 2002/04/26 11:32:48 rhoge * made forking default * * Revision 1.3 2002/03/23 13:17:53 rhoge * added support for Bourget network pushed dicom files, cleaned up * file check and read_numa4_dicom vr check/assignment * * Revision 1.2 2002/03/22 19:19:36 rhoge * Numerous fixes - * - handle Numaris 4 Dicom patient name * - option to cleanup input files * - command option * - list-only option * - debug mode * - user supplied name, idstr * - anonymization * * Revision 1.1 2002/03/22 03:50:02 rhoge * new name for standalone dicom to minc converter * * Revision 1.3 2002/03/22 00:38:08 rhoge * Added progress bar, wait for children at end, updated feedback statements * * Revision 1.2 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.1 2001/12/31 17:26:21 rhoge * adding file to repository- compiles without warning and converts non-mosaic * Numa 4 files. * Will probably not work for Numa 3 files yet. * ---------------------------------------------------------------------------- */ #include #include #include #include #include #include "dicomserver.h" extern char *minc_history; // Global for minc history char *pname; // program name File_Type file_type = UNDEF ; // type of input files char command_line[512]; int N4_OFFSET; // function prototypes private int ima_sort_function(const void *entry1, const void *entry2); private int dcm_sort_function(const void *entry1, const void *entry2); private int print_file_info( int ix, Data_Object_Info *info); public int progress(long index, int end, char *message); #define EXTREME_LOGGING 10 int Do_logging = 0; int Fork = 1; int Debug = 0; int Anon = 0; int UserIdStr = 0; char IdStr[512]; int UserName = 0; char Name[512]; // Do we keep files or are they temporary? // (now this is overridden by Cleanup) static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif // Globals for handling connection timeouts // (obsolete?) int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; int main(int argc, char *argv[]) { long ifile; long max_group; Acr_Group group_list; int exit_status; char **file_list; char **acq_file_list; Data_Object_Info **file_info_list; Data_Object_Info **acq_file_info_list; int num_files, num_files_alloc; int num_acq_files; FILE *fptemp; char last_file_name[256]; // delete? char *project_name = NULL; int process_files, have_extra_file; pid_t parent_pid, child_pid; int statptr; int ix; int UseArgDir = 1; char OutDir[128]; int List = 0; int Cleanup = 0; /* Get server process id */ parent_pid = getpid(); /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* get program name */ pname = argv[0]; if (argc<2) { usage(); } /* read in all the input pars and file names */ for (ix = 1; ixfile_index = ifile; if (file_type == N4DCM) { // read up to but not including pixel data max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (file_type == IMA) { group_list = siemens_to_dicom(file_list[ifile], TRUE); if (group_list == NULL) { fprintf(stderr,"Error reading groups from file %s!\n", file_list[ifile]); exit(EXIT_FAILURE); } } // get some preliminary info from group_list // (which should have been `corrected' in read_xxxx_dicom parse_dicom_groups(group_list, file_info_list[ifile]); // put the file name into the info list file_info_list[ifile]->file_name = strdup(file_list[ifile]); /* print out info about file */ print_file_info(ifile,file_info_list[ifile]); // Delete the group list now that we're done with it acr_delete_group_list(group_list); } // end of loop over files to get basic info printf("Sorting files... "); if (file_type == N4DCM) { // sort the files based on acquisition number qsort(file_info_list, num_files, sizeof(file_info_list[0]), dcm_sort_function); } else if (file_type == IMA) { // sort the files based on file name // (could also use dcm_sort_function, but would have // to use ACR_Image instead of ACR_Acquisition qsort(file_list, num_files, sizeof(file_list[0]), ima_sort_function); } printf("Done sorting files.\n"); /* Get space for acquisition file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); /* Loop over files, processing by acquisition */ if (List) { printf("Listing files by series...\n"); } else { printf("Processing files, one series at a time...\n"); } num_acq_files = 1; for (ifile = 0; ifile < num_files; ifile++) { // Wait for any children that have finished while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} // If there are children, slow down the processing if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Extend acquisition file list if necessary */ if (num_acq_files >= num_files_alloc) { num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; acq_file_list = REALLOC(acq_file_list, num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = REALLOC(acq_file_info_list, num_files_alloc * sizeof(*acq_file_info_list)); } acq_file_list[num_acq_files-1] = NULL; acq_file_info_list[num_acq_files-1] = MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); if (file_type == N4DCM) { /* read up to but not including pixel data */ max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); } else if (file_type == IMA) { group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); if (group_list == NULL) { fprintf(stderr,"Error reading groups from file %s!\n", acq_file_list[num_acq_files-1]); exit(EXIT_FAILURE); } } parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); // put the file name into the info list acq_file_info_list[num_acq_files-1]->file_name = strdup(acq_file_list[num_acq_files-1]); // print some file info (junk) print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); // Check whether we have reached the end of a group of files if (num_acq_files > 1) { if ((acq_file_info_list[num_acq_files-1]->study_id != acq_file_info_list[0]->study_id) || (acq_file_info_list[num_acq_files-1]->acq_id != acq_file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; } else if (ifile == num_files-1) { // we're at the last file process_files = TRUE; } } // Delete the group list now that we're done with it acr_delete_group_list(group_list); // Use the files if we have a complete acquisition if (process_files) { // Check for file from next acquisition if (have_extra_file) num_acq_files--; if (List) { printf("Series %4d: %30s (%4d files)\n", acq_file_info_list[0]->acq_id, acq_file_info_list[0]->protocol_name, num_acq_files); } else { printf("Converting data for series %d (%s: %d files)...\n", acq_file_info_list[0]->acq_id, acq_file_info_list[0]->protocol_name, num_acq_files); } if (Fork) { // Fork child to process the files child_pid = fork(); } else { child_pid = 0; } if (child_pid > 0) { // Parent process // printf("[Parent]: Forked process to create minc file.\n"); } // Error forking else if (child_pid < 0) { fprintf(stderr, "Error forking child to create minc file\n"); return; } else { // Child process if (!List) { // process the files (same function as server) use_the_files(project_name, num_acq_files, acq_file_list, acq_file_info_list,UseArgDir,OutDir); // Print message about child finishing printf("-File creation complete for Series %d.\n", acq_file_info_list[0]->acq_id); // Clean up files, if needed if (Cleanup) { if (num_acq_files > 0) { printf("-Removing input files... "); cleanup_files(num_acq_files, acq_file_list); printf("Done removing input files.\n"); } } } if (Fork) { // Exit from child, if forked exit(EXIT_SUCCESS); } } // End of child process // ---------------------------------------------------- // if we get here we must be the parent, who goes // along happily continuing to eat files // ---------------------------------------------------- // Reset the lists for a new series free_list(num_acq_files, acq_file_list, acq_file_info_list); // check to see if the last series ended by // running into a new series: if (have_extra_file) { // move most recent file info to first entry in array, // in preparation for next series // note that num_acq_files has already been decremented, // so we do not need to subtract 1 to convert to array index acq_file_list[0] = acq_file_list[num_acq_files]; acq_file_info_list[0] = acq_file_info_list[num_acq_files]; acq_file_list[num_acq_files] = NULL; acq_file_info_list[num_acq_files] = NULL; } // num_acq_files = (have_extra_file ? 1 : 0); num_acq_files = (have_extra_file ? 2 : 1); } else { // we're not processing the files yet - just increment counter num_acq_files++; } } // end of loop over files // Wait for child processes if we've been forking. // We sleep in the checking loop to reduce parent CPU usage if (Fork) { printf("-(waiting for child processes to finish...)\n"); while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} } if (List) { printf("Done listing files.\n"); } else { printf("Done processing files.\n"); } /* Save name of first file in last set */ if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } FREE(acq_file_list); FREE(acq_file_info_list); /* Print final message */ exit_status = EXIT_SUCCESS; exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; // if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ima_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two ima file names @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int ima_sort_function(const void *entry1, const void *entry2) { char * const *value1 = entry1; char * const *value2 = entry2; int session1,series1,image1; int session2,series2,image2; sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dcm_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two dcm series numbers @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int dcm_sort_function(const void *entry1, const void *entry2) { Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; // make a sort-able session ID number: date.time double session1 = (*file_info_list1)->study_date + (*file_info_list1)->study_time / 1e6; double session2 = (*file_info_list2)->study_date + (*file_info_list2)->study_time / 1e6; // series index int series1 = (*file_info_list1)->acq_id; int series2 = (*file_info_list2)->acq_id; // frame index int frame1 = (*file_info_list1)->dyn_scan_number; int frame2 = (*file_info_list2)->dyn_scan_number; // image index int image1 = (*file_info_list1)->global_image_number; int image2 = (*file_info_list2)->global_image_number; if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (frame1 < frame2) return -1; else if (frame1 > frame2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } private int print_file_info( int ix, Data_Object_Info *info) { if (!Debug) { return 0; } // printf("SPI_Parameter_file_name = %s\n", // acr_find_string(group_list, SPI_Parameter_file_name, "")); // printf("SPI_Order_of_slices = %s\n", // acr_find_string(group_list, SPI_Order_of_slices, "")); printf("%4s %18s\n %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", "ix","file","study id","date","time","serialno","acq", "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", "seq"); /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ printf("%4d: %18s:\n %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", ix, info->file_name, info->study_id, info->study_date, info->study_time, info->scanner_serialno, info->acq_id, info->num_echoes, info->echo_number, info->num_dyn_scans, info->dyn_scan_number, info->num_slices_nominal, info->slice_number, info->acq_cols, info->rec_cols, info->num_mosaic_rows, info->global_image_number, info->sequence_name); } void usage (void) { fprintf(stderr, "\nUsage: dcm2mnc [options] file1 file2 file3 ... destdir\n"); fprintf(stderr,"\noptions:\n"); fprintf(stderr," -help : print this informative message\n"); fprintf(stderr," -list : print list of series (don't create files)\n"); fprintf(stderr," -anon : exclude subject name from file header\n"); fprintf(stderr," -descr : use str as session descriptor (default = patient initials)\n"); fprintf(stderr," -idstr : use str as subject id string (default = patient ID)\n"); fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); fprintf(stderr," -cleanup : delete input files when done (careful!)\n"); fprintf(stderr," -cmd : apply prog to output files (e.g. gzip)\n"); fprintf(stderr," -fork : fork subprocesses to create minc files (now default)\n"); fprintf(stderr," -nofork : don't fork subprocs, do show progress for individual files\n"); fprintf(stderr," -debug : print debugging info\n"); fprintf(stderr,"\n"); fprintf(stderr,"Files are named according to the following convention:\n\n"); fprintf(stderr," Directory: descr-idstr-scanner-serialno-date-time/\n"); fprintf(stderr," Files: descr-idstr-scanner-serialno-date-time-series-modality.mnc\n\n"); exit(EXIT_FAILURE); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicomreader.c0000644000175000000620000004614112574624760024326 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicomreader.c @DESCRIPTION: Program to convert dicom files to minc @GLOBALS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : * $Log: dicomreader.c,v $ * Revision 1.3 2008-01-17 02:33:01 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 1.2 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 1.1.1.1 2003/08/15 19:52:55 leili * Leili's dicom server for sonata * * Revision 1.3 2002/03/22 00:38:08 rhoge * Added progress bar, wait for children at end, updated feedback statements * * Revision 1.2 2002/03/19 13:13:56 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.1 2001/12/31 17:26:21 rhoge * adding file to repository- compiles without warning and converts non-mosaic * Numa 4 files. * Will probably not work for Numa 3 files yet. * ---------------------------------------------------------------------------- */ #include #include #include #include #include #include "dicomserver.h" extern char *minc_history; /* Global for minc history */ char *pname; /* program name */ File_Type file_type = UNDEF ; /* type of input files */ /* function prototypes */ private int ima_sort_function(const void *entry1, const void *entry2); private int dcm_sort_function(const void *entry1, const void *entry2); private int print_file_info( int ix, Data_Object_Info *info); public int progress(long index, int end, char *message); #define EXTREME_LOGGING 10 /* rhoge */ /* Do we do logging? */ int Do_logging = 0; int NoFork = 0; /* Do we keep files or are they temporary? */ static int Keep_files = #ifndef KEEP_FILES FALSE; #else TRUE; #endif /* Globals for handling connection timeouts */ int Connection_timeout = FALSE; Acr_File *Alarmed_afp = NULL; int main(int argc, char *argv[]) { long ifile; long max_group; Acr_Group group_list; int exit_status; char **file_list; char **acq_file_list; Data_Object_Info **file_info_list; Data_Object_Info **acq_file_info_list; int num_files, num_files_alloc; int num_acq_files; FILE *fptemp; char last_file_name[256]; // delete? char *project_name = NULL; int process_files, have_extra_file; pid_t parent_pid, child_pid; int statptr; /* added by rhoge */ int ix; int UseArgDir = 1; char OutDir[128]; /* Get server process id */ parent_pid = getpid(); /* Create minc history string */ { char *string; string = "dicomserver"; minc_history = time_stamp(1, &string); } /* get program name */ pname = argv[0]; if (argc<2) { usage(); } /* read in all the input pars and file names */ for (ix = 1; ixfile_index = ifile; if (file_type == N4DCM) { // read up to but not including pixel data max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(file_list[ifile], max_group); } else if (file_type == IMA) { group_list = siemens_to_dicom(file_list[ifile], TRUE); if (group_list == NULL) { fprintf(stderr,"Error reading groups from file %s!\n", file_list[ifile]); exit(EXIT_FAILURE); } } // get some preliminary info from group_list // (which should have been `corrected' in read_xxxx_dicom parse_dicom_groups(group_list, file_info_list[ifile]); // put the file name into the info list file_info_list[ifile]->file_name = strdup(file_list[ifile]); /* print out info about file */ print_file_info(ifile,file_info_list[ifile]); // Delete the group list now that we're done with it acr_delete_group_list(group_list); } // end of loop over files to get basic info printf("Sorting files... "); if (file_type == N4DCM) { // sort the files based on acquisition number qsort(file_info_list, num_files, sizeof(file_info_list[0]), dcm_sort_function); } else if (file_type == IMA) { // sort the files based on file name // (could also use dcm_sort_function, but would have // to use ACR_Image instead of ACR_Acquisition qsort(file_list, num_files, sizeof(file_list[0]), ima_sort_function); } printf("Done sorting files.\n"); /* Get space for acquisition file lists */ num_files_alloc = FILE_ALLOC_INCREMENT; acq_file_list = MALLOC((size_t) num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = MALLOC(num_files_alloc * sizeof(*acq_file_info_list)); /* Loop over files, processing by acquisition */ printf("Processing files, one series at a time...\n"); num_acq_files = 1; for (ifile = 0; ifile < num_files; ifile++) { // Wait for any children that have finished while ((child_pid=wait3(&statptr, WNOHANG, NULL)) > 0) {} // If there are children, slow down the processing if (child_pid == 0) { (void) sleep((unsigned int) SERVER_SLEEP_TIME); } /* Set flags indicating whether we should do anything with the files and whether the file lists contain an extra file */ process_files = FALSE; have_extra_file = FALSE; /* Extend acquisition file list if necessary */ if (num_acq_files >= num_files_alloc) { num_files_alloc = num_acq_files + FILE_ALLOC_INCREMENT; acq_file_list = REALLOC(acq_file_list, num_files_alloc * sizeof(*acq_file_list)); acq_file_info_list = REALLOC(acq_file_info_list, num_files_alloc * sizeof(*acq_file_info_list)); } acq_file_list[num_acq_files-1] = NULL; acq_file_info_list[num_acq_files-1] = MALLOC(sizeof(*acq_file_info_list[num_acq_files-1])); acq_file_list[num_acq_files-1] = strdup(file_info_list[ifile]->file_name); if (file_type == N4DCM) { /* read up to but not including pixel data */ max_group = ACR_ACTUAL_IMAGE_GID - 1; group_list = read_numa4_dicom(acq_file_list[num_acq_files-1],max_group); } else if (file_type == IMA) { group_list = siemens_to_dicom(acq_file_list[num_acq_files-1], TRUE); if (group_list == NULL) { fprintf(stderr,"Error reading groups from file %s!\n", acq_file_list[num_acq_files-1]); exit(EXIT_FAILURE); } } parse_dicom_groups(group_list, acq_file_info_list[num_acq_files-1]); // put the file name into the info list acq_file_info_list[num_acq_files-1]->file_name = strdup(acq_file_list[num_acq_files-1]); // print some file info (junk) print_file_info(num_acq_files,acq_file_info_list[num_acq_files-1]); // Check whether we have reached the end of a group of files if (num_acq_files > 1) { if ((acq_file_info_list[num_acq_files-1]->study_id != acq_file_info_list[0]->study_id) || (acq_file_info_list[num_acq_files-1]->acq_id != acq_file_info_list[0]->acq_id)) { process_files = TRUE; have_extra_file = TRUE; } else if (ifile == num_files-1) { // we're at the last file process_files = TRUE; } } // Delete the group list now that we're done with it acr_delete_group_list(group_list); // Use the files if we have a complete acquisition if (process_files) { // Check for file from next acquisition if (have_extra_file) num_acq_files--; printf("Converting data for series %d (%s: %d files)...\n", acq_file_info_list[0]->acq_id, acq_file_info_list[0]->protocol_name, num_acq_files); if (NoFork) { child_pid = 0; } else { // Fork child to process the files child_pid = fork(); } if (child_pid > 0) { // Parent process // printf("[Parent]: Forked process to create minc file.\n"); } // Error forking else if (child_pid < 0) { fprintf(stderr, "Error forking child to create minc file\n"); return; } else { // Child process // process the files (same function as server) use_the_files(project_name, num_acq_files, acq_file_list, acq_file_info_list,UseArgDir,OutDir); // Print message about child finishing if (NoFork) { //printf("[Parent]: Minc creation process finished.\n"); printf("File creation complete for Series %d.\n", acq_file_info_list[0]->acq_id); } else { //printf("[Child]: Minc creation process finished.\n"); printf(" File creation complete for Series %d.\n", acq_file_info_list[0]->acq_id); } if (!NoFork) { // Exit from child, if forked exit(EXIT_SUCCESS); } } // End of child process // ---------------------------------------------------- // if we get here we must be the parent, who goes // along happily continuing to eat files // ---------------------------------------------------- // Reset the lists for a new series free_list(num_acq_files, acq_file_list, acq_file_info_list); // check to see if the last series ended by // running into a new series: if (have_extra_file) { // move most recent file info to first entry in array, // in preparation for next series // note that num_acq_files has already been decremented, // so we do not need to subtract 1 to convert to array index acq_file_list[0] = acq_file_list[num_acq_files]; acq_file_info_list[0] = acq_file_info_list[num_acq_files]; acq_file_list[num_acq_files] = NULL; acq_file_info_list[num_acq_files] = NULL; } // num_acq_files = (have_extra_file ? 1 : 0); num_acq_files = (have_extra_file ? 2 : 1); } else { // we're not processing the files yet - just increment counter num_acq_files++; } } // end of loop over files // Wait for child processes if we've been forking. // We sleep in the checking loop to reduce parent CPU usage if (!NoFork) { printf(" (waiting for child processes to finish...)\n"); while ((child_pid=wait3(&statptr, WNOHANG, NULL)) >= 0) {sleep(1);} } printf("Done processing files.\n"); /* Save name of first file in last set */ if ((num_acq_files > 0) && (acq_file_list[0] != NULL)) { last_file_name[sizeof(last_file_name) - 1] = '\0'; (void) strncpy(last_file_name,acq_file_list[0],sizeof(last_file_name)-1); } else { last_file_name[0] = '\0'; } /* Clean up files, if needed */ if (0) { if (num_acq_files > 0) { cleanup_files(num_acq_files, acq_file_list); free_list(num_acq_files, acq_file_list, acq_file_info_list); num_acq_files = 0; } } FREE(acq_file_list); FREE(acq_file_info_list); /* Print final message */ exit_status = EXIT_SUCCESS; exit(exit_status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_files @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Removes files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void cleanup_files(int num_files, char *file_list[]) { int i; if (Keep_files) return; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { (void) remove(file_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_list @INPUT : num_files - number of files in list file_list - array of file names @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Frees up things pointed to in pointer arrays. Does not free the arrays themselves. @METHOD : @GLOBALS : @CALLS : @CREATED : November 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ public void free_list(int num_files, char **file_list, Data_Object_Info **file_info_list) { int i; for (i=0; i < num_files; i++) { if (file_list[i] != NULL) { FREE(file_list[i]); } if (file_info_list[i] != NULL) { FREE(file_info_list[i]); } } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : ima_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two ima file names @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int ima_sort_function(const void *entry1, const void *entry2) { char * const *value1 = entry1; char * const *value2 = entry2; int session1,series1,image1; int session2,series2,image2; sscanf(*value1,"%d-%d-%d.ima",&session1,&series1,&image1); sscanf(*value2,"%d-%d-%d.ima",&session2,&series2,&image2); if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dcm_sort_function @INPUT : entry1 entry2 @OUTPUT : (none) @RETURNS : -1, 0, 1 for lt, eq, gt @DESCRIPTION: Function to compare two dcm series numbers @METHOD : @GLOBALS : @CALLS : @CREATED : June 2001 (Rick Hoge) @MODIFIED : ---------------------------------------------------------------------------- */ private int dcm_sort_function(const void *entry1, const void *entry2) { Data_Object_Info **file_info_list1 = (Data_Object_Info **) entry1; Data_Object_Info **file_info_list2 = (Data_Object_Info **) entry2; // make a sort-able session ID number: date.time double session1 = (*file_info_list1)->study_date + (*file_info_list1)->study_time / 1e6; double session2 = (*file_info_list2)->study_date + (*file_info_list2)->study_time / 1e6; // series index int series1 = (*file_info_list1)->acq_id; int series2 = (*file_info_list2)->acq_id; // frame index int frame1 = (*file_info_list1)->dyn_scan_number; int frame2 = (*file_info_list2)->dyn_scan_number; // image index int image1 = (*file_info_list1)->global_image_number; int image2 = (*file_info_list2)->global_image_number; if (session1 < session2) return -1; else if (session1 > session2) return 1; else if (series1 < series2) return -1; else if (series1 > series2) return 1; else if (frame1 < frame2) return -1; else if (frame1 > frame2) return 1; else if (image1 < image2) return -1; else if (image1 > image2) return 1; else return 0; } private int print_file_info( int ix, Data_Object_Info *info) { return 0; // printf("SPI_Parameter_file_name = %s\n", // acr_find_string(group_list, SPI_Parameter_file_name, "")); // printf("SPI_Order_of_slices = %s\n", // acr_find_string(group_list, SPI_Order_of_slices, "")); printf("%4s %18s %15s %8s %6s %8s %8s %3s %3s %3s %3s %3s %3s %4s %4s %4s %5s %16s\n", "ix","file","study id","date","time","serialno","acq", "nec","iec","ndy","idy","nsl","isl","acol","rcol","mrow","img#", "seq"); /* ix file stu dat tim sn acq nec iec ndy idy nsl isl */ printf("%4d: %18s %.6f %8d %6d %8d %8d %3d %3d %3d %3d %3d %3d %4d %4d %4d %5d %16s\n\n", ix, info->file_name, info->study_id, info->study_date, info->study_time, info->scanner_serialno, info->acq_id, info->num_echoes, info->echo_number, info->num_dyn_scans, info->dyn_scan_number, info->num_slices_nominal, info->slice_number, info->acq_cols, info->rec_cols, info->num_mosaic_rows, info->global_image_number, info->sequence_name); } void usage (void) { fprintf(stderr, "\nUsage: dicomreader [options] file1 file2 file3 ... destdir\n"); fprintf(stderr,"\noptions:\n"); fprintf(stderr," -help : print this informative message\n"); fprintf(stderr," -log <0|1|2|3> : set logging level (default=0)\n"); fprintf(stderr," -nofork : don't fork to create minc files\n"); exit(EXIT_FAILURE); } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/siemens_dicom_to_minc.h0000644000175000000620000001630212574624760026377 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : siemens_dicom_to_minc.h @DESCRIPTION: Header file for siemens_dicom_to_minc.h @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : * $Log: siemens_dicom_to_minc.h,v $ * Revision 1.1 2003-08-15 19:52:55 leili * Initial revision * * Revision 1.6 2002/04/26 03:27:03 rhoge * fixed MrProt problem - replaced fixed lenght char array with malloc * * Revision 1.5 2002/04/08 17:26:34 rhoge * added additional sequence info to minc header * * Revision 1.4 2002/03/27 18:57:50 rhoge * added diffusion b value * * Revision 1.3 2002/03/19 13:13:57 rhoge * initial working mosaic support - I think time is scrambled though. * * Revision 1.2 2001/12/31 18:27:21 rhoge * modifications for dicomreader processing of Numaris 4 dicom files - at * this point code compiles without warning, but does not deal with * mosaiced files. Also will probably not work at this time for Numaris * 3 .ima files. dicomserver may also not be functional... * * Revision 1.1.1.1 2000/11/30 02:13:15 rhoge * imported sources to CVS repository on amoeba * * Revision 6.1 1999/10/29 17:51:59 neelin * Fixed Log keyword * * Revision 6.0 1997/09/12 13:24:27 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:26 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:06:20 neelin * Release of minc version 0.4 * * Revision 1.1 1997/03/04 20:56:47 neelin * Initial revision * @COPYRIGHT : Copyright 1997 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 /* General constants */ #define SECONDS_PER_MINUTE 60 #define MINUTES_PER_HOUR 60 #define SECONDS_PER_HOUR (MINUTES_PER_HOUR*SECONDS_PER_MINUTE) #define HOURS_PER_DAY 24 #define SECONDS_PER_DAY (HOURS_PER_DAY*SECONDS_PER_HOUR) #define MS_PER_SECOND 1000 #define COORDINATE_EPSILON (100.0*FLT_EPSILON) /* Default value for ncopts */ #define NCOPTS_DEFAULT NC_VERBOSE /* MINC variable for dicom elements */ #define DICOM_ROOT_VAR "dicom_groups" /* Possible MRI dimensions */ typedef enum { SLICE = 0, ECHO, TIME, PHASE, CHEM_SHIFT, MRI_NDIMS } Mri_Index; /* World dimensions */ typedef enum { XCOORD = 0, YCOORD, ZCOORD, WORLD_NDIMS } World_Index; /* Volume dimensions */ typedef enum { VSLICE = 0, VROW, VCOLUMN, VOL_NDIMS } Volume_Index; /* Orientations */ typedef enum {TRANSVERSE = 0, SAGITTAL, CORONAL, NUM_ORIENTATIONS} Orientation; /* String type */ typedef char Cstring[256]; /* Structure for general info about files */ typedef struct { int initialized; double study_id; int acq_id; /* Time of scan */ int rec_num; int image_type; Cstring image_type_string; int nrows; int ncolumns; int default_index[MRI_NDIMS]; /* Index for dimensions with size == 1 */ int size[MRI_NDIMS]; /* Size of dimension across these files */ int total_size[MRI_NDIMS]; /* Size of dimension across acquisition */ int *indices[MRI_NDIMS]; /* List of indices found for each dimension. Only allocated when size > 1 */ int search_start[MRI_NDIMS]; /* Indices into lists for starting searches */ double *coordinates[MRI_NDIMS]; /* Array indicating coordinate of each index in indices array */ int image_index[MRI_NDIMS]; /* Mapping from MRI dim to output image dim */ World_Index slice_world; World_Index row_world; World_Index column_world; double step[WORLD_NDIMS]; double start[WORLD_NDIMS]; double dircos[WORLD_NDIMS][WORLD_NDIMS]; nc_type datatype; int is_signed; double pixel_min; double pixel_max; Cstring units; double window_min; double window_max; int num_mosaic_rows; int num_mosaic_cols; int num_slices_in_file; int sub_image_rows; int sub_image_columns; struct { Cstring name; Cstring identification; Cstring birth_date; Cstring age; Cstring sex; Cstring reg_date; Cstring reg_time; double weight; } patient; struct { Cstring start_time; Cstring modality; Cstring manufacturer; Cstring model; double field_value; Cstring software_version; Cstring serial_no; Cstring calibration_date; Cstring institution; Cstring station_id; Cstring referring_physician; Cstring performing_physician; Cstring operator; Cstring procedure; Cstring study_id; Cstring acquisition_id; } study; struct { Cstring scan_seq; Cstring seq_owner; Cstring seq_descr; Cstring protocol_name; Cstring receive_coil; Cstring transmit_coil; double rep_time; double slice_thickness; double num_slices; double echo_time; double echo_number; double inv_time; double b_value; double flip_angle; double num_avg; double num_dyn_scans; double scan_dur; double ky_lines; double kymax_ix; double kymin_ix; double kz_lines; double dummy_scans; double imaging_freq; Cstring imaged_nucl; double adc_voltage; double adc_offset; double transmit_ampl; double rec_amp_gain; double rec_preamp_gain; double win_center; double win_width; double gy_ampl; double gx_ampl; double gz_ampl; double num_phase_enc_steps; double percent_sampling; double percent_phase_fov; double pixel_bandwidth; char phase_enc_dir[16]; char mr_acq_type[16]; char image_type[128]; double sar; Cstring comments; char *MrProt; // Siemens Numaris 4 specific } acq; Acr_Group group_list; } General_Info; /* Structure for file-specific info */ typedef struct { int valid; int bits_alloc; int bits_stored; int index[MRI_NDIMS]; double pixel_max; double pixel_min; double slice_max; double slice_min; double window_max; double window_min; double coordinate[MRI_NDIMS]; } File_Info; /* Structure for image data */ typedef struct { int free; int nrows; int ncolumns; unsigned short *data; } Image_Data; /* Structure for sorting dimensions */ typedef struct { int identifier; int original_index; double value; } Sort_Element; // multi-image (mosaic) info typedef struct { int packed; int mosaic_seq; int size[2]; int big[2]; int grid[2]; int pixel_size; Acr_Element big_image; Acr_Element small_image; int sub_images; int first_image; double normal[3]; double step[3]; double position[3]; } Multi_Image; minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/dicom_element_defs.c0000644000175000000620000000201112574624760025641 0ustar stevestaff/* ----------------------------- MNI Header ----------------------------------- @NAME : dicom_element_defs.c @DESCRIPTION: Element definitions for DICOM @METHOD : @GLOBALS : @CALLS : @CREATED : January 28, 1997 (Peter Neelin) @MODIFIED : @COPYRIGHT : Copyright 1997 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. ---------------------------------------------------------------------------- */ #define GLOBAL_ELEMENT_DEFINITION #include minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/progress.c0000644000175000000620000000205312574624760023706 0ustar stevestaff // This function prints a text progress bar within a term window // Input arguments assume a for loop starting at zero: // // for (index = 0; index < end; index++) { ... #include #include int progress(long index, int end, char *message) { int ix; int width = 50; int nchars; if (index == 0) { if (strlen(message) > 20) { // truncate message if too long message[20] = '\0'; } printf("%-20s |<--",message); for (ix = 0; ix < width; ix++) { printf(" "); } printf("|"); for (ix = 0; ix < width+1; ix++) { printf("\b"); } } else if ((index > 0) && (index < end)) { nchars = (((float)index/(float)(end-1)) * width) - floor(((float)(index-1)/(float)(end-1)) * width); for (ix = 0; ix < nchars; ix++) { printf("\b->"); (void) fflush(stdout); } // print terminating newline at end if we're done if (index == end-1) { printf("\n"); } } else { fprintf(stderr,"PROGRESS: bad input indices!\n"); return 0; } } minc-tools-2.3.00+dfsg/conversion/dicomserver_sonata/make_element_table0000755000175000000620000001000212574624760025412 0ustar stevestaff#! /usr/bin/env perl -w # Script to read in the Siemens header files ds_head_acr_groups_types.h and # ds_head_shadow_groups_types.h and create a table of elements to be # created. use strict; # Routine for converting to a hex string sub hexstring { my(@result) = (); my($input); foreach $input (@_) { push(@result, sprintf("0x%04x", hex($input))); } return @result; } # Routine to compare fields by group and element number sub group_element_cmp { my($entry1, $entry2) = @_; my($result) = $entry1->{Group} cmp $entry2->{Group}; if ($result == 0) { $result = $entry1->{Element} cmp $entry2->{Element}; } return $result; } # Routine to read in list of valid ids and return a list sub read_valid_element_ids { my($filename) = @_; my(@ids); open(IDS, $filename) || die "Error opening $filename: $!\n"; while () { if (/^\s*(0x[\da-fA-F]{4})\s+(0x[\da-fA-F]{4})\s/) { push(@ids, $1.$2); } } return @ids; } ############################ MAIN PROGRAM ################################ # Make a hash of valid ids my(%valid_ids, $id); foreach $id (read_valid_element_ids("valid_element_ids")) { $valid_ids{$id} = 1; } # Hash for keeping track of fields my(%fields); # Loop over input lines while (<>) { # Check for structure entry if (/^\s*(\w+)\s+(\w+)(\s*\[([^\]]+)\])?\s*;\s*\/\*\s*\(([\da-fA-F]+)\s*,\s*([\da-fA-F]+)/) { # Save the information about the field - use group and field name # to identify fields to avoid naming problems my($type) = $1; my($length) = (defined($4) ? $4 : 1); my($group) = hexstring($5); my($element) = hexstring($6); my($field) = "$group.$2"; if (defined($fields{$field})) { warn "Field $field already defined\n"; } else { $fields{$field} = {Type => $type, Length => $length, Group => $group, Element => $element}; } } # Look for full variable names elsif (/^\s*"(G(\d+)\.[\w\.]+)"/) { my($variable) = $1; my($group) = hexstring($2); my(@parts) = split(/\./, $variable); if ($#parts < 0) { warn "Weird variable $variable\n".$_; } else { my($field, $part); my($newvar) = ""; foreach $part (@parts) { if (length($newvar) > 0) { $newvar .= "."; } $newvar .= $part; my($tempfield) = "$group.$part"; if (defined($fields{$tempfield})) { $field = $tempfield; last; } } if (defined($field) && defined($fields{$field})) { $fields{$field}->{Variable} = $newvar; } else { warn "Fields for variable $variable not previously defined\n"; } } } } # Warn about fields that don't have a variable defined and get data types my($key); my(%data_types); foreach $key (keys(%fields)) { if (!defined($fields{$key}->{Variable})) { warn "Variable not found for field $key\n"; } else { $data_types{$fields{$key}->{Type}} = 1; } } # Sort the fields my(@keys) = sort({group_element_cmp($fields{$a}, $fields{$b});} keys(%fields)); # Write out the results print "Siemens_header_entry Siemens_header_table[] = {\n"; foreach $key (@keys) { my($entry) = $fields{$key}; # Check for a valid id if (!defined($valid_ids{$entry->{Group}.$entry->{Element}})) { next; } # Check for an undefined variable if (!defined($entry->{Variable})) { next; } # Print out the entry print "{$entry->{Group}, $entry->{Element}, &" . "Siemens_header.$entry->{Variable}, create_$entry->{Type}_element, " . "$entry->{Length}},\n"; } print "{0, 0, NULL, NULL, 0}\n"; print "};\n"; # Write out data types print "\n\n/* Functions needed for this table:\n\n"; my($type); foreach $type (sort(keys(%data_types))) { print " create_" . $type . "_element\n"; } print "\n */\n\n"; minc-tools-2.3.00+dfsg/conversion/nifti1/0002755000175000000620000000000012574624760017203 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/nifti1/README.txt0000644000175000000620000000055612574624760020705 0ustar stevestaff Source Code Files for NIfTI-1 Format ==================================== nifti1.h = C header that defines the NIfTI-1 format nifti1_io.c = sample C functions for NIfTI-1 I/O nifti1_io.h = header for nifti1_io.c nifti1_test.c = test program using nifti1_io.c functions nifti_stats.c = functions to compute with NIfTI-1 statistical codes minc-tools-2.3.00+dfsg/conversion/nifti1/nii2mnc.c0000644000175000000620000005625112574624760020715 0ustar stevestaff#if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #undef X /* These are used in nifti1_io */ #undef Y #undef Z #include #include "nifti1_io.h" #include "analyze75.h" #include "nifti1_local.h" void test_xform(mat44 m, int i, int j, int k) { double x, y, z; x = m.m[DIM_X][DIM_I] * i + m.m[DIM_X][DIM_J] * j + m.m[DIM_X][DIM_K] * k + m.m[DIM_X][3]; y = m.m[DIM_Y][DIM_I] * i + m.m[DIM_Y][DIM_J] * j + m.m[DIM_Y][DIM_K] * k + m.m[DIM_Y][3]; z = m.m[DIM_Z][DIM_I] * i + m.m[DIM_Z][DIM_J] * j + m.m[DIM_Z][DIM_K] * k + m.m[DIM_Z][3]; printf("%d %d %d => ", i, j, k); printf("%f %f %f\n", x, y, z); } static int usage(void) { static const char msg[] = { "nii2mnc: Convert NIfTI-1 files to MINC format\n" "usage: nii2mnc [options] filename.nii [filename.mnc]\n" }; fprintf(stderr, "%s", msg); return (-1); } static void find_data_range(int datatype, unsigned long nvox, void *data, double range[2]) { unsigned long i; range[0] = DBL_MAX; range[1] = -DBL_MAX; for (i = 0; i < nvox; i++) { double tmp; switch (datatype) { case DT_INT8: tmp = (double) ((char *)data)[i]; break; case DT_UINT8: tmp = (double) ((unsigned char *)data)[i]; break; case DT_INT16: tmp = (double) ((short *)data)[i]; break; case DT_UINT16: tmp = (double) ((unsigned short *)data)[i]; break; case DT_INT32: tmp = (double) ((int *)data)[i]; break; case DT_UINT32: tmp = (double) ((unsigned int *)data)[i]; break; case DT_FLOAT32: tmp = (double) ((float *)data)[i]; break; case DT_FLOAT64: tmp = (double) ((double *)data)[i]; break; default: fprintf(stderr, "Data type %d not handled\n", datatype); break; } if (tmp < range[0]) { range[0] = tmp; } if (tmp > range[1]) { range[1] = tmp; } } } int main(int argc, char **argv) { /* NIFTI stuff */ nifti_image *nii_ptr; /* MINC stuff */ int mnc_fd; /* MINC file descriptor */ nc_type mnc_mtype; /* MINC memory data type */ int mnc_msign; /* MINC !0 if signed data */ static nc_type mnc_vtype; /* MINC voxel data type */ static int mnc_vsign; /* MINC !0 if signed data */ int mnc_ndims; /* MINC image dimension count */ int mnc_dimids[MAX_VAR_DIMS]; /* MINC image dimension identifiers */ int mnc_iid; /* MINC Image variable ID */ long mnc_start[MAX_VAR_DIMS]; /* MINC data starts */ long mnc_count[MAX_VAR_DIMS]; /* MINC data counts */ char *mnc_hist; /* MINC history */ double mnc_vrange[2]; /* MINC valid min/max */ double mnc_srange[2]; /* MINC image min/max */ double mnc_time_step; double mnc_time_start; int mnc_spatial_axes[MAX_NII_DIMS]; double mnc_starts[MAX_SPACE_DIMS]; double mnc_steps[MAX_SPACE_DIMS]; double mnc_dircos[MAX_SPACE_DIMS][MAX_SPACE_DIMS]; VIO_Transform mnc_xform; VIO_General_transform mnc_linear_xform; int mnc_vid; /* Dimension variable id */ struct analyze75_hdr ana_hdr; /* Other stuff */ char out_str[1024]; /* Big string for filename */ int i; /* Generic loop counter the first */ int j; /* Generic loop counter the second */ char *str_ptr; /* Generic ASCIZ string pointer */ int r; /* Result code. */ static int qflag = 0; /* Quiet flag (default is non-quiet) */ static int rflag = 1; /* Scan range flag */ short order; static int OrderList[6] = { DIMORDER_ZYX, DIMORDER_ZXY, DIMORDER_XYZ, DIMORDER_XZY, DIMORDER_YZX, DIMORDER_YXZ }; int flip[MAX_SPACE_DIMS] = { 0, 0, 0 }; static char *mnc_ordered_dim_names[MAX_SPACE_DIMS]; static ArgvInfo argTable[] = { {"-byte", ARGV_CONSTANT, (char *) NC_BYTE, (char *)&mnc_vtype, "Write voxel data in 8-bit integer format."}, {"-short", ARGV_CONSTANT, (char *) NC_SHORT, (char *)&mnc_vtype, "Write voxel data in 16-bit integer format."}, {"-int", ARGV_CONSTANT, (char *) NC_INT, (char *)&mnc_vtype, "Write voxel data in 32-bit integer format."}, {"-float", ARGV_CONSTANT, (char *) NC_FLOAT, (char *)&mnc_vtype, "Write voxel data in 32-bit floating point format."}, {"-double", ARGV_CONSTANT, (char *) NC_DOUBLE, (char *)&mnc_vtype, "Write voxel data in 64-bit floating point format."}, {"-signed", ARGV_CONSTANT, (char *) 1, (char *)&mnc_vsign, "Write integer voxel data in signed format."}, {"-unsigned", ARGV_CONSTANT, (char *) 0, (char *)&mnc_vsign, "Write integer voxel data in unsigned format."}, {"-noscanrange", ARGV_CONSTANT, (char *) 0, (char *)&rflag, "Do not scan data range."}, {"-quiet", ARGV_CONSTANT, (char *) 0, (char *)&qflag, "Quiet operation"}, {NULL, ARGV_END, NULL, NULL, NULL} }; ncopts = 0; /* Clear global netCDF error reporting flag */ mnc_hist = time_stamp(argc, argv); mnc_vtype = NC_NAT; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { fprintf(stderr, "Too few arguments\n"); return usage(); } if (argc == 2) { strcpy(out_str, argv[1]); str_ptr = strrchr(out_str, '.'); if (str_ptr != NULL) { if (!strcmp(str_ptr, ".nii") || !strcmp(str_ptr, ".hdr")) { *str_ptr = '\0'; strcat(out_str, ".mnc"); } } } else if (argc == 3) { strcpy(out_str, argv[2]); } else { fprintf(stderr, "Filename argument required\n"); return usage(); } /* Read in the entire NIfTI file. */ nii_ptr = nifti_image_read(argv[1], 1); if (nii_ptr->nifti_type == 0) { /* Analyze file!!! */ FILE *fp; int ss; int must_swap; fp = fopen(argv[1], "rb"); if (fp != NULL) { fread(&ana_hdr, sizeof (ana_hdr), 1, fp); fclose(fp); must_swap = 0; ss = ana_hdr.dime.dim[0]; if (ss != 0) { if (ss < 0 || ss > 7) { nifti_swap_2bytes(1, &(ss)); if (ss < 0 || ss > 7) { /* We should never get here!! */ fprintf(stderr, "Bad dimension count!!\n"); } else { must_swap = 1; } } } else { ss = ana_hdr.hk.sizeof_hdr; if (ss != sizeof(ana_hdr)) { nifti_swap_4bytes(1, &(ss)); if (ss != sizeof(ana_hdr)) { /* We should never get here!! */ fprintf(stderr, "Bad header size!!\n"); } else { must_swap = 1; } } } if (must_swap) { nifti_swap_4bytes(1, &(ana_hdr.hk.sizeof_hdr)); nifti_swap_4bytes(1, &(ana_hdr.hk.extents)); nifti_swap_2bytes(1, &(ana_hdr.hk.session_error)); nifti_swap_4bytes(1, &(ana_hdr.dime.compressed)); nifti_swap_4bytes(1, &(ana_hdr.dime.verified)); nifti_swap_4bytes(1, &(ana_hdr.dime.glmax)); nifti_swap_4bytes(1, &(ana_hdr.dime.glmin)); nifti_swap_2bytes(8, ana_hdr.dime.dim); nifti_swap_4bytes(8, ana_hdr.dime.pixdim); nifti_swap_2bytes(1, &(ana_hdr.dime.datatype)); nifti_swap_2bytes(1, &(ana_hdr.dime.bitpix)); nifti_swap_4bytes(1, &(ana_hdr.dime.vox_offset)); nifti_swap_4bytes(1, &(ana_hdr.dime.cal_max)); nifti_swap_4bytes(1, &(ana_hdr.dime.cal_min)); } if (!qflag) { printf("orient = %d\n", ana_hdr.hist.orient); } } } if (!qflag) { nifti_image_infodump(nii_ptr); } /* Determine the data type for output. */ switch (nii_ptr->datatype) { case DT_INT8: mnc_msign = 1; mnc_mtype = NC_BYTE; mnc_vrange[0] = CHAR_MIN; mnc_vrange[1] = CHAR_MAX; break; case DT_UINT8: mnc_msign = 0; mnc_mtype = NC_BYTE; mnc_vrange[0] = 0; mnc_vrange[1] = UCHAR_MAX; break; case DT_INT16: mnc_msign = 1; mnc_mtype = NC_SHORT; mnc_vrange[0] = SHRT_MIN; mnc_vrange[1] = SHRT_MAX; break; case DT_UINT16: mnc_msign = 0; mnc_mtype = NC_SHORT; mnc_vrange[0] = 0; mnc_vrange[1] = USHRT_MAX; break; case DT_INT32: mnc_msign = 1; mnc_mtype = NC_INT; mnc_vrange[0] = INT_MIN; mnc_vrange[1] = INT_MAX; break; case DT_UINT32: mnc_msign = 0; mnc_mtype = NC_INT; mnc_vrange[0] = 0; mnc_vrange[1] = UINT_MAX; break; case DT_FLOAT32: mnc_msign = 1; mnc_mtype = NC_FLOAT; mnc_vrange[0] = -FLT_MAX; mnc_vrange[1] = FLT_MAX; break; case DT_FLOAT64: mnc_msign = 1; mnc_mtype = NC_DOUBLE; mnc_vrange[0] = -DBL_MAX; mnc_vrange[1] = DBL_MAX; break; default: fprintf(stderr, "Data type %d not handled\n", nii_ptr->datatype); break; } if (mnc_vtype == NC_NAT) { mnc_vsign = mnc_msign; mnc_vtype = mnc_mtype; } /* Calculate the starts, steps, and direction cosines. This only * be done properly if the file is NIfTI-1 file. If it is an Analyze * file we have to resort to other methods... */ if (nii_ptr->nifti_type != 0 && (nii_ptr->sform_code != NIFTI_XFORM_UNKNOWN || nii_ptr->qform_code != NIFTI_XFORM_UNKNOWN)) { make_identity_transform(&mnc_xform); if (nii_ptr->sform_code != NIFTI_XFORM_UNKNOWN) { if (!qflag) { printf("Using s-form transform:\n"); } 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]; if (!qflag) { printf("%8.4f, ", nii_ptr->sto_xyz.m[i][j]); } } if (!qflag) { printf("\n"); } } } else { if (!qflag) { printf("Using q-form transform:\n"); } 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]; if (!qflag) { printf("%8.4f, ", nii_ptr->qto_xyz.m[i][j]); } } if (!qflag) { printf("\n"); } } } create_linear_transform(&mnc_linear_xform, &mnc_xform); /* Set up the spatial axis correspondence for the call to * convert_transform_to_starts_and_steps(). Try all orderings * to see which one gives cosines=identity. */ for( order = 0; order < 6; order++ ) { switch (OrderList[order]) { case DIMORDER_ZYX: mnc_ordered_dim_names[DIM_X] = MIxspace; mnc_ordered_dim_names[DIM_Y] = MIyspace; mnc_ordered_dim_names[DIM_Z] = MIzspace; mnc_spatial_axes[DIM_X] = DIM_X; mnc_spatial_axes[DIM_Y] = DIM_Y; mnc_spatial_axes[DIM_Z] = DIM_Z; break; case DIMORDER_ZXY: mnc_ordered_dim_names[DIM_X] = MIyspace; mnc_ordered_dim_names[DIM_Y] = MIxspace; mnc_ordered_dim_names[DIM_Z] = MIzspace; mnc_spatial_axes[DIM_X] = DIM_Y; mnc_spatial_axes[DIM_Y] = DIM_X; mnc_spatial_axes[DIM_Z] = DIM_Z; break; case DIMORDER_XYZ: mnc_ordered_dim_names[DIM_X] = MIzspace; mnc_ordered_dim_names[DIM_Y] = MIyspace; mnc_ordered_dim_names[DIM_Z] = MIxspace; mnc_spatial_axes[DIM_X] = DIM_Z; mnc_spatial_axes[DIM_Y] = DIM_Y; mnc_spatial_axes[DIM_Z] = DIM_X; break; case DIMORDER_XZY: mnc_ordered_dim_names[DIM_X] = MIyspace; mnc_ordered_dim_names[DIM_Y] = MIzspace; mnc_ordered_dim_names[DIM_Z] = MIxspace; mnc_spatial_axes[DIM_X] = DIM_Y; mnc_spatial_axes[DIM_Y] = DIM_Z; mnc_spatial_axes[DIM_Z] = DIM_X; break; case DIMORDER_YZX: mnc_ordered_dim_names[DIM_X] = MIxspace; mnc_ordered_dim_names[DIM_Y] = MIzspace; mnc_ordered_dim_names[DIM_Z] = MIyspace; mnc_spatial_axes[DIM_X] = DIM_X; mnc_spatial_axes[DIM_Y] = DIM_Z; mnc_spatial_axes[DIM_Z] = DIM_Y; break; case DIMORDER_YXZ: mnc_ordered_dim_names[DIM_X] = MIzspace; mnc_ordered_dim_names[DIM_Y] = MIxspace; mnc_ordered_dim_names[DIM_Z] = MIyspace; mnc_spatial_axes[DIM_X] = DIM_Z; mnc_spatial_axes[DIM_Y] = DIM_X; mnc_spatial_axes[DIM_Z] = DIM_Y; break; } convert_transform_to_starts_and_steps(&mnc_linear_xform, MAX_SPACE_DIMS, NULL, mnc_spatial_axes, mnc_starts, mnc_steps, mnc_dircos); /* Do we have a matrix with diagonal dominance? */ if( fabs( mnc_dircos[0][DIM_X] ) > fabs( mnc_dircos[0][DIM_Y] ) && fabs( mnc_dircos[0][DIM_X] ) > fabs( mnc_dircos[0][DIM_Z] ) && fabs( mnc_dircos[1][DIM_Y] ) > fabs( mnc_dircos[1][DIM_X] ) && fabs( mnc_dircos[1][DIM_Y] ) > fabs( mnc_dircos[1][DIM_Z] ) && fabs( mnc_dircos[2][DIM_Z] ) > fabs( mnc_dircos[2][DIM_X] ) && fabs( mnc_dircos[2][DIM_Z] ) > fabs( mnc_dircos[2][DIM_Y] ) ) { if( mnc_dircos[0][DIM_X] < 0.0 ) flip[0] = 1; if( mnc_dircos[1][DIM_Y] < 0.0 ) flip[1] = 1; if( mnc_dircos[2][DIM_Z] < 0.0 ) flip[2] = 1; break; } } } else { /* No official transform was found (possibly this is an Analyze * file). Just use some reasonable defaults. */ mnc_steps[mnc_spatial_axes[DIM_X]] = nii_ptr->dx; mnc_steps[mnc_spatial_axes[DIM_Y]] = nii_ptr->dy; mnc_steps[mnc_spatial_axes[DIM_Z]] = nii_ptr->dz; mnc_starts[mnc_spatial_axes[DIM_X]] = -(nii_ptr->dx * nii_ptr->nx) / 2; mnc_starts[mnc_spatial_axes[DIM_Y]] = -(nii_ptr->dy * nii_ptr->ny) / 2; mnc_starts[mnc_spatial_axes[DIM_Z]] = -(nii_ptr->dz * nii_ptr->nz) / 2; /* Unlike the starts and steps, the direction cosines do NOT change * based upon the data orientation. */ for (i = 0; i < MAX_SPACE_DIMS; i++) { for (j = 0; j < MAX_SPACE_DIMS; j++) { mnc_dircos[i][j] = (i == j) ? 1.0 : 0.0; } } } /* Open the MINC file. It should not already exist. */ mnc_fd = micreate(out_str, NC_NOCLOBBER); if (mnc_fd < 0) { fprintf(stderr, "Can't create output file '%s'\n", out_str); return (-1); } /* Create the necessary dimensions in the minc file */ mnc_ndims = 0; if (nii_ptr->nt > 1) { mnc_dimids[mnc_ndims] = ncdimdef(mnc_fd, MItime, nii_ptr->nt); mnc_start[mnc_ndims] = 0; mnc_count[mnc_ndims] = nii_ptr->nt; mnc_ndims++; r = micreate_std_variable(mnc_fd, MItime, NC_INT, 0, NULL); switch (nii_ptr->time_units) { case NIFTI_UNITS_UNKNOWN: case NIFTI_UNITS_SEC: mnc_time_step = nii_ptr->dt; mnc_time_start = nii_ptr->toffset; break; case NIFTI_UNITS_MSEC: mnc_time_step = nii_ptr->dt / 1000; mnc_time_start = nii_ptr->toffset / 1000; break; case NIFTI_UNITS_USEC: mnc_time_step = nii_ptr->dt / 1000000; mnc_time_start = nii_ptr->toffset / 1000000; break; default: fprintf(stderr, "Unknown time units value %d\n", nii_ptr->time_units); break; } miattputdbl(mnc_fd, r, MIstart, mnc_time_start); miattputdbl(mnc_fd, r, MIstep, mnc_time_step); miattputstr(mnc_fd, r, MIunits, "s"); } if (nii_ptr->nz > 1) { mnc_dimids[mnc_ndims] = ncdimdef(mnc_fd, mnc_ordered_dim_names[DIM_Z], nii_ptr->nz); mnc_start[mnc_ndims] = 0; mnc_count[mnc_ndims] = nii_ptr->nz; mnc_ndims++; r = micreate_std_variable(mnc_fd, mnc_ordered_dim_names[DIM_Z], NC_INT, 0, NULL); miattputdbl(mnc_fd, r, MIstep, nii_ptr->dz); miattputstr(mnc_fd, r, MIunits, "mm"); } if (nii_ptr->ny > 1) { mnc_dimids[mnc_ndims] = ncdimdef(mnc_fd, mnc_ordered_dim_names[DIM_Y], nii_ptr->ny); mnc_start[mnc_ndims] = 0; mnc_count[mnc_ndims] = nii_ptr->ny; mnc_ndims++; r = micreate_std_variable(mnc_fd, mnc_ordered_dim_names[DIM_Y], NC_INT, 0, NULL); miattputdbl(mnc_fd, r, MIstep, nii_ptr->dy); miattputstr(mnc_fd, r, MIunits, "mm"); } if (nii_ptr->nx > 1) { mnc_dimids[mnc_ndims] = ncdimdef(mnc_fd, mnc_ordered_dim_names[DIM_X], nii_ptr->nx); mnc_start[mnc_ndims] = 0; mnc_count[mnc_ndims] = nii_ptr->nx; mnc_ndims++; r = micreate_std_variable(mnc_fd, mnc_ordered_dim_names[DIM_X], NC_INT, 0, NULL); miattputdbl(mnc_fd, r, MIstep, nii_ptr->dx); miattputstr(mnc_fd, r, MIunits, "mm"); } if (nii_ptr->nu > 1) { mnc_dimids[mnc_ndims] = ncdimdef(mnc_fd, MIvector_dimension, nii_ptr->nu); mnc_start[mnc_ndims] = 0; mnc_count[mnc_ndims] = nii_ptr->nu; mnc_ndims++; } /* Create scalar image-min and image-max variables. */ micreate_std_variable(mnc_fd, MIimagemax, NC_DOUBLE, 0, NULL); micreate_std_variable(mnc_fd, MIimagemin, NC_DOUBLE, 0, NULL); /* Create the group variables. */ micreate_std_variable(mnc_fd, MIstudy, NC_INT, 0, NULL); if (strlen(nii_ptr->descrip) > 0 && strlen(nii_ptr->descrip) < 79 ) { int varid = micreate_std_variable(mnc_fd, MIpatient, NC_INT, 0, NULL); (void) miattputstr(mnc_fd, varid, MIfull_name, nii_ptr->descrip); } else { micreate_std_variable(mnc_fd, MIpatient, NC_INT, 0, NULL); } micreate_std_variable(mnc_fd, MIacquisition, NC_INT, 0, NULL); /* Create the MINC image variable. If we can't, there is no * further processing possible... */ mnc_iid = micreate_std_variable(mnc_fd, MIimage, mnc_vtype, mnc_ndims, mnc_dimids); if (mnc_iid < 0) { fprintf(stderr, "Can't create the image variable\n"); return (-1); } miattputstr(mnc_fd, mnc_iid, MIsigntype, (mnc_vsign) ? MI_SIGNED : MI_UNSIGNED); switch (nii_ptr->xyz_units) { case NIFTI_UNITS_METER: for (i = 0; i < MAX_SPACE_DIMS; i++) { mnc_starts[i] *= 1000; mnc_steps[i] *= 1000; } break; case NIFTI_UNITS_MM: break; case NIFTI_UNITS_MICRON: for (i = 0; i < MAX_SPACE_DIMS; i++) { mnc_starts[i] /= 1000; mnc_steps[i] /= 1000; } break; default: fprintf(stderr, "Unknown XYZ units %d\n", nii_ptr->xyz_units); break; } /* Now we write the spatial axis information to the file. The starts, * steps, and cosines have to be associated with the correct spatial * axes. Also, we perform any orientation flipping that was requested. */ for (i = 0; i < MAX_SPACE_DIMS; i++) { if (!qflag) { printf("%s start: %8.4f step: %8.4f cosines: %8.4f %8.4f %8.4f flip: %d\n", mnc_spatial_names[i], mnc_starts[i], mnc_steps[i], mnc_dircos[i][DIM_X], mnc_dircos[i][DIM_Y], mnc_dircos[i][DIM_Z], flip[i]); } mnc_vid = ncvarid(mnc_fd, mnc_spatial_names[i]); /* If we selected "flipping" of the appropriate axis, do it here */ if (flip[i]) { miattputdbl(mnc_fd, mnc_vid, MIstart, mnc_starts[i]+((mnc_count[i]-1)*mnc_steps[i])); miattputdbl(mnc_fd, mnc_vid, MIstep, -mnc_steps[i]); mnc_dircos[i][DIM_X] = -mnc_dircos[i][DIM_X]; mnc_dircos[i][DIM_Y] = -mnc_dircos[i][DIM_Y]; mnc_dircos[i][DIM_Z] = -mnc_dircos[i][DIM_Z]; } else { miattputdbl(mnc_fd, mnc_vid, MIstart, mnc_starts[i]); miattputdbl(mnc_fd, mnc_vid, MIstep, mnc_steps[i]); } ncattput(mnc_fd, mnc_vid, MIdirection_cosines, NC_DOUBLE, MAX_SPACE_DIMS, mnc_dircos[i]); } /* Find the valid minimum and maximum of the data, in order to set the * global image minimum and image maximum properly. */ if (rflag) { find_data_range(nii_ptr->datatype, nii_ptr->nvox, nii_ptr->data, mnc_vrange); } if (nii_ptr->scl_slope != 0.0) { /* Convert slope/offset to min/max */ mnc_srange[0] = (mnc_vrange[0] * nii_ptr->scl_slope) + nii_ptr->scl_inter; mnc_srange[1] = (mnc_vrange[1] * nii_ptr->scl_slope) + nii_ptr->scl_inter; } else { mnc_srange[0] = mnc_vrange[0]; mnc_srange[1] = mnc_vrange[1]; } ncattput(mnc_fd, mnc_iid, MIvalid_range, NC_DOUBLE, 2, mnc_vrange); miattputstr(mnc_fd, NC_GLOBAL, MIhistory, mnc_hist); /* Switch out of definition mode. */ ncendef(mnc_fd); /* Finally, write the values of the image-min, image-max, and image * variables. */ mivarput1(mnc_fd, ncvarid(mnc_fd, MIimagemin), mnc_start, NC_DOUBLE, MI_SIGNED, &mnc_srange[0]); mivarput1(mnc_fd, ncvarid(mnc_fd, MIimagemax), mnc_start, NC_DOUBLE, MI_SIGNED, &mnc_srange[1]); mivarput(mnc_fd, mnc_iid, mnc_start, mnc_count, mnc_mtype, (mnc_msign) ? MI_SIGNED : MI_UNSIGNED, nii_ptr->data); miclose(mnc_fd); return (0); } minc-tools-2.3.00+dfsg/conversion/nifti1/analyze75.h0000644000175000000620000002121012574624760021165 0ustar stevestaff/* dbh.h - Analyze 7.5 header file */ /* */ /* Compiled by Andrew Janke (a.janke@gmail.com) */ /* from http://www.mayo.edu/bir/analyze/AnalyzeFileInfo.html */ /* http://homepage2.nifty.com/peco/gpetview/gpetview.html */ /* Chris Rorden - chris.rorden@nottingham.ac.uk */ /* Matthew Brett - matthew.brett@mrc-cbu.cam.ac.uk */ #define DT_NONE 0 /* No data type */ #define DT_UNKNOWN 0 /* Unknown data type */ #define DT_BINARY 1 /* Binary ( 1 bit per voxel) */ #define DT_UNSIGNED_CHAR 2 /* Unsigned character ( 8 bits per voxel) */ #define DT_SIGNED_SHORT 4 /* Signed short (16 bits per voxel) */ #define DT_SIGNED_INT 8 /* Signed integer (32 bits per voxel) */ #define DT_FLOAT 16 /* Floating point (32 bits per voxel) */ #define DT_COMPLEX 32 /* Complex (64 bits per voxel; 2 floating points) */ #define DT_DOUBLE 64 /* Double precision (64 bits per voxel) */ #define DT_RGB 128 /* Uchar x 3 (24 bits per voxel) */ #define DT_ALL 255 /* */ struct header_key{ /* off + size */ int sizeof_hdr; /* 0 + 4 - the byte size of the header file */ char data_type[10]; /* 4 + 10 - the data type of the file */ char db_name[18]; /* 14 + 18 - */ int extents; /* 32 + 4 - should be 16384 */ short int session_error; /* 36 + 2 - */ char regular; /* 38 + 1 - 'r' indicating all images/volumes are the same size */ char hkey_un0; /* 39 + 1 - */ }; /* total=40 bytes */ struct image_dimension{ /* off + size */ short int dim[8]; /* 0 + 16 - array of the image dimensions */ /* dim[0] # of dimensions in database; usually 4 */ /* dim[1] X dim - pixels in an image row */ /* dim[2] Y dim - pixel rows in slice */ /* dim[3] Z dim - slices in a volume */ /* dim[4] Time dim - volumes in database */ char vox_units[4]; /* 16 + 4 - specifies the spatial units of measure for a voxel */ char cal_units[8]; /* 20 + 8 - specifies the name of the calibration unit */ short int unused1; /* 28 + 2 */ short int datatype; /* 30 + 2 - datatype for this image set */ short int bitpix; /* 32 + 2 - # of bits per pixel 1, 8, 16, 32, or 64. */ short int dim_un0; /* 34 + 2 - */ float pixdim[8]; /* 36 + 32 - pixdim[] specifies the voxel dimensions: */ /* pixdim[1] - voxel width */ /* pixdim[2] - voxel height */ /* pixdim[3] - interslice distance */ /* ..etc */ float vox_offset; /* 68 + 4 - byte offset in the .img file at which voxels start. */ /* This value can be negative to specify that the */ /* absolute value is applied for every image */ float scale_factor; /* 72 + 4 = funused1; scale factor used by SPM; non standard */ float funused1; /* 76 + 4 */ float funused2; /* 80 + 4 */ float cal_max, cal_min; /* 84 + 8 - calibrated max and min: */ /* www.mailbase.ac.uk/lists/spm/2000-09/0099.html */ float compressed; /* 92 + 4 */ float verified; /* 96 + 4 */ int glmax, glmin; /* 100 + 8 - global max and min pixel values (entire database) */ }; /* total=108 bytes */ struct data_history{ /* off + size */ char descrip[80]; /* 0 + 80 */ char aux_file[24]; /* 80 + 24 */ char orient; /* 104 + 1 - slice orientation for this database | */ /* 0 transverse unflipped | */ /* 1 coronal unflipped | disregarded */ /* 2 sagittal unflipped | by SPM */ /* 3 transverse flipped | */ /* 4 coronal flipped | */ /* 5 sagittal flipped | */ short int originator[5]; /* 105 + 10 - origin | */ /* originator[0] x-origin | non standard */ /* originator[1] y-origin | SPM use only */ /* originator[2] z-origin | */ 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 */ }; /* total=200 bytes */ struct analyze75_hdr { struct header_key hk; /* 0 + 40 */ struct image_dimension dime; /* 40 + 108 */ struct data_history hist; /* 148 + 200 */ }; /* total= 348 bytes */ typedef struct{ float real; float imag; } COMPLEX; minc-tools-2.3.00+dfsg/conversion/nifti1/mnc2nii.c0000644000175000000620000006005212574624760020707 0ustar stevestaff#if HAVE_CONFIG_H #include "config.h" #endif #include #include #include "restructure.h" #include "nifti1_io.h" #include "nifti1_local.h" /* Our local definitions */ /* This list is in the order in which dimension lengths and sample * widths are stored in the NIfTI-1 structure. */ static const char *dimnames[MAX_NII_DIMS] = { MIvector_dimension, MItime, MIzspace, MIyspace, MIxspace, NULL, NULL, NULL }; void test_xform(mat44 m, int i, int j, int k) { double x, y, z; x = m.m[DIM_X][DIM_I] * i + m.m[DIM_X][DIM_J] * j + m.m[DIM_X][DIM_K] * k + m.m[DIM_X][3]; y = m.m[DIM_Y][DIM_I] * i + m.m[DIM_Y][DIM_J] * j + m.m[DIM_Y][DIM_K] * k + m.m[DIM_Y][3]; z = m.m[DIM_Z][DIM_I] * i + m.m[DIM_Z][DIM_J] * j + m.m[DIM_Z][DIM_K] * k + m.m[DIM_Z][3]; printf("%d %d %d => ", i, j, k); printf("%f %f %f\n", x, y, z); } static int usage(void) { static const char msg[] = { "mnc2nii: Convert MINC files to NIfTI-1 format\n" "usage: mnc2nii [-q] [filetype] [datatype] filename.mnc [filename.nii]\n" }; fprintf(stderr, "%s", msg); return (-1); } /* Explicitly set all of the fields of the NIfTI I/O header structure to * something reasonable. Right now this is overkill since a simple memset() * would do the same job, but I want this function to help me keep track * of all of the header fields and to allow me to easily override a default * if it becomes useful. */ void init_nifti_header(nifti_image *nii_ptr) { int i, j; nii_ptr->ndim = 0; nii_ptr->nx = nii_ptr->ny = nii_ptr->nz = nii_ptr->nt = nii_ptr->nu = nii_ptr->nv = nii_ptr->nw = 0; for (i = 0; i < MAX_NII_DIMS; i++) { /* Fix suggested by Hyun-Pil Kim (hpkim@ihanyang.ac.kr): Use 1 as the default, not zero */ nii_ptr->dim[i] = 1; } nii_ptr->nvox = 0; nii_ptr->nbyper = 0; nii_ptr->datatype = DT_UNKNOWN; nii_ptr->dx = nii_ptr->dy = nii_ptr->dz = nii_ptr->dt = nii_ptr->du = nii_ptr->dv = nii_ptr->dw = 0.0; for (i = 0; i < MAX_NII_DIMS; i++) { nii_ptr->pixdim[i] = 0.0; } nii_ptr->scl_slope = 0.0; nii_ptr->scl_inter = 0.0; nii_ptr->cal_min = 0.0; nii_ptr->cal_max = 0.0; nii_ptr->qform_code = NIFTI_XFORM_UNKNOWN; nii_ptr->sform_code = NIFTI_XFORM_UNKNOWN; nii_ptr->freq_dim = 0; nii_ptr->phase_dim = 0; nii_ptr->slice_dim = 0; nii_ptr->slice_code = 0; nii_ptr->slice_start = 0; nii_ptr->slice_end = 0; nii_ptr->slice_duration = 0.0; nii_ptr->quatern_b = 0.0; nii_ptr->quatern_c = 0.0; nii_ptr->quatern_d = 0.0; nii_ptr->qoffset_x = 0.0; nii_ptr->qoffset_y = 0.0; nii_ptr->qoffset_z = 0.0; nii_ptr->qfac = 0.0; nii_ptr->toffset = 0.0; nii_ptr->xyz_units = NIFTI_UNITS_MM; /* Default spatial units */ nii_ptr->time_units = NIFTI_UNITS_SEC; /* Default time units */ nii_ptr->nifti_type = FT_ANALYZE; nii_ptr->intent_code = 0; nii_ptr->intent_p1 = 0.0; nii_ptr->intent_p2 = 0.0; nii_ptr->intent_p3 = 0.0; memset(nii_ptr->intent_name, 0, sizeof (nii_ptr->intent_name)); memset(nii_ptr->descrip, 0, sizeof (nii_ptr->descrip)); memset(nii_ptr->aux_file, 0, sizeof (nii_ptr->aux_file)); nii_ptr->fname = NULL; nii_ptr->iname = NULL; nii_ptr->iname_offset = 0; nii_ptr->swapsize = 0; nii_ptr->byteorder = 1; /* default order (LSB) */ nii_ptr->data = NULL; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { nii_ptr->qto_xyz.m[i][j] = 0.0; nii_ptr->qto_ijk.m[i][j] = 0.0; nii_ptr->sto_xyz.m[i][j] = 0.0; nii_ptr->sto_ijk.m[i][j] = 0.0; } } nii_ptr->num_ext = 0; } /* Calculate the first power of two greater than or equal to "value". */ double nearest_power_of_two(double value) { double r = 1.0; while (r < value) { r *= 2.0; } return r; } int main(int argc, char **argv) { /* NIFTI stuff */ nifti_image *nii_ptr; nifti_image nii_rec; int nii_dimids[MAX_NII_DIMS]; int nii_dir[MAX_NII_DIMS]; int nii_map[MAX_NII_DIMS]; unsigned long nii_lens[MAX_NII_DIMS]; int nii_ndims; static int nifti_filetype; static int nifti_datatype; static int nifti_signed = 1; /* MINC stuff */ int mnc_fd; /* MINC file descriptor */ nc_type mnc_type; /* MINC data type as read */ int mnc_ndims; /* MINC image dimension count */ int mnc_dimids[MAX_VAR_DIMS]; /* MINC image dimension identifiers */ long mnc_dlen; /* MINC dimension length value */ double mnc_dstep; /* MINC dimension step value */ int mnc_icv; /* MINC image conversion variable */ int mnc_vid; /* MINC Image variable ID */ long mnc_start[MAX_VAR_DIMS]; /* MINC data starts */ long mnc_count[MAX_VAR_DIMS]; /* MINC data counts */ int mnc_signed; /* MINC if output voxels are signed */ double real_range[2]; /* MINC real range (min, max) */ double input_valid_range[2]; /* MINC valid range (min, max) */ double output_valid_range[2]; /* Valid range of output data. */ double nifti_slope; /* Slope to be applied to output voxels. */ double nifti_inter; /* Intercept to be applied to output voxels. */ double total_valid_range; /* Overall valid range (max - min). */ double total_real_range; /* Overall real range (max - min). */ /* Other stuff */ char out_str[1024]; /* Big string for filename */ char att_str[1024]; /* Big string for attribute values */ int i; /* Generic loop counter the first */ int j; /* Generic loop counter the second */ char *str_ptr; /* Generic ASCIZ string pointer */ int r; /* Result code. */ static int vflag = 0; /* Verbose flag (default is quiet) */ static ArgvInfo argTable[] = { {NULL, ARGV_HELP, NULL, NULL, "Output voxel data type specification"}, {"-byte", ARGV_CONSTANT, (char *)DT_INT8, (char *)&nifti_datatype, "Write voxel data in 8-bit signed integer format."}, {"-short", ARGV_CONSTANT, (char *)DT_INT16, (char *)&nifti_datatype, "Write voxel data in 16-bit signed integer format."}, {"-int", ARGV_CONSTANT, (char *)DT_INT32, (char *)&nifti_datatype, "Write voxel data in 32-bit signed integer format."}, {"-float", ARGV_CONSTANT, (char *)DT_FLOAT32, (char *)&nifti_datatype, "Write voxel data in 32-bit floating point format."}, {"-double", ARGV_CONSTANT, (char *)DT_FLOAT64, (char *)&nifti_datatype, "Write voxel data in 64-bit floating point format."}, {"-signed", ARGV_CONSTANT, (char *)1, (char *)&nifti_signed, "Write integer voxel data in signed format."}, {"-unsigned", ARGV_CONSTANT, (char *)0, (char *)&nifti_signed, "Write integer voxel data in unsigned format."}, {NULL, ARGV_HELP, NULL, NULL, "Output file format specification"}, {"-dual", ARGV_CONSTANT, (char *)FT_NIFTI_DUAL, (char *)&nifti_filetype, "Write NIfTI-1 two-file format (.img and .hdr)"}, {"-ASCII", ARGV_CONSTANT, (char *)FT_NIFTI_ASCII, (char *)&nifti_filetype, "Write NIfTI-1 ASCII header format (.nia)"}, {"-nii", ARGV_CONSTANT, (char *)FT_NIFTI_SINGLE, (char *)&nifti_filetype, "Write NIfTI-1 one-file format (.nii)"}, {"-analyze", ARGV_CONSTANT, (char *)FT_ANALYZE, (char *)&nifti_filetype, "Write an Analyze two-file format file (.img and .hdr)"}, {NULL, ARGV_HELP, NULL, NULL, "Other options"}, {"-quiet", ARGV_CONSTANT, (char *)0, (char *)&vflag, "Quiet operation"}, {"-verbose", ARGV_CONSTANT, (char *)1, (char *)&vflag, "Quiet operation"}, {NULL, ARGV_END, NULL, NULL, NULL} }; ncopts = 0; /* Clear global netCDF error reporting flag */ /* Default NIfTI file type is "NII", single binary file */ nifti_filetype = FT_UNSPECIFIED; nifti_datatype = DT_UNKNOWN; if (ParseArgv(&argc, argv, argTable, 0) || (argc < 2)) { fprintf(stderr, "Too few arguments\n"); return usage(); } if (!nifti_signed) { switch (nifti_datatype) { case DT_INT8: nifti_datatype = DT_UINT8; break; case DT_INT16: nifti_datatype = DT_UINT16; break; case DT_INT32: nifti_datatype = DT_UINT32; break; } } switch (nifti_datatype){ case DT_INT8: case DT_UINT8: mnc_type = NC_BYTE; break; case DT_INT16: case DT_UINT16: mnc_type = NC_SHORT; break; case DT_INT32: case DT_UINT32: mnc_type = NC_INT; break; case DT_FLOAT32: mnc_type = NC_FLOAT; break; case DT_FLOAT64: mnc_type = NC_DOUBLE; break; } if (argc == 2) { strcpy(out_str, argv[1]); str_ptr = strrchr(out_str, '.'); if (str_ptr != NULL && !strcmp(str_ptr, ".mnc")) { *str_ptr = '\0'; } } else if (argc == 3) { strcpy(out_str, argv[2]); str_ptr = strrchr(out_str, '.'); if (str_ptr != NULL) { /* See if a recognized file extension was specified. If so, * we trim it off and set the output file type if none was * specified. If the extension is not recognized, assume * that we will form the filename by just adding the right * extension for the selected output format. */ if (!strcmp(str_ptr, ".nii")) { if (nifti_filetype == FT_UNSPECIFIED) { nifti_filetype = FT_NIFTI_SINGLE; } *str_ptr = '\0'; } else if (!strcmp(str_ptr, ".img") || !strcmp(str_ptr, ".hdr")) { if (nifti_filetype == FT_UNSPECIFIED) { nifti_filetype = FT_NIFTI_DUAL; } *str_ptr = '\0'; } else if (!strcmp(str_ptr, ".nia")) { if (nifti_filetype == FT_UNSPECIFIED) { nifti_filetype = FT_NIFTI_ASCII; } *str_ptr = '\0'; } } } else { fprintf(stderr, "Filename argument required\n"); return usage(); } /* Open the MINC file. It needs to exist. */ mnc_fd = miopen(argv[1], NC_NOWRITE); if (mnc_fd < 0) { fprintf(stderr, "Can't find input file '%s'\n", argv[1]); return (-1); } /* Find the MINC image variable. If we can't find it, there is no * further processing possible... */ mnc_vid = ncvarid(mnc_fd, MIimage); if (mnc_vid < 0) { fprintf(stderr, "Can't locate the image variable (mnc_vid=%d)\n", mnc_vid); return (-1); } /* Find out about the MINC image variable - specifically, how many * dimensions, and which dimensions. */ r = ncvarinq(mnc_fd, mnc_vid, NULL, NULL, &mnc_ndims, mnc_dimids, NULL); if (r < 0) { fprintf(stderr, "Can't read information from image variable\n"); return (-1); } if (mnc_ndims > MAX_NII_DIMS) { fprintf(stderr, "NIfTI-1 files may contain at most %d dimensions\n", MAX_NII_DIMS); return (-1); } /* Initialize the NIfTI structure */ nii_ptr = &nii_rec; init_nifti_header(nii_ptr); /* For now we just use the mnc2nii command line as the description * field. Probably we should use something better, perhaps a * combination of some other standard MINC fields that might * provide more information. */ str_ptr = nii_ptr->descrip; for (i = 0; i < argc; i++) { char *arg_ptr = argv[i]; if ((str_ptr - nii_ptr->descrip) >= MAX_NII_DESCRIP) { break; } if (i != 0) { *str_ptr++ = ' '; } while (*arg_ptr != '\0' && (str_ptr - nii_ptr->descrip) < MAX_NII_DESCRIP) { *str_ptr++ = *arg_ptr++; } *str_ptr = '\0'; } nii_ptr->fname = malloc(strlen(out_str) + 4 + 1); nii_ptr->iname = malloc(strlen(out_str) + 4 + 1); strcpy(nii_ptr->fname, out_str); strcpy(nii_ptr->iname, out_str); switch (nifti_filetype) { case FT_ANALYZE: strcat(nii_ptr->fname, ".hdr"); strcat(nii_ptr->iname, ".img"); break; case FT_NIFTI_SINGLE: strcat(nii_ptr->fname, ".nii"); strcat(nii_ptr->iname, ".nii"); break; case FT_NIFTI_DUAL: strcat(nii_ptr->fname, ".hdr"); strcat(nii_ptr->iname, ".img"); break; case FT_NIFTI_ASCII: strcat(nii_ptr->fname, ".nia"); strcat(nii_ptr->iname, ".nia"); break; default: fprintf(stderr, "Unknown output file type %d\n", nifti_filetype); return (-1); } /* Get real voxel range for the input file. */ miget_image_range(mnc_fd, real_range); /* Get the actual valid voxel value range. */ miget_valid_range(mnc_fd, mnc_vid, input_valid_range); /* Find the default range for the output type. Our output file * will use the full legal range of the output type if it is * an integer. */ if (nifti_datatype == DT_UNKNOWN) { nii_ptr->datatype = DT_FLOAT32; /* Default */ mnc_type = NC_FLOAT; mnc_signed = 1; } else { nii_ptr->datatype = nifti_datatype; mnc_signed = nifti_signed; } if (vflag) { fprintf(stderr, "MINC type %d signed %d\n", mnc_type, mnc_signed); } miget_default_range(mnc_type, mnc_signed, output_valid_range); total_valid_range = input_valid_range[1] - input_valid_range[0]; total_real_range = real_range[1] - real_range[0]; if ((output_valid_range[1] - output_valid_range[0]) > total_valid_range) { /* Empirically, forcing the valid range to be the nearest power * of two greater than the existing valid range seems to improve * the behavior of the conversion. This is at least in part because * of the limited precision of the NIfTI-1 voxel scaling fields. */ double new_range = nearest_power_of_two(total_valid_range); if (new_range - 1.0 >= total_valid_range) { new_range -= 1.0; } if (output_valid_range[1] > total_valid_range) { output_valid_range[0] = 0; output_valid_range[1] = new_range; } else { output_valid_range[1] = output_valid_range[0] + new_range; } } else { /* The new range can't fully represent the input range. Use the * full available range, and warn the user that they may have a * problem. */ printf("WARNING: Range of input exceeds range of output format.\n"); } if (vflag) { printf("Real range: %f %f Input valid range: %f %f Output valid range: %f %f\n", real_range[0], real_range[1], input_valid_range[0], input_valid_range[1], output_valid_range[0], output_valid_range[1]); } /* If the output type is not floating point, we may need to scale the * voxel values. */ if (mnc_type != NC_FLOAT && mnc_type != NC_DOUBLE) { /* Figure out how to map pixel values into the range of the * output datatype. */ nifti_slope = ((real_range[1] - real_range[0]) / (output_valid_range[1] - output_valid_range[0])); if (nifti_slope == 0.0) { nifti_slope = 1.0; } nifti_inter = real_range[0] - (output_valid_range[0] * nifti_slope); /* One problem with NIfTI-1 is the limited precision of the * scl_slope and scl_inter fields (they are just 32-bits). So * we look for possible issues and warn about that here. */ if (nifti_inter != (float) nifti_inter || nifti_slope != (float) nifti_slope) { double epsilon_i = nifti_inter - (float) nifti_inter; double epsilon_s = nifti_slope - (float) nifti_slope; /* If the loss in precision is more than one part per thousand * of the real range, flag this as a problem! */ if ((epsilon_i > total_real_range / 1.0e3) || (epsilon_s > total_real_range / 1.0e3)) { fprintf(stderr, "ERROR: Slope and intercept cannot be represented in the NIfTI-1 header.\n"); fprintf(stderr, " slope %f (%f), intercept %f (%f)\n", nifti_slope, (float) nifti_slope, nifti_inter, (float) nifti_inter); return (-1); } } } else { nifti_slope = 0.0; } nii_ptr->scl_slope = nifti_slope; nii_ptr->scl_inter = nifti_inter; nii_ptr->nvox = 1; /* Initial value for voxel count */ /* Find all of the dimensions of the MINC file, in the order they * will be listed in the NIfTI-1/Analyze file. We use this to build * a map for restructuring the data according to the normal rules * of NIfTI-1. */ nii_ndims = 0; for (i = 0; i < MAX_NII_DIMS; i++) { if (dimnames[i] == NULL) { nii_dimids[nii_ndims] = -1; continue; } nii_dimids[nii_ndims] = ncdimid(mnc_fd, dimnames[i]); if (nii_dimids[nii_ndims] == -1) { continue; } /* Make sure the dimension is actually used to define the image. */ for (j = 0; j < mnc_ndims; j++) { if (nii_dimids[nii_ndims] == mnc_dimids[j]) { nii_map[nii_ndims] = j; break; } } if (j < mnc_ndims) { mnc_dlen = 1; mnc_dstep = 0; ncdiminq(mnc_fd, nii_dimids[nii_ndims], NULL, &mnc_dlen); ncattget(mnc_fd, ncvarid(mnc_fd, dimnames[i]), MIstep, &mnc_dstep); if (mnc_dstep < 0) { nii_dir[nii_ndims] = -1; mnc_dstep = -mnc_dstep; } else { nii_dir[nii_ndims] = 1; } nii_lens[nii_ndims] = mnc_dlen; nii_ndims++; } nii_ptr->dim[dimmap[i]] = (int) mnc_dlen; nii_ptr->nvox *= mnc_dlen; nii_ptr->pixdim[dimmap[i]] = (float) mnc_dstep; } /* Here we do some "post-processing" of the results. Make certain that * the nt value is never zero, and make certain that ndim is set to * 4 if there is a time dimension and 5 if there is a vector dimension */ if (nii_ptr->dim[3] > 1 && nii_ndims < 4) { nii_ndims = 4; } if (nii_ptr->dim[4] > 1) { nii_ptr->intent_code = NIFTI_INTENT_VECTOR; nii_ndims = 5; } nii_ptr->ndim = nii_ndims; /* Total number of dimensions in file */ nii_ptr->nx = nii_ptr->dim[0]; nii_ptr->ny = nii_ptr->dim[1]; nii_ptr->nz = nii_ptr->dim[2]; nii_ptr->nt = nii_ptr->dim[3]; nii_ptr->nu = nii_ptr->dim[4]; nii_ptr->dx = nii_ptr->pixdim[0]; nii_ptr->dy = nii_ptr->pixdim[1]; nii_ptr->dz = nii_ptr->pixdim[2]; nii_ptr->dt = nii_ptr->pixdim[3]; nii_ptr->du = 1; /* MINC files don't define a sample size for a vector_dimension */ nii_ptr->nifti_type = nifti_filetype; /* Load the direction_cosines and start values into the NIfTI-1 * sform structure. * */ for (i = 0; i < MAX_SPACE_DIMS; i++) { int id = ncvarid(mnc_fd, mnc_spatial_names[i]); double start; double step; double dircos[MAX_SPACE_DIMS]; int tmp; if (id < 0) { continue; } /* Set default values */ start = 0.0; step = 1.0; dircos[DIM_X] = dircos[DIM_Y] = dircos[DIM_Z] = 0.0; dircos[i] = 1.0; miattget(mnc_fd, id, MIstart, NC_DOUBLE, 1, &start, &tmp); miattget(mnc_fd, id, MIstep, NC_DOUBLE, 1, &step, &tmp); miattget(mnc_fd, id, MIdirection_cosines, NC_DOUBLE, MAX_SPACE_DIMS, dircos, &tmp); ncdiminq(mnc_fd, ncdimid(mnc_fd, mnc_spatial_names[i]), NULL, &mnc_dlen); if (step < 0) { step = -step; start = start - step * (mnc_dlen - 1); } nii_ptr->sto_xyz.m[0][i] = step * dircos[0]; nii_ptr->sto_xyz.m[1][i] = step * dircos[1]; nii_ptr->sto_xyz.m[2][i] = step * dircos[2]; nii_ptr->sto_xyz.m[0][3] += start * dircos[0]; nii_ptr->sto_xyz.m[1][3] += start * dircos[1]; nii_ptr->sto_xyz.m[2][3] += start * dircos[2]; miattgetstr(mnc_fd, id, MIspacetype, sizeof(att_str), att_str); /* Try to set the S-transform code correctly. */ if (!strcmp(att_str, MI_TALAIRACH)) { nii_ptr->sform_code = NIFTI_XFORM_TALAIRACH; } else if (!strcmp(att_str, MI_CALLOSAL)) { /* TODO: Not clear what do do here... */ nii_ptr->sform_code = NIFTI_XFORM_SCANNER_ANAT; } else { /* MI_NATIVE or unknown */ nii_ptr->sform_code = NIFTI_XFORM_SCANNER_ANAT; } } /* So the last row is right... */ nii_ptr->sto_xyz.m[3][0] = 0.0; nii_ptr->sto_xyz.m[3][1] = 0.0; nii_ptr->sto_xyz.m[3][2] = 0.0; nii_ptr->sto_xyz.m[3][3] = 1.0; nii_ptr->sto_ijk = nifti_mat44_inverse(nii_ptr->sto_xyz); nifti_datatype_sizes(nii_ptr->datatype, &nii_ptr->nbyper, &nii_ptr->swapsize); if (vflag) { nifti_image_infodump(nii_ptr); } /* Now load the actual MINC data. */ nii_ptr->data = malloc(nii_ptr->nbyper * nii_ptr->nvox); if (nii_ptr->data == NULL) { fprintf(stderr, "Out of memory.\n"); return (-1); } mnc_icv = miicv_create(); miicv_setint(mnc_icv, MI_ICV_TYPE, mnc_type); miicv_setstr(mnc_icv, MI_ICV_SIGN, (mnc_signed) ? MI_SIGNED : MI_UNSIGNED); miicv_setdbl(mnc_icv, MI_ICV_VALID_MAX, output_valid_range[1]); miicv_setdbl(mnc_icv, MI_ICV_VALID_MIN, output_valid_range[0]); miicv_setdbl(mnc_icv, MI_ICV_IMAGE_MAX, real_range[1]); miicv_setdbl(mnc_icv, MI_ICV_IMAGE_MIN, real_range[0]); miicv_setdbl(mnc_icv, MI_ICV_DO_NORM, TRUE); miicv_setdbl(mnc_icv, MI_ICV_USER_NORM, TRUE); miicv_attach(mnc_icv, mnc_fd, mnc_vid); /* Read in the entire hyperslab from the file. */ for (i = 0; i < mnc_ndims; i++) { ncdiminq(mnc_fd, mnc_dimids[i], NULL, &mnc_count[i]); mnc_start[i] = 0; } r = miicv_get(mnc_icv, mnc_start, mnc_count, nii_ptr->data); if (r < 0) { fprintf(stderr, "Read error\n"); return (-1); } /* Shut down the MINC stuff now that it has done its work. */ miicv_detach(mnc_icv); miicv_free(mnc_icv); miclose(mnc_fd); if (vflag) { /* Debugging stuff - just to check the contents of these arrays. */ for (i = 0; i < nii_ndims; i++) { printf("%d: %ld %d %d\n", i, nii_lens[i], nii_map[i], nii_dir[i]); } printf("bytes per voxel %d\n", nii_ptr->nbyper); printf("# of voxels %ld\n", nii_ptr->nvox); } /* Rearrange the data to correspond to the NIfTI dimension ordering. */ restructure_array(nii_ndims, nii_ptr->data, nii_lens, nii_ptr->nbyper, nii_map, nii_dir); if (vflag) { /* More debugging stuff - check coordinate transform. */ test_xform(nii_ptr->sto_xyz, 0, 0, 0); test_xform(nii_ptr->sto_xyz, 10, 0, 0); test_xform(nii_ptr->sto_xyz, 0, 10, 0); test_xform(nii_ptr->sto_xyz, 0, 0, 10); test_xform(nii_ptr->sto_xyz, 10, 10, 10); } if (vflag) { fprintf(stdout, "Writing NIfTI-1 file..."); } nifti_image_write(nii_ptr); if (vflag) { fprintf(stdout, "done.\n"); } return (0); } minc-tools-2.3.00+dfsg/conversion/nifti1/nii2mnc.man10000644000175000000620000000535212574624760021323 0ustar stevestaff.TH nii2mnc 1 "Apr 22 2005" "$Revision: 1.4 $" "" .SH NAME .B nii2mnc - convert a NIfTI-1 or Analyze 7.5 format file to a MINC format file. .SH SYNOPSIS .B nii2mnc .I [] .B nii2mnc .I -help .SH DESCRIPTION The .B nii2mnc command is used to convert "NIfTI-1" format files to MINC format. The NIfTI-1 format was developed by the members of the Neuroinformatics Technology Initiative's Data Format Working Group (DFWG). The NIfTI-1 format is based upon the Mayo Clinic's Analyze 7.5 format. The name of the program is derived from the common filename suffixes used for NIfTI-1 and MINC files. NIfTI-1 defines two possible formats, a "header plus raw image" 2-file format, and a single-file format that includes both header information and the image data. As with Analyze 7.5, the 2-file format consists of one file with the suffix ".hdr" and another file with the extension ".img". In NIfTI-1 single-file format, the two files may be combined into a single file with a ".nii" filename suffix. .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH "Output voxel format" .TP .BI -float Save voxels in 32-bit floating point format .TP .BI -double Save voxels in 64-bit floating point format .TP .BI -byte Save voxels in 8-bit integer format .TP .BI -short Save voxels in 16-bit integer format .TP .BI -int Save voxels in 32-bit integer format .TP .BI -signed Save voxels in signed (2's complement) integer format .TP .BI -unsigned Save voxels in unsigned integer format .SH Analyze 7.5 specific options .TP .B -transverse Assume data is in ZYX dimension order. .TP .B -sagittal Assume data is in XZY dimension order. .TP .B -coronal Assume data is in YZX dimension order. .TP .B -xyz Assume data is in XYZ dimension order. .TP .B -zxy Assume data is in ZXY dimension order. .TP .B -yxz Assume data is in YXZ dimension order. .TP .B -flipx, -flipy, -flipz Invert the samples along the given axis. .SH "Other options" .TP .BI -noscanrange Don't scan data to determine valid range. .TP .BI -quiet Quiet operation - do not print progress or debugging information. .SH "Generic options for all commands" .TP .BI -help Print summary of command\-line options and abort .TP .BI -version Print the program and library versions and abort .SH "KNOWN BUGS" Current handling of NIfTI-1 qform and sform coordinate transforms should probably be revised as the NIfTI group clarifies the correct usage of these fields. .SH "SEE ALSO" .IR mnc2nii .SH AUTHOR Robert Vincent (bert@bic.mni.mcgill.ca) with assistance from the NIfTI-1 library authored by Robert Cox et al. .SH "COPYRIGHTS" Copyrights 2005 by Robert Vincent for the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/conversion/nifti1/nifti1_test.c0000644000175000000620000000515412574624760021603 0ustar stevestaff#include "nifti1_io.c" /* directly include I/O library functions */ /*-----------------------------------------------*/ /* cc -o nifti1_test -O2 nifti1_test.c -lm */ /*-----------------------------------------------*/ /****************************************************************************/ int main( int argc , char *argv[] ) { nifti_image *nim ; int iarg=1 , outmode=1 , ll ; if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf("Usage: nifti1_test [-n2|-n1|-na|-a2] infile [prefix]\n" "\n" " If prefix is given, then the options mean:\n" " -a2 ==> write an ANALYZE 7.5 file pair: prefix.hdr/prefix.img\n" " -n2 ==> write a NIFTI-1 file pair: prefix.hdr/prefix.img\n" " -n1 ==> write a NIFTI-1 single file: prefix.nii\n" " -na ==> write a NIFTI-1 ASCII+binary file: prefix.nia\n" " The default is '-n1'.\n" "\n" " If prefix is not given, then the header info from infile\n" " file is printed to stdout.\n" "\n" " Please note that the '.nia' format is NOT part of the\n" " NIFTI-1 specification, but is provided mostly for ease\n" " of visualization (e.g., you can edit a .nia file and\n" " change some header fields, then rewrite it as .nii)\n" ) ; printf("\nsizeof(nifti_1_header)=%u\n",(unsigned int)sizeof(nifti_1_header)) ; exit(0) ; } if( argv[1][0] == '-' ){ if( argv[1][1] == 'a' ){ outmode = 0 ; } else if( argv[1][1] == 'n' ){ switch( argv[1][2] ){ case '1': outmode = 1 ; break ; default: outmode = 2 ; break ; case 'a': outmode = 3 ; break ; } } iarg++ ; } if( iarg >= argc ){ fprintf(stderr,"** ERROR: no input file on command line!?\n"); exit(1); } nim = nifti_image_read( argv[iarg++] , 1 ) ; if( nim == NULL ) exit(1) ; if( iarg >= argc ){ nifti_image_infodump(nim); exit(0); } nim->nifti_type = outmode ; if( nim->fname != NULL ) free(nim->fname) ; if( nim->iname != NULL ) free(nim->iname) ; ll = strlen(argv[iarg]) ; nim->fname = (char *)calloc(1,ll+6) ; strcpy(nim->fname,argv[iarg]) ; nim->iname = (char *)calloc(1,ll+6) ; strcpy(nim->iname,argv[iarg]) ; if( nim->nifti_type == 1 ){ strcat(nim->fname,".nii") ; strcat(nim->iname,".nii") ; } else if ( nim->nifti_type == 3 ){ strcat(nim->fname,".nia") ; strcat(nim->iname,".nia") ; } else { strcat(nim->fname,".hdr") ; strcat(nim->iname,".img") ; } nifti_image_write( nim ) ; exit(0) ; } minc-tools-2.3.00+dfsg/conversion/nifti1/nifti_stats.c0000644000175000000620000110404312574624760021677 0ustar stevestaff /************************************************************************/ /** Functions to compute cumulative distributions and their inverses **/ /** for the NIfTI-1 statistical types. Much of this code is taken **/ /** from other sources. In particular, the cdflib functions by **/ /** Brown and Lovato make up the bulk of this file. That code **/ /** was placed in the public domain. The code by K. Krishnamoorthy **/ /** is also released for unrestricted use. Finally, the other parts **/ /** of this file (by RW Cox) are released to the public domain. **/ /** **/ /** Most of this file comprises a set of "static" functions, to be **/ /** called by the user-level functions at the very end of the file. **/ /** At the end of the file is a simple main program to drive these **/ /** functions. **/ /** **/ /** To find the user-level functions, search forward for the string **/ /** "nifti_", which will be at about line 11000. **/ /************************************************************************/ /*****==============================================================*****/ /***** 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! *****/ /*****==============================================================*****/ /************************************************************************/ /*....................................................................... To compile with gcc, for example: gcc -O3 -ffast-math -o nifti_stats nifti_stats.c -lm ........................................................................*/ #include "nifti1.h" /* for the NIFTI_INTENT_* constants */ #include #include #include /************************************************************************/ /************ Include all the cdflib functions here and now *************/ /************ [about 9900 lines of code below here] *************/ /************************************************************************/ /** Prototypes for cdflib functions **/ static double algdiv(double*,double*); static double alngam(double*); static double alnrel(double*); static double apser(double*,double*,double*,double*); static double basym(double*,double*,double*,double*); static double bcorr(double*,double*); static double betaln(double*,double*); static double bfrac(double*,double*,double*,double*,double*,double*); static void bgrat(double*,double*,double*,double*,double*,double*,int*i); static double bpser(double*,double*,double*,double*); static void bratio(double*,double*,double*,double*,double*,double*,int*); static double brcmp1(int*,double*,double*,double*,double*); static double brcomp(double*,double*,double*,double*); static double bup(double*,double*,double*,double*,int*,double*); static void cdfbet(int*,double*,double*,double*,double*,double*,double*, int*,double*); static void cdfbin(int*,double*,double*,double*,double*,double*,double*, int*,double*); static void cdfchi(int*,double*,double*,double*,double*,int*,double*); static void cdfchn(int*,double*,double*,double*,double*,double*,int*,double*); static void cdff(int*,double*,double*,double*,double*,double*,int*,double*); static void cdffnc(int*,double*,double*,double*,double*,double*,double*, int*s,double*); static void cdfgam(int*,double*,double*,double*,double*,double*,int*,double*); static void cdfnbn(int*,double*,double*,double*,double*,double*,double*, int*,double*); static void cdfnor(int*,double*,double*,double*,double*,double*,int*,double*); static void cdfpoi(int*,double*,double*,double*,double*,int*,double*); static void cdft(int*,double*,double*,double*,double*,int*,double*); static void cumbet(double*,double*,double*,double*,double*,double*); static void cumbin(double*,double*,double*,double*,double*,double*); static void cumchi(double*,double*,double*,double*); static void cumchn(double*,double*,double*,double*,double*); static void cumf(double*,double*,double*,double*,double*); static void cumfnc(double*,double*,double*,double*,double*,double*); static void cumgam(double*,double*,double*,double*); static void cumnbn(double*,double*,double*,double*,double*,double*); static void cumnor(double*,double*,double*); static void cumpoi(double*,double*,double*,double*); static void cumt(double*,double*,double*,double*); static double dbetrm(double*,double*); static double devlpl(double [],int*,double*); static double dexpm1(double*); static double dinvnr(double *p,double *q); static void E0000(int,int*,double*,double*,unsigned long*, unsigned long*,double*,double*,double*, double*,double*,double*,double*); static void dinvr(int*,double*,double*,unsigned long*,unsigned long*); static void dstinv(double*,double*,double*,double*,double*,double*, double*); static double dlanor(double*); static double dln1mx(double*); static double dln1px(double*); static double dlnbet(double*,double*); static double dlngam(double*); static double dstrem(double*); static double dt1(double*,double*,double*); static void E0001(int,int*,double*,double*,double*,double*, unsigned long*,unsigned long*,double*,double*, double*,double*); static void dzror(int*,double*,double*,double*,double *, unsigned long*,unsigned long*); static void dstzr(double *zxlo,double *zxhi,double *zabstl,double *zreltl); static double erf1(double*); static double erfc1(int*,double*); static double esum(int*,double*); static double exparg(int*); static double fpser(double*,double*,double*,double*); static double gam1(double*); static void gaminv(double*,double*,double*,double*,double*,int*); static double gamln(double*); static double gamln1(double*); static double Xgamm(double*); static void grat1(double*,double*,double*,double*,double*,double*); static void gratio(double*,double*,double*,double*,int*); static double gsumln(double*,double*); static double psi(double*); static double rcomp(double*,double*); static double rexp(double*); static double rlog(double*); static double rlog1(double*); static double spmpar(int*); static double stvaln(double*); static double fifdint(double); static double fifdmax1(double,double); static double fifdmin1(double,double); static double fifdsign(double,double); static long fifidint(double); static long fifmod(long,long); static void ftnstop(char*); static int ipmpar(int*); /***=====================================================================***/ static double algdiv(double *a,double *b) /* ----------------------------------------------------------------------- COMPUTATION OF LN(GAMMA(B)/GAMMA(A+B)) WHEN B .GE. 8 -------- IN THIS ALGORITHM, DEL(X) IS THE FUNCTION DEFINED BY LN(GAMMA(X)) = (X - 0.5)*LN(X) - X + 0.5*LN(2*PI) + DEL(X). ----------------------------------------------------------------------- */ { static double c0 = .833333333333333e-01; static double c1 = -.277777777760991e-02; static double c2 = .793650666825390e-03; static double c3 = -.595202931351870e-03; static double c4 = .837308034031215e-03; static double c5 = -.165322962780713e-02; static double algdiv,c,d,h,s11,s3,s5,s7,s9,t,u,v,w,x,x2,T1; /* .. .. Executable Statements .. */ if(*a <= *b) goto S10; h = *b/ *a; c = 1.0e0/(1.0e0+h); x = h/(1.0e0+h); d = *a+(*b-0.5e0); goto S20; S10: h = *a/ *b; c = h/(1.0e0+h); x = 1.0e0/(1.0e0+h); d = *b+(*a-0.5e0); S20: /* SET SN = (1 - X**N)/(1 - X) */ x2 = x*x; s3 = 1.0e0+(x+x2); s5 = 1.0e0+(x+x2*s3); s7 = 1.0e0+(x+x2*s5); s9 = 1.0e0+(x+x2*s7); s11 = 1.0e0+(x+x2*s9); /* SET W = DEL(B) - DEL(A + B) */ t = pow(1.0e0/ *b,2.0); w = ((((c5*s11*t+c4*s9)*t+c3*s7)*t+c2*s5)*t+c1*s3)*t+c0; w *= (c/ *b); /* COMBINE THE RESULTS */ T1 = *a/ *b; u = d*alnrel(&T1); v = *a*(log(*b)-1.0e0); if(u <= v) goto S30; algdiv = w-v-u; return algdiv; S30: algdiv = w-u-v; return algdiv; } /* END */ /***=====================================================================***/ static double alngam(double *x) /* ********************************************************************** double alngam(double *x) double precision LN of the GAMma function Function Returns the natural logarithm of GAMMA(X). Arguments X --> value at which scaled log gamma is to be returned X is DOUBLE PRECISION Method If X .le. 6.0, then use recursion to get X below 3 then apply rational approximation number 5236 of Hart et al, Computer Approximations, John Wiley and Sons, NY, 1968. If X .gt. 6.0, then use recursion to get X to at least 12 and then use formula 5423 of the same source. ********************************************************************** */ { #define hln2pi 0.91893853320467274178e0 static double coef[5] = { 0.83333333333333023564e-1,-0.27777777768818808e-2,0.79365006754279e-3, -0.594997310889e-3,0.8065880899e-3 }; static double scoefd[4] = { 0.62003838007126989331e2,0.9822521104713994894e1,-0.8906016659497461257e1, 0.1000000000000000000e1 }; static double scoefn[9] = { 0.62003838007127258804e2,0.36036772530024836321e2,0.20782472531792126786e2, 0.6338067999387272343e1,0.215994312846059073e1,0.3980671310203570498e0, 0.1093115956710439502e0,0.92381945590275995e-2,0.29737866448101651e-2 }; static int K1 = 9; static int K3 = 4; static int K5 = 5; static double alngam,offset,prod,xx; static int i,n; static double T2,T4,T6; /* .. .. Executable Statements .. */ if(!(*x <= 6.0e0)) goto S70; prod = 1.0e0; xx = *x; if(!(*x > 3.0e0)) goto S30; S10: if(!(xx > 3.0e0)) goto S20; xx -= 1.0e0; prod *= xx; goto S10; S30: S20: if(!(*x < 2.0e0)) goto S60; S40: if(!(xx < 2.0e0)) goto S50; prod /= xx; xx += 1.0e0; goto S40; S60: S50: T2 = xx-2.0e0; T4 = xx-2.0e0; alngam = devlpl(scoefn,&K1,&T2)/devlpl(scoefd,&K3,&T4); /* COMPUTE RATIONAL APPROXIMATION TO GAMMA(X) */ alngam *= prod; alngam = log(alngam); goto S110; S70: offset = hln2pi; /* IF NECESSARY MAKE X AT LEAST 12 AND CARRY CORRECTION IN OFFSET */ n = fifidint(12.0e0-*x); if(!(n > 0)) goto S90; prod = 1.0e0; for(i=1; i<=n; i++) prod *= (*x+(double)(i-1)); offset -= log(prod); xx = *x+(double)n; goto S100; S90: xx = *x; S100: /* COMPUTE POWER SERIES */ T6 = 1.0e0/pow(xx,2.0); alngam = devlpl(coef,&K5,&T6)/xx; alngam += (offset+(xx-0.5e0)*log(xx)-xx); S110: return alngam; #undef hln2pi } /* END */ /***=====================================================================***/ static double alnrel(double *a) /* ----------------------------------------------------------------------- EVALUATION OF THE FUNCTION LN(1 + A) ----------------------------------------------------------------------- */ { static double p1 = -.129418923021993e+01; static double p2 = .405303492862024e+00; static double p3 = -.178874546012214e-01; static double q1 = -.162752256355323e+01; static double q2 = .747811014037616e+00; static double q3 = -.845104217945565e-01; static double alnrel,t,t2,w,x; /* .. .. Executable Statements .. */ if(fabs(*a) > 0.375e0) goto S10; t = *a/(*a+2.0e0); t2 = t*t; w = (((p3*t2+p2)*t2+p1)*t2+1.0e0)/(((q3*t2+q2)*t2+q1)*t2+1.0e0); alnrel = 2.0e0*t*w; return alnrel; S10: x = 1.e0+*a; alnrel = log(x); return alnrel; } /* END */ /***=====================================================================***/ static double apser(double *a,double *b,double *x,double *eps) /* ----------------------------------------------------------------------- APSER YIELDS THE INCOMPLETE BETA RATIO I(SUB(1-X))(B,A) FOR A .LE. MIN(EPS,EPS*B), B*X .LE. 1, AND X .LE. 0.5. USED WHEN A IS VERY SMALL. USE ONLY IF ABOVE INEQUALITIES ARE SATISFIED. ----------------------------------------------------------------------- */ { static double g = .577215664901533e0; static double apser,aj,bx,c,j,s,t,tol; /* .. .. Executable Statements .. */ bx = *b**x; t = *x-bx; if(*b**eps > 2.e-2) goto S10; c = log(*x)+psi(b)+g+t; goto S20; S10: c = log(bx)+g+t; S20: tol = 5.0e0**eps*fabs(c); j = 1.0e0; s = 0.0e0; S30: j += 1.0e0; t *= (*x-bx/j); aj = t/j; s += aj; if(fabs(aj) > tol) goto S30; apser = -(*a*(c+s)); return apser; } /* END */ /***=====================================================================***/ static double basym(double *a,double *b,double *lambda,double *eps) /* ----------------------------------------------------------------------- ASYMPTOTIC EXPANSION FOR IX(A,B) FOR LARGE A AND B. LAMBDA = (A + B)*Y - B AND EPS IS THE TOLERANCE USED. IT IS ASSUMED THAT LAMBDA IS NONNEGATIVE AND THAT A AND B ARE GREATER THAN OR EQUAL TO 15. ----------------------------------------------------------------------- */ { static double e0 = 1.12837916709551e0; static double e1 = .353553390593274e0; static int num = 20; /* ------------------------ ****** NUM IS THE MAXIMUM VALUE THAT N CAN TAKE IN THE DO LOOP ENDING AT STATEMENT 50. IT IS REQUIRED THAT NUM BE EVEN. THE ARRAYS A0, B0, C, D HAVE DIMENSION NUM + 1. ------------------------ E0 = 2/SQRT(PI) E1 = 2**(-3/2) ------------------------ */ static int K3 = 1; static double basym,bsum,dsum,f,h,h2,hn,j0,j1,r,r0,r1,s,sum,t,t0,t1,u,w,w0,z,z0, z2,zn,znm1; static int i,im1,imj,j,m,mm1,mmj,n,np1; static double a0[21],b0[21],c[21],d[21],T1,T2; /* .. .. Executable Statements .. */ basym = 0.0e0; if(*a >= *b) goto S10; h = *a/ *b; r0 = 1.0e0/(1.0e0+h); r1 = (*b-*a)/ *b; w0 = 1.0e0/sqrt(*a*(1.0e0+h)); goto S20; S10: h = *b/ *a; r0 = 1.0e0/(1.0e0+h); r1 = (*b-*a)/ *a; w0 = 1.0e0/sqrt(*b*(1.0e0+h)); S20: T1 = -(*lambda/ *a); T2 = *lambda/ *b; f = *a*rlog1(&T1)+*b*rlog1(&T2); t = exp(-f); if(t == 0.0e0) return basym; z0 = sqrt(f); z = 0.5e0*(z0/e1); z2 = f+f; a0[0] = 2.0e0/3.0e0*r1; c[0] = -(0.5e0*a0[0]); d[0] = -c[0]; j0 = 0.5e0/e0*erfc1(&K3,&z0); j1 = e1; sum = j0+d[0]*w0*j1; s = 1.0e0; h2 = h*h; hn = 1.0e0; w = w0; znm1 = z; zn = z2; for(n=2; n<=num; n+=2) { hn = h2*hn; a0[n-1] = 2.0e0*r0*(1.0e0+h*hn)/((double)n+2.0e0); np1 = n+1; s += hn; a0[np1-1] = 2.0e0*r1*s/((double)n+3.0e0); for(i=n; i<=np1; i++) { r = -(0.5e0*((double)i+1.0e0)); b0[0] = r*a0[0]; for(m=2; m<=i; m++) { bsum = 0.0e0; mm1 = m-1; for(j=1; j<=mm1; j++) { mmj = m-j; bsum += (((double)j*r-(double)mmj)*a0[j-1]*b0[mmj-1]); } b0[m-1] = r*a0[m-1]+bsum/(double)m; } c[i-1] = b0[i-1]/((double)i+1.0e0); dsum = 0.0e0; im1 = i-1; for(j=1; j<=im1; j++) { imj = i-j; dsum += (d[imj-1]*c[j-1]); } d[i-1] = -(dsum+c[i-1]); } j0 = e1*znm1+((double)n-1.0e0)*j0; j1 = e1*zn+(double)n*j1; znm1 = z2*znm1; zn = z2*zn; w = w0*w; t0 = d[n-1]*w*j0; w = w0*w; t1 = d[np1-1]*w*j1; sum += (t0+t1); if(fabs(t0)+fabs(t1) <= *eps*sum) goto S80; } S80: u = exp(-bcorr(a,b)); basym = e0*t*u*sum; return basym; } /* END */ /***=====================================================================***/ static double bcorr(double *a0,double *b0) /* ----------------------------------------------------------------------- EVALUATION OF DEL(A0) + DEL(B0) - DEL(A0 + B0) WHERE LN(GAMMA(A)) = (A - 0.5)*LN(A) - A + 0.5*LN(2*PI) + DEL(A). IT IS ASSUMED THAT A0 .GE. 8 AND B0 .GE. 8. ----------------------------------------------------------------------- */ { static double c0 = .833333333333333e-01; static double c1 = -.277777777760991e-02; static double c2 = .793650666825390e-03; static double c3 = -.595202931351870e-03; static double c4 = .837308034031215e-03; static double c5 = -.165322962780713e-02; static double bcorr,a,b,c,h,s11,s3,s5,s7,s9,t,w,x,x2; /* .. .. Executable Statements .. */ a = fifdmin1(*a0,*b0); b = fifdmax1(*a0,*b0); h = a/b; c = h/(1.0e0+h); x = 1.0e0/(1.0e0+h); x2 = x*x; /* SET SN = (1 - X**N)/(1 - X) */ s3 = 1.0e0+(x+x2); s5 = 1.0e0+(x+x2*s3); s7 = 1.0e0+(x+x2*s5); s9 = 1.0e0+(x+x2*s7); s11 = 1.0e0+(x+x2*s9); /* SET W = DEL(B) - DEL(A + B) */ t = pow(1.0e0/b,2.0); w = ((((c5*s11*t+c4*s9)*t+c3*s7)*t+c2*s5)*t+c1*s3)*t+c0; w *= (c/b); /* COMPUTE DEL(A) + W */ t = pow(1.0e0/a,2.0); bcorr = (((((c5*t+c4)*t+c3)*t+c2)*t+c1)*t+c0)/a+w; return bcorr; } /* END */ /***=====================================================================***/ static double betaln(double *a0,double *b0) /* ----------------------------------------------------------------------- EVALUATION OF THE LOGARITHM OF THE BETA FUNCTION ----------------------------------------------------------------------- E = 0.5*LN(2*PI) -------------------------- */ { static double e = .918938533204673e0; static double betaln,a,b,c,h,u,v,w,z; static int i,n; static double T1; /* .. .. Executable Statements .. */ a = fifdmin1(*a0,*b0); b = fifdmax1(*a0,*b0); if(a >= 8.0e0) goto S100; if(a >= 1.0e0) goto S20; /* ----------------------------------------------------------------------- PROCEDURE WHEN A .LT. 1 ----------------------------------------------------------------------- */ if(b >= 8.0e0) goto S10; T1 = a+b; betaln = gamln(&a)+(gamln(&b)-gamln(&T1)); return betaln; S10: betaln = gamln(&a)+algdiv(&a,&b); return betaln; S20: /* ----------------------------------------------------------------------- PROCEDURE WHEN 1 .LE. A .LT. 8 ----------------------------------------------------------------------- */ if(a > 2.0e0) goto S40; if(b > 2.0e0) goto S30; betaln = gamln(&a)+gamln(&b)-gsumln(&a,&b); return betaln; S30: w = 0.0e0; if(b < 8.0e0) goto S60; betaln = gamln(&a)+algdiv(&a,&b); return betaln; S40: /* REDUCTION OF A WHEN B .LE. 1000 */ if(b > 1000.0e0) goto S80; n = a-1.0e0; w = 1.0e0; for(i=1; i<=n; i++) { a -= 1.0e0; h = a/b; w *= (h/(1.0e0+h)); } w = log(w); if(b < 8.0e0) goto S60; betaln = w+gamln(&a)+algdiv(&a,&b); return betaln; S60: /* REDUCTION OF B WHEN B .LT. 8 */ n = b-1.0e0; z = 1.0e0; for(i=1; i<=n; i++) { b -= 1.0e0; z *= (b/(a+b)); } betaln = w+log(z)+(gamln(&a)+(gamln(&b)-gsumln(&a,&b))); return betaln; S80: /* REDUCTION OF A WHEN B .GT. 1000 */ n = a-1.0e0; w = 1.0e0; for(i=1; i<=n; i++) { a -= 1.0e0; w *= (a/(1.0e0+a/b)); } betaln = log(w)-(double)n*log(b)+(gamln(&a)+algdiv(&a,&b)); return betaln; S100: /* ----------------------------------------------------------------------- PROCEDURE WHEN A .GE. 8 ----------------------------------------------------------------------- */ w = bcorr(&a,&b); h = a/b; c = h/(1.0e0+h); u = -((a-0.5e0)*log(c)); v = b*alnrel(&h); if(u <= v) goto S110; betaln = -(0.5e0*log(b))+e+w-v-u; return betaln; S110: betaln = -(0.5e0*log(b))+e+w-u-v; return betaln; } /* END */ /***=====================================================================***/ static double bfrac(double *a,double *b,double *x,double *y,double *lambda, double *eps) /* ----------------------------------------------------------------------- CONTINUED FRACTION EXPANSION FOR IX(A,B) WHEN A,B .GT. 1. IT IS ASSUMED THAT LAMBDA = (A + B)*Y - B. ----------------------------------------------------------------------- */ { static double bfrac,alpha,an,anp1,beta,bn,bnp1,c,c0,c1,e,n,p,r,r0,s,t,w,yp1; /* .. .. Executable Statements .. */ bfrac = brcomp(a,b,x,y); if(bfrac == 0.0e0) return bfrac; c = 1.0e0+*lambda; c0 = *b/ *a; c1 = 1.0e0+1.0e0/ *a; yp1 = *y+1.0e0; n = 0.0e0; p = 1.0e0; s = *a+1.0e0; an = 0.0e0; bn = anp1 = 1.0e0; bnp1 = c/c1; r = c1/c; S10: /* CONTINUED FRACTION CALCULATION */ n += 1.0e0; t = n/ *a; w = n*(*b-n)**x; e = *a/s; alpha = p*(p+c0)*e*e*(w**x); e = (1.0e0+t)/(c1+t+t); beta = n+w/s+e*(c+n*yp1); p = 1.0e0+t; s += 2.0e0; /* UPDATE AN, BN, ANP1, AND BNP1 */ t = alpha*an+beta*anp1; an = anp1; anp1 = t; t = alpha*bn+beta*bnp1; bn = bnp1; bnp1 = t; r0 = r; r = anp1/bnp1; if(fabs(r-r0) <= *eps*r) goto S20; /* RESCALE AN, BN, ANP1, AND BNP1 */ an /= bnp1; bn /= bnp1; anp1 = r; bnp1 = 1.0e0; goto S10; S20: /* TERMINATION */ bfrac *= r; return bfrac; } /* END */ /***=====================================================================***/ static void bgrat(double *a,double *b,double *x,double *y,double *w, double *eps,int *ierr) /* ----------------------------------------------------------------------- ASYMPTOTIC EXPANSION FOR IX(A,B) WHEN A IS LARGER THAN B. THE RESULT OF THE EXPANSION IS ADDED TO W. IT IS ASSUMED THAT A .GE. 15 AND B .LE. 1. EPS IS THE TOLERANCE USED. IERR IS A VARIABLE THAT REPORTS THE STATUS OF THE RESULTS. ----------------------------------------------------------------------- */ { static double bm1,bp2n,cn,coef,dj,j,l,lnx,n2,nu,p,q,r,s,sum,t,t2,u,v,z; static int i,n,nm1; static double c[30],d[30],T1; /* .. .. Executable Statements .. */ bm1 = *b-0.5e0-0.5e0; nu = *a+0.5e0*bm1; if(*y > 0.375e0) goto S10; T1 = -*y; lnx = alnrel(&T1); goto S20; S10: lnx = log(*x); S20: z = -(nu*lnx); if(*b*z == 0.0e0) goto S70; /* COMPUTATION OF THE EXPANSION SET R = EXP(-Z)*Z**B/GAMMA(B) */ r = *b*(1.0e0+gam1(b))*exp(*b*log(z)); r *= (exp(*a*lnx)*exp(0.5e0*bm1*lnx)); u = algdiv(b,a)+*b*log(nu); u = r*exp(-u); if(u == 0.0e0) goto S70; grat1(b,&z,&r,&p,&q,eps); v = 0.25e0*pow(1.0e0/nu,2.0); t2 = 0.25e0*lnx*lnx; l = *w/u; j = q/r; sum = j; t = cn = 1.0e0; n2 = 0.0e0; for(n=1; n<=30; n++) { bp2n = *b+n2; j = (bp2n*(bp2n+1.0e0)*j+(z+bp2n+1.0e0)*t)*v; n2 += 2.0e0; t *= t2; cn /= (n2*(n2+1.0e0)); c[n-1] = cn; s = 0.0e0; if(n == 1) goto S40; nm1 = n-1; coef = *b-(double)n; for(i=1; i<=nm1; i++) { s += (coef*c[i-1]*d[n-i-1]); coef += *b; } S40: d[n-1] = bm1*cn+s/(double)n; dj = d[n-1]*j; sum += dj; if(sum <= 0.0e0) goto S70; if(fabs(dj) <= *eps*(sum+l)) goto S60; } S60: /* ADD THE RESULTS TO W */ *ierr = 0; *w += (u*sum); return; S70: /* THE EXPANSION CANNOT BE COMPUTED */ *ierr = 1; return; } /* END */ /***=====================================================================***/ static double bpser(double *a,double *b,double *x,double *eps) /* ----------------------------------------------------------------------- POWER SERIES EXPANSION FOR EVALUATING IX(A,B) WHEN B .LE. 1 OR B*X .LE. 0.7. EPS IS THE TOLERANCE USED. ----------------------------------------------------------------------- */ { static double bpser,a0,apb,b0,c,n,sum,t,tol,u,w,z; static int i,m; /* .. .. Executable Statements .. */ bpser = 0.0e0; if(*x == 0.0e0) return bpser; /* ----------------------------------------------------------------------- COMPUTE THE FACTOR X**A/(A*BETA(A,B)) ----------------------------------------------------------------------- */ a0 = fifdmin1(*a,*b); if(a0 < 1.0e0) goto S10; z = *a*log(*x)-betaln(a,b); bpser = exp(z)/ *a; goto S100; S10: b0 = fifdmax1(*a,*b); if(b0 >= 8.0e0) goto S90; if(b0 > 1.0e0) goto S40; /* PROCEDURE FOR A0 .LT. 1 AND B0 .LE. 1 */ bpser = pow(*x,*a); if(bpser == 0.0e0) return bpser; apb = *a+*b; if(apb > 1.0e0) goto S20; z = 1.0e0+gam1(&apb); goto S30; S20: u = *a+*b-1.e0; z = (1.0e0+gam1(&u))/apb; S30: c = (1.0e0+gam1(a))*(1.0e0+gam1(b))/z; bpser *= (c*(*b/apb)); goto S100; S40: /* PROCEDURE FOR A0 .LT. 1 AND 1 .LT. B0 .LT. 8 */ u = gamln1(&a0); m = b0-1.0e0; if(m < 1) goto S60; c = 1.0e0; for(i=1; i<=m; i++) { b0 -= 1.0e0; c *= (b0/(a0+b0)); } u = log(c)+u; S60: z = *a*log(*x)-u; b0 -= 1.0e0; apb = a0+b0; if(apb > 1.0e0) goto S70; t = 1.0e0+gam1(&apb); goto S80; S70: u = a0+b0-1.e0; t = (1.0e0+gam1(&u))/apb; S80: bpser = exp(z)*(a0/ *a)*(1.0e0+gam1(&b0))/t; goto S100; S90: /* PROCEDURE FOR A0 .LT. 1 AND B0 .GE. 8 */ u = gamln1(&a0)+algdiv(&a0,&b0); z = *a*log(*x)-u; bpser = a0/ *a*exp(z); S100: if(bpser == 0.0e0 || *a <= 0.1e0**eps) return bpser; /* ----------------------------------------------------------------------- COMPUTE THE SERIES ----------------------------------------------------------------------- */ sum = n = 0.0e0; c = 1.0e0; tol = *eps/ *a; S110: n += 1.0e0; c *= ((0.5e0+(0.5e0-*b/n))**x); w = c/(*a+n); sum += w; if(fabs(w) > tol) goto S110; bpser *= (1.0e0+*a*sum); return bpser; } /* END */ /***=====================================================================***/ static void bratio(double *a,double *b,double *x,double *y,double *w, double *w1,int *ierr) /* ----------------------------------------------------------------------- EVALUATION OF THE INCOMPLETE BETA FUNCTION IX(A,B) -------------------- IT IS ASSUMED THAT A AND B ARE NONNEGATIVE, AND THAT X .LE. 1 AND Y = 1 - X. BRATIO ASSIGNS W AND W1 THE VALUES W = IX(A,B) W1 = 1 - IX(A,B) IERR IS A VARIABLE THAT REPORTS THE STATUS OF THE RESULTS. IF NO INPUT ERRORS ARE DETECTED THEN IERR IS SET TO 0 AND W AND W1 ARE COMPUTED. OTHERWISE, IF AN ERROR IS DETECTED, THEN W AND W1 ARE ASSIGNED THE VALUE 0 AND IERR IS SET TO ONE OF THE FOLLOWING VALUES ... IERR = 1 IF A OR B IS NEGATIVE IERR = 2 IF A = B = 0 IERR = 3 IF X .LT. 0 OR X .GT. 1 IERR = 4 IF Y .LT. 0 OR Y .GT. 1 IERR = 5 IF X + Y .NE. 1 IERR = 6 IF X = A = 0 IERR = 7 IF Y = B = 0 -------------------- WRITTEN BY ALFRED H. MORRIS, JR. NAVAL SURFACE WARFARE CENTER DAHLGREN, VIRGINIA REVISED ... NOV 1991 ----------------------------------------------------------------------- */ { static int K1 = 1; static double a0,b0,eps,lambda,t,x0,y0,z; static int ierr1,ind,n; static double T2,T3,T4,T5; /* .. .. Executable Statements .. */ /* ****** EPS IS A MACHINE DEPENDENT CONSTANT. EPS IS THE SMALLEST FLOATING POINT NUMBER FOR WHICH 1.0 + EPS .GT. 1.0 */ eps = spmpar(&K1); *w = *w1 = 0.0e0; if(*a < 0.0e0 || *b < 0.0e0) goto S270; if(*a == 0.0e0 && *b == 0.0e0) goto S280; if(*x < 0.0e0 || *x > 1.0e0) goto S290; if(*y < 0.0e0 || *y > 1.0e0) goto S300; z = *x+*y-0.5e0-0.5e0; if(fabs(z) > 3.0e0*eps) goto S310; *ierr = 0; if(*x == 0.0e0) goto S210; if(*y == 0.0e0) goto S230; if(*a == 0.0e0) goto S240; if(*b == 0.0e0) goto S220; eps = fifdmax1(eps,1.e-15); if(fifdmax1(*a,*b) < 1.e-3*eps) goto S260; ind = 0; a0 = *a; b0 = *b; x0 = *x; y0 = *y; if(fifdmin1(a0,b0) > 1.0e0) goto S40; /* PROCEDURE FOR A0 .LE. 1 OR B0 .LE. 1 */ if(*x <= 0.5e0) goto S10; ind = 1; a0 = *b; b0 = *a; x0 = *y; y0 = *x; S10: if(b0 < fifdmin1(eps,eps*a0)) goto S90; if(a0 < fifdmin1(eps,eps*b0) && b0*x0 <= 1.0e0) goto S100; if(fifdmax1(a0,b0) > 1.0e0) goto S20; if(a0 >= fifdmin1(0.2e0,b0)) goto S110; if(pow(x0,a0) <= 0.9e0) goto S110; if(x0 >= 0.3e0) goto S120; n = 20; goto S140; S20: if(b0 <= 1.0e0) goto S110; if(x0 >= 0.3e0) goto S120; if(x0 >= 0.1e0) goto S30; if(pow(x0*b0,a0) <= 0.7e0) goto S110; S30: if(b0 > 15.0e0) goto S150; n = 20; goto S140; S40: /* PROCEDURE FOR A0 .GT. 1 AND B0 .GT. 1 */ if(*a > *b) goto S50; lambda = *a-(*a+*b)**x; goto S60; S50: lambda = (*a+*b)**y-*b; S60: if(lambda >= 0.0e0) goto S70; ind = 1; a0 = *b; b0 = *a; x0 = *y; y0 = *x; lambda = fabs(lambda); S70: if(b0 < 40.0e0 && b0*x0 <= 0.7e0) goto S110; if(b0 < 40.0e0) goto S160; if(a0 > b0) goto S80; if(a0 <= 100.0e0) goto S130; if(lambda > 0.03e0*a0) goto S130; goto S200; S80: if(b0 <= 100.0e0) goto S130; if(lambda > 0.03e0*b0) goto S130; goto S200; S90: /* EVALUATION OF THE APPROPRIATE ALGORITHM */ *w = fpser(&a0,&b0,&x0,&eps); *w1 = 0.5e0+(0.5e0-*w); goto S250; S100: *w1 = apser(&a0,&b0,&x0,&eps); *w = 0.5e0+(0.5e0-*w1); goto S250; S110: *w = bpser(&a0,&b0,&x0,&eps); *w1 = 0.5e0+(0.5e0-*w); goto S250; S120: *w1 = bpser(&b0,&a0,&y0,&eps); *w = 0.5e0+(0.5e0-*w1); goto S250; S130: T2 = 15.0e0*eps; *w = bfrac(&a0,&b0,&x0,&y0,&lambda,&T2); *w1 = 0.5e0+(0.5e0-*w); goto S250; S140: *w1 = bup(&b0,&a0,&y0,&x0,&n,&eps); b0 += (double)n; S150: T3 = 15.0e0*eps; bgrat(&b0,&a0,&y0,&x0,w1,&T3,&ierr1); *w = 0.5e0+(0.5e0-*w1); goto S250; S160: n = b0; b0 -= (double)n; if(b0 != 0.0e0) goto S170; n -= 1; b0 = 1.0e0; S170: *w = bup(&b0,&a0,&y0,&x0,&n,&eps); if(x0 > 0.7e0) goto S180; *w += bpser(&a0,&b0,&x0,&eps); *w1 = 0.5e0+(0.5e0-*w); goto S250; S180: if(a0 > 15.0e0) goto S190; n = 20; *w += bup(&a0,&b0,&x0,&y0,&n,&eps); a0 += (double)n; S190: T4 = 15.0e0*eps; bgrat(&a0,&b0,&x0,&y0,w,&T4,&ierr1); *w1 = 0.5e0+(0.5e0-*w); goto S250; S200: T5 = 100.0e0*eps; *w = basym(&a0,&b0,&lambda,&T5); *w1 = 0.5e0+(0.5e0-*w); goto S250; S210: /* TERMINATION OF THE PROCEDURE */ if(*a == 0.0e0) goto S320; S220: *w = 0.0e0; *w1 = 1.0e0; return; S230: if(*b == 0.0e0) goto S330; S240: *w = 1.0e0; *w1 = 0.0e0; return; S250: if(ind == 0) return; t = *w; *w = *w1; *w1 = t; return; S260: /* PROCEDURE FOR A AND B .LT. 1.E-3*EPS */ *w = *b/(*a+*b); *w1 = *a/(*a+*b); return; S270: /* ERROR RETURN */ *ierr = 1; return; S280: *ierr = 2; return; S290: *ierr = 3; return; S300: *ierr = 4; return; S310: *ierr = 5; return; S320: *ierr = 6; return; S330: *ierr = 7; return; } /* END */ /***=====================================================================***/ static double brcmp1(int *mu,double *a,double *b,double *x,double *y) /* ----------------------------------------------------------------------- EVALUATION OF EXP(MU) * (X**A*Y**B/BETA(A,B)) ----------------------------------------------------------------------- */ { static double Const = .398942280401433e0; static double brcmp1,a0,apb,b0,c,e,h,lambda,lnx,lny,t,u,v,x0,y0,z; static int i,n; /* ----------------- CONST = 1/SQRT(2*PI) ----------------- */ static double T1,T2,T3,T4; /* .. .. Executable Statements .. */ a0 = fifdmin1(*a,*b); if(a0 >= 8.0e0) goto S130; if(*x > 0.375e0) goto S10; lnx = log(*x); T1 = -*x; lny = alnrel(&T1); goto S30; S10: if(*y > 0.375e0) goto S20; T2 = -*y; lnx = alnrel(&T2); lny = log(*y); goto S30; S20: lnx = log(*x); lny = log(*y); S30: z = *a*lnx+*b*lny; if(a0 < 1.0e0) goto S40; z -= betaln(a,b); brcmp1 = esum(mu,&z); return brcmp1; S40: /* ----------------------------------------------------------------------- PROCEDURE FOR A .LT. 1 OR B .LT. 1 ----------------------------------------------------------------------- */ b0 = fifdmax1(*a,*b); if(b0 >= 8.0e0) goto S120; if(b0 > 1.0e0) goto S70; /* ALGORITHM FOR B0 .LE. 1 */ brcmp1 = esum(mu,&z); if(brcmp1 == 0.0e0) return brcmp1; apb = *a+*b; if(apb > 1.0e0) goto S50; z = 1.0e0+gam1(&apb); goto S60; S50: u = *a+*b-1.e0; z = (1.0e0+gam1(&u))/apb; S60: c = (1.0e0+gam1(a))*(1.0e0+gam1(b))/z; brcmp1 = brcmp1*(a0*c)/(1.0e0+a0/b0); return brcmp1; S70: /* ALGORITHM FOR 1 .LT. B0 .LT. 8 */ u = gamln1(&a0); n = b0-1.0e0; if(n < 1) goto S90; c = 1.0e0; for(i=1; i<=n; i++) { b0 -= 1.0e0; c *= (b0/(a0+b0)); } u = log(c)+u; S90: z -= u; b0 -= 1.0e0; apb = a0+b0; if(apb > 1.0e0) goto S100; t = 1.0e0+gam1(&apb); goto S110; S100: u = a0+b0-1.e0; t = (1.0e0+gam1(&u))/apb; S110: brcmp1 = a0*esum(mu,&z)*(1.0e0+gam1(&b0))/t; return brcmp1; S120: /* ALGORITHM FOR B0 .GE. 8 */ u = gamln1(&a0)+algdiv(&a0,&b0); T3 = z-u; brcmp1 = a0*esum(mu,&T3); return brcmp1; S130: /* ----------------------------------------------------------------------- PROCEDURE FOR A .GE. 8 AND B .GE. 8 ----------------------------------------------------------------------- */ if(*a > *b) goto S140; h = *a/ *b; x0 = h/(1.0e0+h); y0 = 1.0e0/(1.0e0+h); lambda = *a-(*a+*b)**x; goto S150; S140: h = *b/ *a; x0 = 1.0e0/(1.0e0+h); y0 = h/(1.0e0+h); lambda = (*a+*b)**y-*b; S150: e = -(lambda/ *a); if(fabs(e) > 0.6e0) goto S160; u = rlog1(&e); goto S170; S160: u = e-log(*x/x0); S170: e = lambda/ *b; if(fabs(e) > 0.6e0) goto S180; v = rlog1(&e); goto S190; S180: v = e-log(*y/y0); S190: T4 = -(*a*u+*b*v); z = esum(mu,&T4); brcmp1 = Const*sqrt(*b*x0)*z*exp(-bcorr(a,b)); return brcmp1; } /* END */ /***=====================================================================***/ static double brcomp(double *a,double *b,double *x,double *y) /* ----------------------------------------------------------------------- EVALUATION OF X**A*Y**B/BETA(A,B) ----------------------------------------------------------------------- */ { static double Const = .398942280401433e0; static double brcomp,a0,apb,b0,c,e,h,lambda,lnx,lny,t,u,v,x0,y0,z; static int i,n; /* ----------------- CONST = 1/SQRT(2*PI) ----------------- */ static double T1,T2; /* .. .. Executable Statements .. */ brcomp = 0.0e0; if(*x == 0.0e0 || *y == 0.0e0) return brcomp; a0 = fifdmin1(*a,*b); if(a0 >= 8.0e0) goto S130; if(*x > 0.375e0) goto S10; lnx = log(*x); T1 = -*x; lny = alnrel(&T1); goto S30; S10: if(*y > 0.375e0) goto S20; T2 = -*y; lnx = alnrel(&T2); lny = log(*y); goto S30; S20: lnx = log(*x); lny = log(*y); S30: z = *a*lnx+*b*lny; if(a0 < 1.0e0) goto S40; z -= betaln(a,b); brcomp = exp(z); return brcomp; S40: /* ----------------------------------------------------------------------- PROCEDURE FOR A .LT. 1 OR B .LT. 1 ----------------------------------------------------------------------- */ b0 = fifdmax1(*a,*b); if(b0 >= 8.0e0) goto S120; if(b0 > 1.0e0) goto S70; /* ALGORITHM FOR B0 .LE. 1 */ brcomp = exp(z); if(brcomp == 0.0e0) return brcomp; apb = *a+*b; if(apb > 1.0e0) goto S50; z = 1.0e0+gam1(&apb); goto S60; S50: u = *a+*b-1.e0; z = (1.0e0+gam1(&u))/apb; S60: c = (1.0e0+gam1(a))*(1.0e0+gam1(b))/z; brcomp = brcomp*(a0*c)/(1.0e0+a0/b0); return brcomp; S70: /* ALGORITHM FOR 1 .LT. B0 .LT. 8 */ u = gamln1(&a0); n = b0-1.0e0; if(n < 1) goto S90; c = 1.0e0; for(i=1; i<=n; i++) { b0 -= 1.0e0; c *= (b0/(a0+b0)); } u = log(c)+u; S90: z -= u; b0 -= 1.0e0; apb = a0+b0; if(apb > 1.0e0) goto S100; t = 1.0e0+gam1(&apb); goto S110; S100: u = a0+b0-1.e0; t = (1.0e0+gam1(&u))/apb; S110: brcomp = a0*exp(z)*(1.0e0+gam1(&b0))/t; return brcomp; S120: /* ALGORITHM FOR B0 .GE. 8 */ u = gamln1(&a0)+algdiv(&a0,&b0); brcomp = a0*exp(z-u); return brcomp; S130: /* ----------------------------------------------------------------------- PROCEDURE FOR A .GE. 8 AND B .GE. 8 ----------------------------------------------------------------------- */ if(*a > *b) goto S140; h = *a/ *b; x0 = h/(1.0e0+h); y0 = 1.0e0/(1.0e0+h); lambda = *a-(*a+*b)**x; goto S150; S140: h = *b/ *a; x0 = 1.0e0/(1.0e0+h); y0 = h/(1.0e0+h); lambda = (*a+*b)**y-*b; S150: e = -(lambda/ *a); if(fabs(e) > 0.6e0) goto S160; u = rlog1(&e); goto S170; S160: u = e-log(*x/x0); S170: e = lambda/ *b; if(fabs(e) > 0.6e0) goto S180; v = rlog1(&e); goto S190; S180: v = e-log(*y/y0); S190: z = exp(-(*a*u+*b*v)); brcomp = Const*sqrt(*b*x0)*z*exp(-bcorr(a,b)); return brcomp; } /* END */ /***=====================================================================***/ static double bup(double *a,double *b,double *x,double *y,int *n,double *eps) /* ----------------------------------------------------------------------- EVALUATION OF IX(A,B) - IX(A+N,B) WHERE N IS A POSITIVE INTEGER. EPS IS THE TOLERANCE USED. ----------------------------------------------------------------------- */ { static int K1 = 1; static int K2 = 0; static double bup,ap1,apb,d,l,r,t,w; static int i,k,kp1,mu,nm1; /* .. .. Executable Statements .. */ /* OBTAIN THE SCALING FACTOR EXP(-MU) AND EXP(MU)*(X**A*Y**B/BETA(A,B))/A */ apb = *a+*b; ap1 = *a+1.0e0; mu = 0; d = 1.0e0; if(*n == 1 || *a < 1.0e0) goto S10; if(apb < 1.1e0*ap1) goto S10; mu = fabs(exparg(&K1)); k = exparg(&K2); if(k < mu) mu = k; t = mu; d = exp(-t); S10: bup = brcmp1(&mu,a,b,x,y)/ *a; if(*n == 1 || bup == 0.0e0) return bup; nm1 = *n-1; w = d; /* LET K BE THE INDEX OF THE MAXIMUM TERM */ k = 0; if(*b <= 1.0e0) goto S50; if(*y > 1.e-4) goto S20; k = nm1; goto S30; S20: r = (*b-1.0e0)**x/ *y-*a; if(r < 1.0e0) goto S50; k = t = nm1; if(r < t) k = r; S30: /* ADD THE INCREASING TERMS OF THE SERIES */ for(i=1; i<=k; i++) { l = i-1; d = (apb+l)/(ap1+l)**x*d; w += d; } if(k == nm1) goto S70; S50: /* ADD THE REMAINING TERMS OF THE SERIES */ kp1 = k+1; for(i=kp1; i<=nm1; i++) { l = i-1; d = (apb+l)/(ap1+l)**x*d; w += d; if(d <= *eps*w) goto S70; } S70: /* TERMINATE THE PROCEDURE */ bup *= w; return bup; } /* END */ /***=====================================================================***/ static void cdfbet(int *which,double *p,double *q,double *x,double *y, double *a,double *b,int *status,double *bound) /********************************************************************** void cdfbet(int *which,double *p,double *q,double *x,double *y, double *a,double *b,int *status,double *bound) Cumulative Distribution Function BETa Distribution Function Calculates any one parameter of the beta distribution given values for the others. Arguments WHICH --> Integer indicating which of the next four argument values is to be calculated from the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from X,Y,A and B iwhich = 2 : Calculate X and Y from P,Q,A and B iwhich = 3 : Calculate A from P,Q,X,Y and B iwhich = 4 : Calculate B from P,Q,X,Y and A P <--> The integral from 0 to X of the chi-square distribution. Input range: [0, 1]. Q <--> 1-P. Input range: [0, 1]. P + Q = 1.0. X <--> Upper limit of integration of beta density. Input range: [0,1]. Search range: [0,1] Y <--> 1-X. Input range: [0,1]. Search range: [0,1] X + Y = 1.0. A <--> The first parameter of the beta density. Input range: (0, +infinity). Search range: [1D-300,1D300] B <--> The second parameter of the beta density. Input range: (0, +infinity). Search range: [1D-300,1D300] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 4 if X + Y .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Cumulative distribution function (P) is calculated directly by code associated with the following reference. DiDinato, A. R. and Morris, A. H. Algorithm 708: Significant Digit Computation of the Incomplete Beta Function Ratios. ACM Trans. Math. Softw. 18 (1993), 360-373. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. Note The beta density is proportional to t^(A-1) * (1-t)^(B-1) **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define inf 1.0e300 #define one 1.0e0 static int K1 = 1; static double K2 = 0.0e0; static double K3 = 1.0e0; static double K8 = 0.5e0; static double K9 = 5.0e0; static double fx,xhi,xlo,cum,ccum,xy,pq; static unsigned long qhi,qleft,qporq; static double T4,T5,T6,T7,T10,T11,T12,T13,T14,T15; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q < 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q < 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S150; /* X */ if(!(*x < 0.0e0 || *x > 1.0e0)) goto S140; if(!(*x < 0.0e0)) goto S120; *bound = 0.0e0; goto S130; S120: *bound = 1.0e0; S130: *status = -4; return; S150: S140: if(*which == 2) goto S190; /* Y */ if(!(*y < 0.0e0 || *y > 1.0e0)) goto S180; if(!(*y < 0.0e0)) goto S160; *bound = 0.0e0; goto S170; S160: *bound = 1.0e0; S170: *status = -5; return; S190: S180: if(*which == 3) goto S210; /* A */ if(!(*a <= 0.0e0)) goto S200; *bound = 0.0e0; *status = -6; return; S210: S200: if(*which == 4) goto S230; /* B */ if(!(*b <= 0.0e0)) goto S220; *bound = 0.0e0; *status = -7; return; S230: S220: if(*which == 1) goto S270; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S260; if(!(pq < 0.0e0)) goto S240; *bound = 0.0e0; goto S250; S240: *bound = 1.0e0; S250: *status = 3; return; S270: S260: if(*which == 2) goto S310; /* X + Y */ xy = *x+*y; if(!(fabs(xy-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S300; if(!(xy < 0.0e0)) goto S280; *bound = 0.0e0; goto S290; S280: *bound = 1.0e0; S290: *status = 4; return; S310: S300: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Calculating P and Q */ cumbet(x,y,a,b,p,q); *status = 0; } else if(2 == *which) { /* Calculating X and Y */ T4 = atol; T5 = tol; dstzr(&K2,&K3,&T4,&T5); if(!qporq) goto S340; *status = 0; dzror(status,x,&fx,&xlo,&xhi,&qleft,&qhi); *y = one-*x; S320: if(!(*status == 1)) goto S330; cumbet(x,y,a,b,&cum,&ccum); fx = cum-*p; dzror(status,x,&fx,&xlo,&xhi,&qleft,&qhi); *y = one-*x; goto S320; S330: goto S370; S340: *status = 0; dzror(status,y,&fx,&xlo,&xhi,&qleft,&qhi); *x = one-*y; S350: if(!(*status == 1)) goto S360; cumbet(x,y,a,b,&cum,&ccum); fx = ccum-*q; dzror(status,y,&fx,&xlo,&xhi,&qleft,&qhi); *x = one-*y; goto S350; S370: S360: if(!(*status == -1)) goto S400; if(!qleft) goto S380; *status = 1; *bound = 0.0e0; goto S390; S380: *status = 2; *bound = 1.0e0; S400: S390: ; } else if(3 == *which) { /* Computing A */ *a = 5.0e0; T6 = zero; T7 = inf; T10 = atol; T11 = tol; dstinv(&T6,&T7,&K8,&K8,&K9,&T10,&T11); *status = 0; dinvr(status,a,&fx,&qleft,&qhi); S410: if(!(*status == 1)) goto S440; cumbet(x,y,a,b,&cum,&ccum); if(!qporq) goto S420; fx = cum-*p; goto S430; S420: fx = ccum-*q; S430: dinvr(status,a,&fx,&qleft,&qhi); goto S410; S440: if(!(*status == -1)) goto S470; if(!qleft) goto S450; *status = 1; *bound = zero; goto S460; S450: *status = 2; *bound = inf; S470: S460: ; } else if(4 == *which) { /* Computing B */ *b = 5.0e0; T12 = zero; T13 = inf; T14 = atol; T15 = tol; dstinv(&T12,&T13,&K8,&K8,&K9,&T14,&T15); *status = 0; dinvr(status,b,&fx,&qleft,&qhi); S480: if(!(*status == 1)) goto S510; cumbet(x,y,a,b,&cum,&ccum); if(!qporq) goto S490; fx = cum-*p; goto S500; S490: fx = ccum-*q; S500: dinvr(status,b,&fx,&qleft,&qhi); goto S480; S510: if(!(*status == -1)) goto S540; if(!qleft) goto S520; *status = 1; *bound = zero; goto S530; S520: *status = 2; *bound = inf; S530: ; } S540: return; #undef tol #undef atol #undef zero #undef inf #undef one } /* END */ /***=====================================================================***/ static void cdfbin(int *which,double *p,double *q,double *s,double *xn, double *pr,double *ompr,int *status,double *bound) /********************************************************************** void cdfbin(int *which,double *p,double *q,double *s,double *xn, double *pr,double *ompr,int *status,double *bound) Cumulative Distribution Function BINomial distribution Function Calculates any one parameter of the binomial distribution given values for the others. Arguments WHICH --> Integer indicating which of the next four argument values is to be calculated from the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from S,XN,PR and OMPR iwhich = 2 : Calculate S from P,Q,XN,PR and OMPR iwhich = 3 : Calculate XN from P,Q,S,PR and OMPR iwhich = 4 : Calculate PR and OMPR from P,Q,S and XN P <--> The cumulation from 0 to S of the binomial distribution. (Probablility of S or fewer successes in XN trials each with probability of success PR.) Input range: [0,1]. Q <--> 1-P. Input range: [0, 1]. P + Q = 1.0. S <--> The number of successes observed. Input range: [0, XN] Search range: [0, XN] XN <--> The number of binomial trials. Input range: (0, +infinity). Search range: [1E-300, 1E300] PR <--> The probability of success in each binomial trial. Input range: [0,1]. Search range: [0,1] OMPR <--> 1-PR Input range: [0,1]. Search range: [0,1] PR + OMPR = 1.0 STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 4 if PR + OMPR .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.5.24 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the binomial distribution to the cumulative incomplete beta distribution. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. **********************************************************************/ { #define atol (1.0e-50) #define tol (1.0e-8) #define zero (1.0e-300) #define inf 1.0e300 #define one 1.0e0 static int K1 = 1; static double K2 = 0.0e0; static double K3 = 0.5e0; static double K4 = 5.0e0; static double K11 = 1.0e0; static double fx,xhi,xlo,cum,ccum,pq,prompr; static unsigned long qhi,qleft,qporq; static double T5,T6,T7,T8,T9,T10,T12,T13; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 && *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q < 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q < 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 3) goto S130; /* XN */ if(!(*xn <= 0.0e0)) goto S120; *bound = 0.0e0; *status = -5; return; S130: S120: if(*which == 2) goto S170; /* S */ if(!(*s < 0.0e0 || *which != 3 && *s > *xn)) goto S160; if(!(*s < 0.0e0)) goto S140; *bound = 0.0e0; goto S150; S140: *bound = *xn; S150: *status = -4; return; S170: S160: if(*which == 4) goto S210; /* PR */ if(!(*pr < 0.0e0 || *pr > 1.0e0)) goto S200; if(!(*pr < 0.0e0)) goto S180; *bound = 0.0e0; goto S190; S180: *bound = 1.0e0; S190: *status = -6; return; S210: S200: if(*which == 4) goto S250; /* OMPR */ if(!(*ompr < 0.0e0 || *ompr > 1.0e0)) goto S240; if(!(*ompr < 0.0e0)) goto S220; *bound = 0.0e0; goto S230; S220: *bound = 1.0e0; S230: *status = -7; return; S250: S240: if(*which == 1) goto S290; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S280; if(!(pq < 0.0e0)) goto S260; *bound = 0.0e0; goto S270; S260: *bound = 1.0e0; S270: *status = 3; return; S290: S280: if(*which == 4) goto S330; /* PR + OMPR */ prompr = *pr+*ompr; if(!(fabs(prompr-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S320; if(!(prompr < 0.0e0)) goto S300; *bound = 0.0e0; goto S310; S300: *bound = 1.0e0; S310: *status = 4; return; S330: S320: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ cumbin(s,xn,pr,ompr,p,q); *status = 0; } else if(2 == *which) { /* Calculating S */ *s = 5.0e0; T5 = atol; T6 = tol; dstinv(&K2,xn,&K3,&K3,&K4,&T5,&T6); *status = 0; dinvr(status,s,&fx,&qleft,&qhi); S340: if(!(*status == 1)) goto S370; cumbin(s,xn,pr,ompr,&cum,&ccum); if(!qporq) goto S350; fx = cum-*p; goto S360; S350: fx = ccum-*q; S360: dinvr(status,s,&fx,&qleft,&qhi); goto S340; S370: if(!(*status == -1)) goto S400; if(!qleft) goto S380; *status = 1; *bound = 0.0e0; goto S390; S380: *status = 2; *bound = *xn; S400: S390: ; } else if(3 == *which) { /* Calculating XN */ *xn = 5.0e0; T7 = zero; T8 = inf; T9 = atol; T10 = tol; dstinv(&T7,&T8,&K3,&K3,&K4,&T9,&T10); *status = 0; dinvr(status,xn,&fx,&qleft,&qhi); S410: if(!(*status == 1)) goto S440; cumbin(s,xn,pr,ompr,&cum,&ccum); if(!qporq) goto S420; fx = cum-*p; goto S430; S420: fx = ccum-*q; S430: dinvr(status,xn,&fx,&qleft,&qhi); goto S410; S440: if(!(*status == -1)) goto S470; if(!qleft) goto S450; *status = 1; *bound = zero; goto S460; S450: *status = 2; *bound = inf; S470: S460: ; } else if(4 == *which) { /* Calculating PR and OMPR */ T12 = atol; T13 = tol; dstzr(&K2,&K11,&T12,&T13); if(!qporq) goto S500; *status = 0; dzror(status,pr,&fx,&xlo,&xhi,&qleft,&qhi); *ompr = one-*pr; S480: if(!(*status == 1)) goto S490; cumbin(s,xn,pr,ompr,&cum,&ccum); fx = cum-*p; dzror(status,pr,&fx,&xlo,&xhi,&qleft,&qhi); *ompr = one-*pr; goto S480; S490: goto S530; S500: *status = 0; dzror(status,ompr,&fx,&xlo,&xhi,&qleft,&qhi); *pr = one-*ompr; S510: if(!(*status == 1)) goto S520; cumbin(s,xn,pr,ompr,&cum,&ccum); fx = ccum-*q; dzror(status,ompr,&fx,&xlo,&xhi,&qleft,&qhi); *pr = one-*ompr; goto S510; S530: S520: if(!(*status == -1)) goto S560; if(!qleft) goto S540; *status = 1; *bound = 0.0e0; goto S550; S540: *status = 2; *bound = 1.0e0; S550: ; } S560: return; #undef atol #undef tol #undef zero #undef inf #undef one } /* END */ /***=====================================================================***/ static void cdfchi(int *which,double *p,double *q,double *x,double *df, int *status,double *bound) /********************************************************************** void cdfchi(int *which,double *p,double *q,double *x,double *df, int *status,double *bound) Cumulative Distribution Function CHI-Square distribution Function Calculates any one parameter of the chi-square distribution given values for the others. Arguments WHICH --> Integer indicating which of the next three argument values is to be calculated from the others. Legal range: 1..3 iwhich = 1 : Calculate P and Q from X and DF iwhich = 2 : Calculate X from P,Q and DF iwhich = 3 : Calculate DF from P,Q and X P <--> The integral from 0 to X of the chi-square distribution. Input range: [0, 1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. X <--> Upper limit of integration of the non-central chi-square distribution. Input range: [0, +infinity). Search range: [0,1E300] DF <--> Degrees of freedom of the chi-square distribution. Input range: (0, +infinity). Search range: [ 1E-300, 1E300] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 10 indicates error returned from cumgam. See references in cdfgam BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.4.19 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the chisqure distribution to the incomplete distribution. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define inf 1.0e300 static int K1 = 1; static double K2 = 0.0e0; static double K4 = 0.5e0; static double K5 = 5.0e0; static double fx,cum,ccum,pq,porq; static unsigned long qhi,qleft,qporq; static double T3,T6,T7,T8,T9,T10,T11; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 3)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 3.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S130; /* X */ if(!(*x < 0.0e0)) goto S120; *bound = 0.0e0; *status = -4; return; S130: S120: if(*which == 3) goto S150; /* DF */ if(!(*df <= 0.0e0)) goto S140; *bound = 0.0e0; *status = -5; return; S150: S140: if(*which == 1) goto S190; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S180; if(!(pq < 0.0e0)) goto S160; *bound = 0.0e0; goto S170; S160: *bound = 1.0e0; S170: *status = 3; return; S190: S180: if(*which == 1) goto S220; /* Select the minimum of P or Q */ qporq = *p <= *q; if(!qporq) goto S200; porq = *p; goto S210; S200: porq = *q; S220: S210: /* Calculate ANSWERS */ if(1 == *which) { /* Calculating P and Q */ *status = 0; cumchi(x,df,p,q); if(porq > 1.5e0) { *status = 10; return; } } else if(2 == *which) { /* Calculating X */ *x = 5.0e0; T3 = inf; T6 = atol; T7 = tol; dstinv(&K2,&T3,&K4,&K4,&K5,&T6,&T7); *status = 0; dinvr(status,x,&fx,&qleft,&qhi); S230: if(!(*status == 1)) goto S270; cumchi(x,df,&cum,&ccum); if(!qporq) goto S240; fx = cum-*p; goto S250; S240: fx = ccum-*q; S250: if(!(fx+porq > 1.5e0)) goto S260; *status = 10; return; S260: dinvr(status,x,&fx,&qleft,&qhi); goto S230; S270: if(!(*status == -1)) goto S300; if(!qleft) goto S280; *status = 1; *bound = 0.0e0; goto S290; S280: *status = 2; *bound = inf; S300: S290: ; } else if(3 == *which) { /* Calculating DF */ *df = 5.0e0; T8 = zero; T9 = inf; T10 = atol; T11 = tol; dstinv(&T8,&T9,&K4,&K4,&K5,&T10,&T11); *status = 0; dinvr(status,df,&fx,&qleft,&qhi); S310: if(!(*status == 1)) goto S350; cumchi(x,df,&cum,&ccum); if(!qporq) goto S320; fx = cum-*p; goto S330; S320: fx = ccum-*q; S330: if(!(fx+porq > 1.5e0)) goto S340; *status = 10; return; S340: dinvr(status,df,&fx,&qleft,&qhi); goto S310; S350: if(!(*status == -1)) goto S380; if(!qleft) goto S360; *status = 1; *bound = zero; goto S370; S360: *status = 2; *bound = inf; S370: ; } S380: return; #undef tol #undef atol #undef zero #undef inf } /* END */ /***=====================================================================***/ static void cdfchn(int *which,double *p,double *q,double *x,double *df, double *pnonc,int *status,double *bound) /********************************************************************** void cdfchn(int *which,double *p,double *q,double *x,double *df, double *pnonc,int *status,double *bound) Cumulative Distribution Function Non-central Chi-Square Function Calculates any one parameter of the non-central chi-square distribution given values for the others. Arguments WHICH --> Integer indicating which of the next three argument values is to be calculated from the others. Input range: 1..4 iwhich = 1 : Calculate P and Q from X and DF iwhich = 2 : Calculate X from P,DF and PNONC iwhich = 3 : Calculate DF from P,X and PNONC iwhich = 3 : Calculate PNONC from P,X and DF P <--> The integral from 0 to X of the non-central chi-square distribution. Input range: [0, 1-1E-16). Q <--> 1-P. Q is not used by this subroutine and is only included for similarity with other cdf* routines. X <--> Upper limit of integration of the non-central chi-square distribution. Input range: [0, +infinity). Search range: [0,1E300] DF <--> Degrees of freedom of the non-central chi-square distribution. Input range: (0, +infinity). Search range: [ 1E-300, 1E300] PNONC <--> Non-centrality parameter of the non-central chi-square distribution. Input range: [0, +infinity). Search range: [0,1E4] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.4.25 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to compute the cumulative distribution function. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. WARNING The computation time required for this routine is proportional to the noncentrality parameter (PNONC). Very large values of this parameter can consume immense computer resources. This is why the search range is bounded by 10,000. **********************************************************************/ { #define tent4 1.0e4 #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define one (1.0e0-1.0e-16) #define inf 1.0e300 static double K1 = 0.0e0; static double K3 = 0.5e0; static double K4 = 5.0e0; static double fx,cum,ccum; static unsigned long qhi,qleft; static double T2,T5,T6,T7,T8,T9,T10,T11,T12,T13; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > one)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = one; S50: *status = -2; return; S70: S60: if(*which == 2) goto S90; /* X */ if(!(*x < 0.0e0)) goto S80; *bound = 0.0e0; *status = -4; return; S90: S80: if(*which == 3) goto S110; /* DF */ if(!(*df <= 0.0e0)) goto S100; *bound = 0.0e0; *status = -5; return; S110: S100: if(*which == 4) goto S130; /* PNONC */ if(!(*pnonc < 0.0e0)) goto S120; *bound = 0.0e0; *status = -6; return; S130: S120: /* Calculate ANSWERS */ if(1 == *which) { /* Calculating P and Q */ cumchn(x,df,pnonc,p,q); *status = 0; } else if(2 == *which) { /* Calculating X */ *x = 5.0e0; T2 = inf; T5 = atol; T6 = tol; dstinv(&K1,&T2,&K3,&K3,&K4,&T5,&T6); *status = 0; dinvr(status,x,&fx,&qleft,&qhi); S140: if(!(*status == 1)) goto S150; cumchn(x,df,pnonc,&cum,&ccum); fx = cum-*p; dinvr(status,x,&fx,&qleft,&qhi); goto S140; S150: if(!(*status == -1)) goto S180; if(!qleft) goto S160; *status = 1; *bound = 0.0e0; goto S170; S160: *status = 2; *bound = inf; S180: S170: ; } else if(3 == *which) { /* Calculating DF */ *df = 5.0e0; T7 = zero; T8 = inf; T9 = atol; T10 = tol; dstinv(&T7,&T8,&K3,&K3,&K4,&T9,&T10); *status = 0; dinvr(status,df,&fx,&qleft,&qhi); S190: if(!(*status == 1)) goto S200; cumchn(x,df,pnonc,&cum,&ccum); fx = cum-*p; dinvr(status,df,&fx,&qleft,&qhi); goto S190; S200: if(!(*status == -1)) goto S230; if(!qleft) goto S210; *status = 1; *bound = zero; goto S220; S210: *status = 2; *bound = inf; S230: S220: ; } else if(4 == *which) { /* Calculating PNONC */ *pnonc = 5.0e0; T11 = tent4; T12 = atol; T13 = tol; dstinv(&K1,&T11,&K3,&K3,&K4,&T12,&T13); *status = 0; dinvr(status,pnonc,&fx,&qleft,&qhi); S240: if(!(*status == 1)) goto S250; cumchn(x,df,pnonc,&cum,&ccum); fx = cum-*p; dinvr(status,pnonc,&fx,&qleft,&qhi); goto S240; S250: if(!(*status == -1)) goto S280; if(!qleft) goto S260; *status = 1; *bound = zero; goto S270; S260: *status = 2; *bound = tent4; S270: ; } S280: return; #undef tent4 #undef tol #undef atol #undef zero #undef one #undef inf } /* END */ /***=====================================================================***/ static void cdff(int *which,double *p,double *q,double *f,double *dfn, double *dfd,int *status,double *bound) /********************************************************************** void cdff(int *which,double *p,double *q,double *f,double *dfn, double *dfd,int *status,double *bound) Cumulative Distribution Function F distribution Function Calculates any one parameter of the F distribution given values for the others. Arguments WHICH --> Integer indicating which of the next four argument values is to be calculated from the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from F,DFN and DFD iwhich = 2 : Calculate F from P,Q,DFN and DFD iwhich = 3 : Calculate DFN from P,Q,F and DFD iwhich = 4 : Calculate DFD from P,Q,F and DFN P <--> The integral from 0 to F of the f-density. Input range: [0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. F <--> Upper limit of integration of the f-density. Input range: [0, +infinity). Search range: [0,1E300] DFN < --> Degrees of freedom of the numerator sum of squares. Input range: (0, +infinity). Search range: [ 1E-300, 1E300] DFD < --> Degrees of freedom of the denominator sum of squares. Input range: (0, +infinity). Search range: [ 1E-300, 1E300] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.6.2 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the computation of the cumulative distribution function for the F variate to that of an incomplete beta. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. WARNING The value of the cumulative F distribution is not necessarily monotone in either degrees of freedom. There thus may be two values that provide a given CDF value. This routine assumes monotonicity and will find an arbitrary one of the two values. **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define inf 1.0e300 static int K1 = 1; static double K2 = 0.0e0; static double K4 = 0.5e0; static double K5 = 5.0e0; static double pq,fx,cum,ccum; static unsigned long qhi,qleft,qporq; static double T3,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S130; /* F */ if(!(*f < 0.0e0)) goto S120; *bound = 0.0e0; *status = -4; return; S130: S120: if(*which == 3) goto S150; /* DFN */ if(!(*dfn <= 0.0e0)) goto S140; *bound = 0.0e0; *status = -5; return; S150: S140: if(*which == 4) goto S170; /* DFD */ if(!(*dfd <= 0.0e0)) goto S160; *bound = 0.0e0; *status = -6; return; S170: S160: if(*which == 1) goto S210; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S200; if(!(pq < 0.0e0)) goto S180; *bound = 0.0e0; goto S190; S180: *bound = 1.0e0; S190: *status = 3; return; S210: S200: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ cumf(f,dfn,dfd,p,q); *status = 0; } else if(2 == *which) { /* Calculating F */ *f = 5.0e0; T3 = inf; T6 = atol; T7 = tol; dstinv(&K2,&T3,&K4,&K4,&K5,&T6,&T7); *status = 0; dinvr(status,f,&fx,&qleft,&qhi); S220: if(!(*status == 1)) goto S250; cumf(f,dfn,dfd,&cum,&ccum); if(!qporq) goto S230; fx = cum-*p; goto S240; S230: fx = ccum-*q; S240: dinvr(status,f,&fx,&qleft,&qhi); goto S220; S250: if(!(*status == -1)) goto S280; if(!qleft) goto S260; *status = 1; *bound = 0.0e0; goto S270; S260: *status = 2; *bound = inf; S280: S270: ; } else if(3 == *which) { /* Calculating DFN */ *dfn = 5.0e0; T8 = zero; T9 = inf; T10 = atol; T11 = tol; dstinv(&T8,&T9,&K4,&K4,&K5,&T10,&T11); *status = 0; dinvr(status,dfn,&fx,&qleft,&qhi); S290: if(!(*status == 1)) goto S320; cumf(f,dfn,dfd,&cum,&ccum); if(!qporq) goto S300; fx = cum-*p; goto S310; S300: fx = ccum-*q; S310: dinvr(status,dfn,&fx,&qleft,&qhi); goto S290; S320: if(!(*status == -1)) goto S350; if(!qleft) goto S330; *status = 1; *bound = zero; goto S340; S330: *status = 2; *bound = inf; S350: S340: ; } else if(4 == *which) { /* Calculating DFD */ *dfd = 5.0e0; T12 = zero; T13 = inf; T14 = atol; T15 = tol; dstinv(&T12,&T13,&K4,&K4,&K5,&T14,&T15); *status = 0; dinvr(status,dfd,&fx,&qleft,&qhi); S360: if(!(*status == 1)) goto S390; cumf(f,dfn,dfd,&cum,&ccum); if(!qporq) goto S370; fx = cum-*p; goto S380; S370: fx = ccum-*q; S380: dinvr(status,dfd,&fx,&qleft,&qhi); goto S360; S390: if(!(*status == -1)) goto S420; if(!qleft) goto S400; *status = 1; *bound = zero; goto S410; S400: *status = 2; *bound = inf; S410: ; } S420: return; #undef tol #undef atol #undef zero #undef inf } /* END */ /***=====================================================================***/ static void cdffnc(int *which,double *p,double *q,double *f,double *dfn, double *dfd,double *phonc,int *status,double *bound) /********************************************************************** void cdffnc(int *which,double *p,double *q,double *f,double *dfn, double *dfd,double *phonc,int *status,double *bound) Cumulative Distribution Function Non-central F distribution Function Calculates any one parameter of the Non-central F distribution given values for the others. Arguments WHICH --> Integer indicating which of the next five argument values is to be calculated from the others. Legal range: 1..5 iwhich = 1 : Calculate P and Q from F,DFN,DFD and PNONC iwhich = 2 : Calculate F from P,Q,DFN,DFD and PNONC iwhich = 3 : Calculate DFN from P,Q,F,DFD and PNONC iwhich = 4 : Calculate DFD from P,Q,F,DFN and PNONC iwhich = 5 : Calculate PNONC from P,Q,F,DFN and DFD P <--> The integral from 0 to F of the non-central f-density. Input range: [0,1-1E-16). Q <--> 1-P. Q is not used by this subroutine and is only included for similarity with other cdf* routines. F <--> Upper limit of integration of the non-central f-density. Input range: [0, +infinity). Search range: [0,1E300] DFN < --> Degrees of freedom of the numerator sum of squares. Input range: (0, +infinity). Search range: [ 1E-300, 1E300] DFD < --> Degrees of freedom of the denominator sum of squares. Must be in range: (0, +infinity). Input range: (0, +infinity). Search range: [ 1E-300, 1E300] PNONC <-> The non-centrality parameter Input range: [0,infinity) Search range: [0,1E4] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.6.20 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to compute the cumulative distribution function. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. WARNING The computation time required for this routine is proportional to the noncentrality parameter (PNONC). Very large values of this parameter can consume immense computer resources. This is why the search range is bounded by 10,000. WARNING The value of the cumulative noncentral F distribution is not necessarily monotone in either degrees of freedom. There thus may be two values that provide a given CDF value. This routine assumes monotonicity and will find an arbitrary one of the two values. **********************************************************************/ { #define tent4 1.0e4 #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define one (1.0e0-1.0e-16) #define inf 1.0e300 static double K1 = 0.0e0; static double K3 = 0.5e0; static double K4 = 5.0e0; static double fx,cum,ccum; static unsigned long qhi,qleft; static double T2,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 5)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 5.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > one)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = one; S50: *status = -2; return; S70: S60: if(*which == 2) goto S90; /* F */ if(!(*f < 0.0e0)) goto S80; *bound = 0.0e0; *status = -4; return; S90: S80: if(*which == 3) goto S110; /* DFN */ if(!(*dfn <= 0.0e0)) goto S100; *bound = 0.0e0; *status = -5; return; S110: S100: if(*which == 4) goto S130; /* DFD */ if(!(*dfd <= 0.0e0)) goto S120; *bound = 0.0e0; *status = -6; return; S130: S120: if(*which == 5) goto S150; /* PHONC */ if(!(*phonc < 0.0e0)) goto S140; *bound = 0.0e0; *status = -7; return; S150: S140: /* Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ cumfnc(f,dfn,dfd,phonc,p,q); *status = 0; } else if(2 == *which) { /* Calculating F */ *f = 5.0e0; T2 = inf; T5 = atol; T6 = tol; dstinv(&K1,&T2,&K3,&K3,&K4,&T5,&T6); *status = 0; dinvr(status,f,&fx,&qleft,&qhi); S160: if(!(*status == 1)) goto S170; cumfnc(f,dfn,dfd,phonc,&cum,&ccum); fx = cum-*p; dinvr(status,f,&fx,&qleft,&qhi); goto S160; S170: if(!(*status == -1)) goto S200; if(!qleft) goto S180; *status = 1; *bound = 0.0e0; goto S190; S180: *status = 2; *bound = inf; S200: S190: ; } else if(3 == *which) { /* Calculating DFN */ *dfn = 5.0e0; T7 = zero; T8 = inf; T9 = atol; T10 = tol; dstinv(&T7,&T8,&K3,&K3,&K4,&T9,&T10); *status = 0; dinvr(status,dfn,&fx,&qleft,&qhi); S210: if(!(*status == 1)) goto S220; cumfnc(f,dfn,dfd,phonc,&cum,&ccum); fx = cum-*p; dinvr(status,dfn,&fx,&qleft,&qhi); goto S210; S220: if(!(*status == -1)) goto S250; if(!qleft) goto S230; *status = 1; *bound = zero; goto S240; S230: *status = 2; *bound = inf; S250: S240: ; } else if(4 == *which) { /* Calculating DFD */ *dfd = 5.0e0; T11 = zero; T12 = inf; T13 = atol; T14 = tol; dstinv(&T11,&T12,&K3,&K3,&K4,&T13,&T14); *status = 0; dinvr(status,dfd,&fx,&qleft,&qhi); S260: if(!(*status == 1)) goto S270; cumfnc(f,dfn,dfd,phonc,&cum,&ccum); fx = cum-*p; dinvr(status,dfd,&fx,&qleft,&qhi); goto S260; S270: if(!(*status == -1)) goto S300; if(!qleft) goto S280; *status = 1; *bound = zero; goto S290; S280: *status = 2; *bound = inf; S300: S290: ; } else if(5 == *which) { /* Calculating PHONC */ *phonc = 5.0e0; T15 = tent4; T16 = atol; T17 = tol; dstinv(&K1,&T15,&K3,&K3,&K4,&T16,&T17); *status = 0; dinvr(status,phonc,&fx,&qleft,&qhi); S310: if(!(*status == 1)) goto S320; cumfnc(f,dfn,dfd,phonc,&cum,&ccum); fx = cum-*p; dinvr(status,phonc,&fx,&qleft,&qhi); goto S310; S320: if(!(*status == -1)) goto S350; if(!qleft) goto S330; *status = 1; *bound = 0.0e0; goto S340; S330: *status = 2; *bound = tent4; S340: ; } S350: return; #undef tent4 #undef tol #undef atol #undef zero #undef one #undef inf } /* END */ /***=====================================================================***/ static void cdfgam(int *which,double *p,double *q,double *x,double *shape, double *scale,int *status,double *bound) /********************************************************************** void cdfgam(int *which,double *p,double *q,double *x,double *shape, double *scale,int *status,double *bound) Cumulative Distribution Function GAMma Distribution Function Calculates any one parameter of the gamma distribution given values for the others. Arguments WHICH --> Integer indicating which of the next four argument values is to be calculated from the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from X,SHAPE and SCALE iwhich = 2 : Calculate X from P,Q,SHAPE and SCALE iwhich = 3 : Calculate SHAPE from P,Q,X and SCALE iwhich = 4 : Calculate SCALE from P,Q,X and SHAPE P <--> The integral from 0 to X of the gamma density. Input range: [0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. X <--> The upper limit of integration of the gamma density. Input range: [0, +infinity). Search range: [0,1E300] SHAPE <--> The shape parameter of the gamma density. Input range: (0, +infinity). Search range: [1E-300,1E300] SCALE <--> The scale parameter of the gamma density. Input range: (0, +infinity). Search range: (1E-300,1E300] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 10 if the gamma or inverse gamma routine cannot compute the answer. Usually happens only for X and SHAPE very large (gt 1E10 or more) BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Cumulative distribution function (P) is calculated directly by the code associated with: DiDinato, A. R. and Morris, A. H. Computation of the incomplete gamma function ratios and their inverse. ACM Trans. Math. Softw. 12 (1986), 377-393. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. Note The gamma density is proportional to T**(SHAPE - 1) * EXP(- SCALE * T) **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define inf 1.0e300 static int K1 = 1; static double K5 = 0.5e0; static double K6 = 5.0e0; static double xx,fx,xscale,cum,ccum,pq,porq; static int ierr; static unsigned long qhi,qleft,qporq; static double T2,T3,T4,T7,T8,T9; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S130; /* X */ if(!(*x < 0.0e0)) goto S120; *bound = 0.0e0; *status = -4; return; S130: S120: if(*which == 3) goto S150; /* SHAPE */ if(!(*shape <= 0.0e0)) goto S140; *bound = 0.0e0; *status = -5; return; S150: S140: if(*which == 4) goto S170; /* SCALE */ if(!(*scale <= 0.0e0)) goto S160; *bound = 0.0e0; *status = -6; return; S170: S160: if(*which == 1) goto S210; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S200; if(!(pq < 0.0e0)) goto S180; *bound = 0.0e0; goto S190; S180: *bound = 1.0e0; S190: *status = 3; return; S210: S200: if(*which == 1) goto S240; /* Select the minimum of P or Q */ qporq = *p <= *q; if(!qporq) goto S220; porq = *p; goto S230; S220: porq = *q; S240: S230: /* Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ *status = 0; xscale = *x**scale; cumgam(&xscale,shape,p,q); if(porq > 1.5e0) *status = 10; } else if(2 == *which) { /* Computing X */ T2 = -1.0e0; gaminv(shape,&xx,&T2,p,q,&ierr); if(ierr < 0.0e0) { *status = 10; return; } else { *x = xx/ *scale; *status = 0; } } else if(3 == *which) { /* Computing SHAPE */ *shape = 5.0e0; xscale = *x**scale; T3 = zero; T4 = inf; T7 = atol; T8 = tol; dstinv(&T3,&T4,&K5,&K5,&K6,&T7,&T8); *status = 0; dinvr(status,shape,&fx,&qleft,&qhi); S250: if(!(*status == 1)) goto S290; cumgam(&xscale,shape,&cum,&ccum); if(!qporq) goto S260; fx = cum-*p; goto S270; S260: fx = ccum-*q; S270: if(!(qporq && cum > 1.5e0 || !qporq && ccum > 1.5e0)) goto S280; *status = 10; return; S280: dinvr(status,shape,&fx,&qleft,&qhi); goto S250; S290: if(!(*status == -1)) goto S320; if(!qleft) goto S300; *status = 1; *bound = zero; goto S310; S300: *status = 2; *bound = inf; S320: S310: ; } else if(4 == *which) { /* Computing SCALE */ T9 = -1.0e0; gaminv(shape,&xx,&T9,p,q,&ierr); if(ierr < 0.0e0) { *status = 10; return; } else { *scale = xx/ *x; *status = 0; } } return; #undef tol #undef atol #undef zero #undef inf } /* END */ /***=====================================================================***/ static void cdfnbn(int *which,double *p,double *q,double *s,double *xn, double *pr,double *ompr,int *status,double *bound) /********************************************************************** void cdfnbn(int *which,double *p,double *q,double *s,double *xn, double *pr,double *ompr,int *status,double *bound) Cumulative Distribution Function Negative BiNomial distribution Function Calculates any one parameter of the negative binomial distribution given values for the others. The cumulative negative binomial distribution returns the probability that there will be F or fewer failures before the XNth success in binomial trials each of which has probability of success PR. The individual term of the negative binomial is the probability of S failures before XN successes and is Choose( S, XN+S-1 ) * PR^(XN) * (1-PR)^S Arguments WHICH --> Integer indicating which of the next four argument values is to be calculated from the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from S,XN,PR and OMPR iwhich = 2 : Calculate S from P,Q,XN,PR and OMPR iwhich = 3 : Calculate XN from P,Q,S,PR and OMPR iwhich = 4 : Calculate PR and OMPR from P,Q,S and XN P <--> The cumulation from 0 to S of the negative binomial distribution. Input range: [0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. S <--> The upper limit of cumulation of the binomial distribution. There are F or fewer failures before the XNth success. Input range: [0, +infinity). Search range: [0, 1E300] XN <--> The number of successes. Input range: [0, +infinity). Search range: [0, 1E300] PR <--> The probability of success in each binomial trial. Input range: [0,1]. Search range: [0,1]. OMPR <--> 1-PR Input range: [0,1]. Search range: [0,1] PR + OMPR = 1.0 STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 4 if PR + OMPR .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.5.26 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce calculation of the cumulative distribution function to that of an incomplete beta. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define inf 1.0e300 #define one 1.0e0 static int K1 = 1; static double K2 = 0.0e0; static double K4 = 0.5e0; static double K5 = 5.0e0; static double K11 = 1.0e0; static double fx,xhi,xlo,pq,prompr,cum,ccum; static unsigned long qhi,qleft,qporq; static double T3,T6,T7,T8,T9,T10,T12,T13; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S130; /* S */ if(!(*s < 0.0e0)) goto S120; *bound = 0.0e0; *status = -4; return; S130: S120: if(*which == 3) goto S150; /* XN */ if(!(*xn < 0.0e0)) goto S140; *bound = 0.0e0; *status = -5; return; S150: S140: if(*which == 4) goto S190; /* PR */ if(!(*pr < 0.0e0 || *pr > 1.0e0)) goto S180; if(!(*pr < 0.0e0)) goto S160; *bound = 0.0e0; goto S170; S160: *bound = 1.0e0; S170: *status = -6; return; S190: S180: if(*which == 4) goto S230; /* OMPR */ if(!(*ompr < 0.0e0 || *ompr > 1.0e0)) goto S220; if(!(*ompr < 0.0e0)) goto S200; *bound = 0.0e0; goto S210; S200: *bound = 1.0e0; S210: *status = -7; return; S230: S220: if(*which == 1) goto S270; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S260; if(!(pq < 0.0e0)) goto S240; *bound = 0.0e0; goto S250; S240: *bound = 1.0e0; S250: *status = 3; return; S270: S260: if(*which == 4) goto S310; /* PR + OMPR */ prompr = *pr+*ompr; if(!(fabs(prompr-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S300; if(!(prompr < 0.0e0)) goto S280; *bound = 0.0e0; goto S290; S280: *bound = 1.0e0; S290: *status = 4; return; S310: S300: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ cumnbn(s,xn,pr,ompr,p,q); *status = 0; } else if(2 == *which) { /* Calculating S */ *s = 5.0e0; T3 = inf; T6 = atol; T7 = tol; dstinv(&K2,&T3,&K4,&K4,&K5,&T6,&T7); *status = 0; dinvr(status,s,&fx,&qleft,&qhi); S320: if(!(*status == 1)) goto S350; cumnbn(s,xn,pr,ompr,&cum,&ccum); if(!qporq) goto S330; fx = cum-*p; goto S340; S330: fx = ccum-*q; S340: dinvr(status,s,&fx,&qleft,&qhi); goto S320; S350: if(!(*status == -1)) goto S380; if(!qleft) goto S360; *status = 1; *bound = 0.0e0; goto S370; S360: *status = 2; *bound = inf; S380: S370: ; } else if(3 == *which) { /* Calculating XN */ *xn = 5.0e0; T8 = inf; T9 = atol; T10 = tol; dstinv(&K2,&T8,&K4,&K4,&K5,&T9,&T10); *status = 0; dinvr(status,xn,&fx,&qleft,&qhi); S390: if(!(*status == 1)) goto S420; cumnbn(s,xn,pr,ompr,&cum,&ccum); if(!qporq) goto S400; fx = cum-*p; goto S410; S400: fx = ccum-*q; S410: dinvr(status,xn,&fx,&qleft,&qhi); goto S390; S420: if(!(*status == -1)) goto S450; if(!qleft) goto S430; *status = 1; *bound = 0.0e0; goto S440; S430: *status = 2; *bound = inf; S450: S440: ; } else if(4 == *which) { /* Calculating PR and OMPR */ T12 = atol; T13 = tol; dstzr(&K2,&K11,&T12,&T13); if(!qporq) goto S480; *status = 0; dzror(status,pr,&fx,&xlo,&xhi,&qleft,&qhi); *ompr = one-*pr; S460: if(!(*status == 1)) goto S470; cumnbn(s,xn,pr,ompr,&cum,&ccum); fx = cum-*p; dzror(status,pr,&fx,&xlo,&xhi,&qleft,&qhi); *ompr = one-*pr; goto S460; S470: goto S510; S480: *status = 0; dzror(status,ompr,&fx,&xlo,&xhi,&qleft,&qhi); *pr = one-*ompr; S490: if(!(*status == 1)) goto S500; cumnbn(s,xn,pr,ompr,&cum,&ccum); fx = ccum-*q; dzror(status,ompr,&fx,&xlo,&xhi,&qleft,&qhi); *pr = one-*ompr; goto S490; S510: S500: if(!(*status == -1)) goto S540; if(!qleft) goto S520; *status = 1; *bound = 0.0e0; goto S530; S520: *status = 2; *bound = 1.0e0; S530: ; } S540: return; #undef tol #undef atol #undef inf #undef one } /* END */ /***=====================================================================***/ static void cdfnor(int *which,double *p,double *q,double *x,double *mean, double *sd,int *status,double *bound) /********************************************************************** void cdfnor(int *which,double *p,double *q,double *x,double *mean, double *sd,int *status,double *bound) Cumulative Distribution Function NORmal distribution Function Calculates any one parameter of the normal distribution given values for the others. Arguments WHICH --> Integer indicating which of the next parameter values is to be calculated using values of the others. Legal range: 1..4 iwhich = 1 : Calculate P and Q from X,MEAN and SD iwhich = 2 : Calculate X from P,Q,MEAN and SD iwhich = 3 : Calculate MEAN from P,Q,X and SD iwhich = 4 : Calculate SD from P,Q,X and MEAN P <--> The integral from -infinity to X of the normal density. Input range: (0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. X < --> Upper limit of integration of the normal-density. Input range: ( -infinity, +infinity) MEAN <--> The mean of the normal density. Input range: (-infinity, +infinity) SD <--> Standard Deviation of the normal density. Input range: (0, +infinity). STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method A slightly modified version of ANORM from Cody, W.D. (1993). "ALGORITHM 715: SPECFUN - A Portabel FORTRAN Package of Special Function Routines and Test Drivers" acm Transactions on Mathematical Software. 19, 22-32. is used to calulate the cumulative standard normal distribution. The rational functions from pages 90-95 of Kennedy and Gentle, Statistical Computing, Marcel Dekker, NY, 1980 are used as starting values to Newton's Iterations which compute the inverse standard normal. Therefore no searches are necessary for any parameter. For X < -15, the asymptotic expansion for the normal is used as the starting value in finding the inverse standard normal. This is formula 26.2.12 of Abramowitz and Stegun. Note The normal density is proportional to exp( - 0.5 * (( X - MEAN)/SD)**2) **********************************************************************/ { static int K1 = 1; static double z,pq; /* .. .. Executable Statements .. */ /* Check arguments */ *status = 0; if(!(*which < 1 || *which > 4)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 4.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p <= 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p <= 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 1) goto S150; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S140; if(!(pq < 0.0e0)) goto S120; *bound = 0.0e0; goto S130; S120: *bound = 1.0e0; S130: *status = 3; return; S150: S140: if(*which == 4) goto S170; /* SD */ if(!(*sd <= 0.0e0)) goto S160; *bound = 0.0e0; *status = -6; return; S170: S160: /* Calculate ANSWERS */ if(1 == *which) { /* Computing P */ z = (*x-*mean)/ *sd; cumnor(&z,p,q); } else if(2 == *which) { /* Computing X */ z = dinvnr(p,q); *x = *sd*z+*mean; } else if(3 == *which) { /* Computing the MEAN */ z = dinvnr(p,q); *mean = *x-*sd*z; } else if(4 == *which) { /* Computing SD */ z = dinvnr(p,q); *sd = (*x-*mean)/z; } return; } /* END */ /***=====================================================================***/ static void cdfpoi(int *which,double *p,double *q,double *s,double *xlam, int *status,double *bound) /********************************************************************** void cdfpoi(int *which,double *p,double *q,double *s,double *xlam, int *status,double *bound) Cumulative Distribution Function POIsson distribution Function Calculates any one parameter of the Poisson distribution given values for the others. Arguments WHICH --> Integer indicating which argument value is to be calculated from the others. Legal range: 1..3 iwhich = 1 : Calculate P and Q from S and XLAM iwhich = 2 : Calculate A from P,Q and XLAM iwhich = 3 : Calculate XLAM from P,Q and S P <--> The cumulation from 0 to S of the poisson density. Input range: [0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. S <--> Upper limit of cumulation of the Poisson. Input range: [0, +infinity). Search range: [0,1E300] XLAM <--> Mean of the Poisson distribution. Input range: [0, +infinity). Search range: [0,1E300] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.4.21 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the computation of the cumulative distribution function to that of computing a chi-square, hence an incomplete gamma function. Cumulative distribution function (P) is calculated directly. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define inf 1.0e300 static int K1 = 1; static double K2 = 0.0e0; static double K4 = 0.5e0; static double K5 = 5.0e0; static double fx,cum,ccum,pq; static unsigned long qhi,qleft,qporq; static double T3,T6,T7,T8,T9,T10; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 3)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 3.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p < 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p < 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 2) goto S130; /* S */ if(!(*s < 0.0e0)) goto S120; *bound = 0.0e0; *status = -4; return; S130: S120: if(*which == 3) goto S150; /* XLAM */ if(!(*xlam < 0.0e0)) goto S140; *bound = 0.0e0; *status = -5; return; S150: S140: if(*which == 1) goto S190; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S180; if(!(pq < 0.0e0)) goto S160; *bound = 0.0e0; goto S170; S160: *bound = 1.0e0; S170: *status = 3; return; S190: S180: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Calculating P */ cumpoi(s,xlam,p,q); *status = 0; } else if(2 == *which) { /* Calculating S */ *s = 5.0e0; T3 = inf; T6 = atol; T7 = tol; dstinv(&K2,&T3,&K4,&K4,&K5,&T6,&T7); *status = 0; dinvr(status,s,&fx,&qleft,&qhi); S200: if(!(*status == 1)) goto S230; cumpoi(s,xlam,&cum,&ccum); if(!qporq) goto S210; fx = cum-*p; goto S220; S210: fx = ccum-*q; S220: dinvr(status,s,&fx,&qleft,&qhi); goto S200; S230: if(!(*status == -1)) goto S260; if(!qleft) goto S240; *status = 1; *bound = 0.0e0; goto S250; S240: *status = 2; *bound = inf; S260: S250: ; } else if(3 == *which) { /* Calculating XLAM */ *xlam = 5.0e0; T8 = inf; T9 = atol; T10 = tol; dstinv(&K2,&T8,&K4,&K4,&K5,&T9,&T10); *status = 0; dinvr(status,xlam,&fx,&qleft,&qhi); S270: if(!(*status == 1)) goto S300; cumpoi(s,xlam,&cum,&ccum); if(!qporq) goto S280; fx = cum-*p; goto S290; S280: fx = ccum-*q; S290: dinvr(status,xlam,&fx,&qleft,&qhi); goto S270; S300: if(!(*status == -1)) goto S330; if(!qleft) goto S310; *status = 1; *bound = 0.0e0; goto S320; S310: *status = 2; *bound = inf; S320: ; } S330: return; #undef tol #undef atol #undef inf } /* END */ /***=====================================================================***/ static void cdft(int *which,double *p,double *q,double *t,double *df, int *status,double *bound) /********************************************************************** void cdft(int *which,double *p,double *q,double *t,double *df, int *status,double *bound) Cumulative Distribution Function T distribution Function Calculates any one parameter of the t distribution given values for the others. Arguments WHICH --> Integer indicating which argument values is to be calculated from the others. Legal range: 1..3 iwhich = 1 : Calculate P and Q from T and DF iwhich = 2 : Calculate T from P,Q and DF iwhich = 3 : Calculate DF from P,Q and T P <--> The integral from -infinity to t of the t-density. Input range: (0,1]. Q <--> 1-P. Input range: (0, 1]. P + Q = 1.0. T <--> Upper limit of integration of the t-density. Input range: ( -infinity, +infinity). Search range: [ -1E300, 1E300 ] DF <--> Degrees of freedom of the t-distribution. Input range: (0 , +infinity). Search range: [1e-300, 1E10] STATUS <-- 0 if calculation completed correctly -I if input parameter number I is out of range 1 if answer appears to be lower than lowest search bound 2 if answer appears to be higher than greatest search bound 3 if P + Q .ne. 1 BOUND <-- Undefined if STATUS is 0 Bound exceeded by parameter number I if STATUS is negative. Lower search bound if STATUS is 1. Upper search bound if STATUS is 2. Method Formula 26.5.27 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the computation of the cumulative distribution function to that of an incomplete beta. Computation of other parameters involve a seach for a value that produces the desired value of P. The search relies on the monotinicity of P with the other parameter. **********************************************************************/ { #define tol (1.0e-8) #define atol (1.0e-50) #define zero (1.0e-300) #define inf 1.0e300 #define maxdf 1.0e10 static int K1 = 1; static double K4 = 0.5e0; static double K5 = 5.0e0; static double fx,cum,ccum,pq; static unsigned long qhi,qleft,qporq; static double T2,T3,T6,T7,T8,T9,T10,T11; /* .. .. Executable Statements .. */ /* Check arguments */ if(!(*which < 1 || *which > 3)) goto S30; if(!(*which < 1)) goto S10; *bound = 1.0e0; goto S20; S10: *bound = 3.0e0; S20: *status = -1; return; S30: if(*which == 1) goto S70; /* P */ if(!(*p <= 0.0e0 || *p > 1.0e0)) goto S60; if(!(*p <= 0.0e0)) goto S40; *bound = 0.0e0; goto S50; S40: *bound = 1.0e0; S50: *status = -2; return; S70: S60: if(*which == 1) goto S110; /* Q */ if(!(*q <= 0.0e0 || *q > 1.0e0)) goto S100; if(!(*q <= 0.0e0)) goto S80; *bound = 0.0e0; goto S90; S80: *bound = 1.0e0; S90: *status = -3; return; S110: S100: if(*which == 3) goto S130; /* DF */ if(!(*df <= 0.0e0)) goto S120; *bound = 0.0e0; *status = -5; return; S130: S120: if(*which == 1) goto S170; /* P + Q */ pq = *p+*q; if(!(fabs(pq-0.5e0-0.5e0) > 3.0e0*spmpar(&K1))) goto S160; if(!(pq < 0.0e0)) goto S140; *bound = 0.0e0; goto S150; S140: *bound = 1.0e0; S150: *status = 3; return; S170: S160: if(!(*which == 1)) qporq = *p <= *q; /* Select the minimum of P or Q Calculate ANSWERS */ if(1 == *which) { /* Computing P and Q */ cumt(t,df,p,q); *status = 0; } else if(2 == *which) { /* Computing T .. Get initial approximation for T */ *t = dt1(p,q,df); T2 = -inf; T3 = inf; T6 = atol; T7 = tol; dstinv(&T2,&T3,&K4,&K4,&K5,&T6,&T7); *status = 0; dinvr(status,t,&fx,&qleft,&qhi); S180: if(!(*status == 1)) goto S210; cumt(t,df,&cum,&ccum); if(!qporq) goto S190; fx = cum-*p; goto S200; S190: fx = ccum-*q; S200: dinvr(status,t,&fx,&qleft,&qhi); goto S180; S210: if(!(*status == -1)) goto S240; if(!qleft) goto S220; *status = 1; *bound = -inf; goto S230; S220: *status = 2; *bound = inf; S240: S230: ; } else if(3 == *which) { /* Computing DF */ *df = 5.0e0; T8 = zero; T9 = maxdf; T10 = atol; T11 = tol; dstinv(&T8,&T9,&K4,&K4,&K5,&T10,&T11); *status = 0; dinvr(status,df,&fx,&qleft,&qhi); S250: if(!(*status == 1)) goto S280; cumt(t,df,&cum,&ccum); if(!qporq) goto S260; fx = cum-*p; goto S270; S260: fx = ccum-*q; S270: dinvr(status,df,&fx,&qleft,&qhi); goto S250; S280: if(!(*status == -1)) goto S310; if(!qleft) goto S290; *status = 1; *bound = zero; goto S300; S290: *status = 2; *bound = maxdf; S300: ; } S310: return; #undef tol #undef atol #undef zero #undef inf #undef maxdf } /* END */ /***=====================================================================***/ static void cumbet(double *x,double *y,double *a,double *b,double *cum, double *ccum) /* ********************************************************************** void cumbet(double *x,double *y,double *a,double *b,double *cum, double *ccum) Double precision cUMulative incomplete BETa distribution Function Calculates the cdf to X of the incomplete beta distribution with parameters a and b. This is the integral from 0 to x of (1/B(a,b))*f(t)) where f(t) = t**(a-1) * (1-t)**(b-1) Arguments X --> Upper limit of integration. X is DOUBLE PRECISION Y --> 1 - X. Y is DOUBLE PRECISION A --> First parameter of the beta distribution. A is DOUBLE PRECISION B --> Second parameter of the beta distribution. B is DOUBLE PRECISION CUM <-- Cumulative incomplete beta distribution. CUM is DOUBLE PRECISION CCUM <-- Compliment of Cumulative incomplete beta distribution. CCUM is DOUBLE PRECISION Method Calls the routine BRATIO. References Didonato, Armido R. and Morris, Alfred H. Jr. (1992) Algorithim 708 Significant Digit Computation of the Incomplete Beta Function Ratios. ACM ToMS, Vol.18, No. 3, Sept. 1992, 360-373. ********************************************************************** */ { static int ierr; /* .. .. Executable Statements .. */ if(!(*x <= 0.0e0)) goto S10; *cum = 0.0e0; *ccum = 1.0e0; return; S10: if(!(*y <= 0.0e0)) goto S20; *cum = 1.0e0; *ccum = 0.0e0; return; S20: bratio(a,b,x,y,cum,ccum,&ierr); /* Call bratio routine */ return; } /* END */ /***=====================================================================***/ static void cumbin(double *s,double *xn,double *pr,double *ompr, double *cum,double *ccum) /* ********************************************************************** void cumbin(double *s,double *xn,double *pr,double *ompr, double *cum,double *ccum) CUmulative BINomial distribution Function Returns the probability of 0 to S successes in XN binomial trials, each of which has a probability of success, PBIN. Arguments S --> The upper limit of cumulation of the binomial distribution. S is DOUBLE PRECISION XN --> The number of binomial trials. XN is DOUBLE PRECISIO PBIN --> The probability of success in each binomial trial. PBIN is DOUBLE PRECIS OMPR --> 1 - PBIN OMPR is DOUBLE PRECIS CUM <-- Cumulative binomial distribution. CUM is DOUBLE PRECISI CCUM <-- Compliment of Cumulative binomial distribution. CCUM is DOUBLE PRECIS Method Formula 26.5.24 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the binomial distribution to the cumulative beta distribution. ********************************************************************** */ { static double T1,T2; /* .. .. Executable Statements .. */ if(!(*s < *xn)) goto S10; T1 = *s+1.0e0; T2 = *xn-*s; cumbet(pr,ompr,&T1,&T2,ccum,cum); goto S20; S10: *cum = 1.0e0; *ccum = 0.0e0; S20: return; } /* END */ /***=====================================================================***/ static void cumchi(double *x,double *df,double *cum,double *ccum) /* ********************************************************************** void cumchi(double *x,double *df,double *cum,double *ccum) CUMulative of the CHi-square distribution Function Calculates the cumulative chi-square distribution. Arguments X --> Upper limit of integration of the chi-square distribution. X is DOUBLE PRECISION DF --> Degrees of freedom of the chi-square distribution. DF is DOUBLE PRECISION CUM <-- Cumulative chi-square distribution. CUM is DOUBLE PRECISIO CCUM <-- Compliment of Cumulative chi-square distribution. CCUM is DOUBLE PRECISI Method Calls incomplete gamma function (CUMGAM) ********************************************************************** */ { static double a,xx; /* .. .. Executable Statements .. */ a = *df*0.5e0; xx = *x*0.5e0; cumgam(&xx,&a,cum,ccum); return; } /* END */ /***=====================================================================***/ static void cumchn(double *x,double *df,double *pnonc,double *cum, double *ccum) /* ********************************************************************** void cumchn(double *x,double *df,double *pnonc,double *cum, double *ccum) CUMulative of the Non-central CHi-square distribution Function Calculates the cumulative non-central chi-square distribution, i.e., the probability that a random variable which follows the non-central chi-square distribution, with non-centrality parameter PNONC and continuous degrees of freedom DF, is less than or equal to X. Arguments X --> Upper limit of integration of the non-central chi-square distribution. X is DOUBLE PRECISION DF --> Degrees of freedom of the non-central chi-square distribution. DF is DOUBLE PRECISION PNONC --> Non-centrality parameter of the non-central chi-square distribution. PNONC is DOUBLE PRECIS CUM <-- Cumulative non-central chi-square distribution. CUM is DOUBLE PRECISIO CCUM <-- Compliment of Cumulative non-central chi-square distribut CCUM is DOUBLE PRECISI Method Uses formula 26.4.25 of Abramowitz and Stegun, Handbook of Mathematical Functions, US NBS (1966) to calculate the non-central chi-square. Variables EPS --- Convergence criterion. The sum stops when a term is less than EPS*SUM. EPS is DOUBLE PRECISIO NTIRED --- Maximum number of terms to be evaluated in each sum. NTIRED is INTEGER QCONV --- .TRUE. if convergence achieved - i.e., program did not stop on NTIRED criterion. QCONV is LOGICAL CCUM <-- Compliment of Cumulative non-central chi-square distribution. CCUM is DOUBLE PRECISI ********************************************************************** */ { #define dg(i) (*df+2.0e0*(double)(i)) #define qsmall(xx) (int)(sum < 1.0e-20 || (xx) < eps*sum) #define qtired(i) (int)((i) > ntired) static double eps = 1.0e-5; static int ntired = 1000; static double adj,centaj,centwt,chid2,dfd2,lcntaj,lcntwt,lfact,pcent,pterm,sum, sumadj,term,wt,xnonc; static int i,icent,iterb,iterf; static double T1,T2,T3; /* .. .. Executable Statements .. */ if(!(*x <= 0.0e0)) goto S10; *cum = 0.0e0; *ccum = 1.0e0; return; S10: if(!(*pnonc <= 1.0e-10)) goto S20; /* When non-centrality parameter is (essentially) zero, use cumulative chi-square distribution */ cumchi(x,df,cum,ccum); return; S20: xnonc = *pnonc/2.0e0; /* ********************************************************************** The following code calcualtes the weight, chi-square, and adjustment term for the central term in the infinite series. The central term is the one in which the poisson weight is greatest. The adjustment term is the amount that must be subtracted from the chi-square to move up two degrees of freedom. ********************************************************************** */ icent = fifidint(xnonc); if(icent == 0) icent = 1; chid2 = *x/2.0e0; /* Calculate central weight term */ T1 = (double)(icent+1); lfact = alngam(&T1); lcntwt = -xnonc+(double)icent*log(xnonc)-lfact; centwt = exp(lcntwt); /* Calculate central chi-square */ T2 = dg(icent); cumchi(x,&T2,&pcent,ccum); /* Calculate central adjustment term */ dfd2 = dg(icent)/2.0e0; T3 = 1.0e0+dfd2; lfact = alngam(&T3); lcntaj = dfd2*log(chid2)-chid2-lfact; centaj = exp(lcntaj); sum = centwt*pcent; /* ********************************************************************** Sum backwards from the central term towards zero. Quit whenever either (1) the zero term is reached, or (2) the term gets small relative to the sum, or (3) More than NTIRED terms are totaled. ********************************************************************** */ iterb = 0; sumadj = 0.0e0; adj = centaj; wt = centwt; i = icent; goto S40; S30: if(qtired(iterb) || qsmall(term) || i == 0) goto S50; S40: dfd2 = dg(i)/2.0e0; /* Adjust chi-square for two fewer degrees of freedom. The adjusted value ends up in PTERM. */ adj = adj*dfd2/chid2; sumadj += adj; pterm = pcent+sumadj; /* Adjust poisson weight for J decreased by one */ wt *= ((double)i/xnonc); term = wt*pterm; sum += term; i -= 1; iterb += 1; goto S30; S50: iterf = 0; /* ********************************************************************** Now sum forward from the central term towards infinity. Quit when either (1) the term gets small relative to the sum, or (2) More than NTIRED terms are totaled. ********************************************************************** */ sumadj = adj = centaj; wt = centwt; i = icent; goto S70; S60: if(qtired(iterf) || qsmall(term)) goto S80; S70: /* Update weights for next higher J */ wt *= (xnonc/(double)(i+1)); /* Calculate PTERM and add term to sum */ pterm = pcent-sumadj; term = wt*pterm; sum += term; /* Update adjustment term for DF for next iteration */ i += 1; dfd2 = dg(i)/2.0e0; adj = adj*chid2/dfd2; sumadj += adj; iterf += 1; goto S60; S80: *cum = sum; *ccum = 0.5e0+(0.5e0-*cum); return; #undef dg #undef qsmall #undef qtired } /* END */ /***=====================================================================***/ static void cumf(double *f,double *dfn,double *dfd,double *cum,double *ccum) /* ********************************************************************** void cumf(double *f,double *dfn,double *dfd,double *cum,double *ccum) CUMulative F distribution Function Computes the integral from 0 to F of the f-density with DFN and DFD degrees of freedom. Arguments F --> Upper limit of integration of the f-density. F is DOUBLE PRECISION DFN --> Degrees of freedom of the numerator sum of squares. DFN is DOUBLE PRECISI DFD --> Degrees of freedom of the denominator sum of squares. DFD is DOUBLE PRECISI CUM <-- Cumulative f distribution. CUM is DOUBLE PRECISI CCUM <-- Compliment of Cumulative f distribution. CCUM is DOUBLE PRECIS Method Formula 26.5.28 of Abramowitz and Stegun is used to reduce the cumulative F to a cumulative beta distribution. Note If F is less than or equal to 0, 0 is returned. ********************************************************************** */ { #define half 0.5e0 #define done 1.0e0 static double dsum,prod,xx,yy; static int ierr; static double T1,T2; /* .. .. Executable Statements .. */ if(!(*f <= 0.0e0)) goto S10; *cum = 0.0e0; *ccum = 1.0e0; return; S10: prod = *dfn**f; /* XX is such that the incomplete beta with parameters DFD/2 and DFN/2 evaluated at XX is 1 - CUM or CCUM YY is 1 - XX Calculate the smaller of XX and YY accurately */ dsum = *dfd+prod; xx = *dfd/dsum; if(xx > half) { yy = prod/dsum; xx = done-yy; } else yy = done-xx; T1 = *dfd*half; T2 = *dfn*half; bratio(&T1,&T2,&xx,&yy,ccum,cum,&ierr); return; #undef half #undef done } /* END */ /***=====================================================================***/ static void cumfnc(double *f,double *dfn,double *dfd,double *pnonc, double *cum,double *ccum) /* ********************************************************************** F -NON- -C-ENTRAL F DISTRIBUTION Function COMPUTES NONCENTRAL F DISTRIBUTION WITH DFN AND DFD DEGREES OF FREEDOM AND NONCENTRALITY PARAMETER PNONC Arguments X --> UPPER LIMIT OF INTEGRATION OF NONCENTRAL F IN EQUATION DFN --> DEGREES OF FREEDOM OF NUMERATOR DFD --> DEGREES OF FREEDOM OF DENOMINATOR PNONC --> NONCENTRALITY PARAMETER. CUM <-- CUMULATIVE NONCENTRAL F DISTRIBUTION CCUM <-- COMPLIMENT OF CUMMULATIVE Method USES FORMULA 26.6.20 OF REFERENCE FOR INFINITE SERIES. SERIES IS CALCULATED BACKWARD AND FORWARD FROM J = LAMBDA/2 (THIS IS THE TERM WITH THE LARGEST POISSON WEIGHT) UNTIL THE CONVERGENCE CRITERION IS MET. FOR SPEED, THE INCOMPLETE BETA FUNCTIONS ARE EVALUATED BY FORMULA 26.5.16. REFERENCE HANDBOOD OF MATHEMATICAL FUNCTIONS EDITED BY MILTON ABRAMOWITZ AND IRENE A. STEGUN NATIONAL BUREAU OF STANDARDS APPLIED MATEMATICS SERIES - 55 MARCH 1965 P 947, EQUATIONS 26.6.17, 26.6.18 Note THE SUM CONTINUES UNTIL A SUCCEEDING TERM IS LESS THAN EPS TIMES THE SUM (OR THE SUM IS LESS THAN 1.0E-20). EPS IS SET TO 1.0E-4 IN A DATA STATEMENT WHICH CAN BE CHANGED. ********************************************************************** */ { #define qsmall(x) (int)(sum < 1.0e-20 || (x) < eps*sum) #define half 0.5e0 #define done 1.0e0 static double eps = 1.0e-4; static double dsum,dummy,prod,xx,yy,adn,aup,b,betdn,betup,centwt,dnterm,sum, upterm,xmult,xnonc; static int i,icent,ierr; static double T1,T2,T3,T4,T5,T6; /* .. .. Executable Statements .. */ if(!(*f <= 0.0e0)) goto S10; *cum = 0.0e0; *ccum = 1.0e0; return; S10: if(!(*pnonc < 1.0e-10)) goto S20; /* Handle case in which the non-centrality parameter is (essentially) zero. */ cumf(f,dfn,dfd,cum,ccum); return; S20: xnonc = *pnonc/2.0e0; /* Calculate the central term of the poisson weighting factor. */ icent = xnonc; if(icent == 0) icent = 1; /* Compute central weight term */ T1 = (double)(icent+1); centwt = exp(-xnonc+(double)icent*log(xnonc)-alngam(&T1)); /* Compute central incomplete beta term Assure that minimum of arg to beta and 1 - arg is computed accurately. */ prod = *dfn**f; dsum = *dfd+prod; yy = *dfd/dsum; if(yy > half) { xx = prod/dsum; yy = done-xx; } else xx = done-yy; T2 = *dfn*half+(double)icent; T3 = *dfd*half; bratio(&T2,&T3,&xx,&yy,&betdn,&dummy,&ierr); adn = *dfn/2.0e0+(double)icent; aup = adn; b = *dfd/2.0e0; betup = betdn; sum = centwt*betdn; /* Now sum terms backward from icent until convergence or all done */ xmult = centwt; i = icent; T4 = adn+b; T5 = adn+1.0e0; dnterm = exp(alngam(&T4)-alngam(&T5)-alngam(&b)+adn*log(xx)+b*log(yy)); S30: if(qsmall(xmult*betdn) || i <= 0) goto S40; xmult *= ((double)i/xnonc); i -= 1; adn -= 1.0; dnterm = (adn+1.0)/((adn+b)*xx)*dnterm; betdn += dnterm; sum += (xmult*betdn); goto S30; S40: i = icent+1; /* Now sum forwards until convergence */ xmult = centwt; if(aup-1.0+b == 0) upterm = exp(-alngam(&aup)-alngam(&b)+(aup-1.0)*log(xx)+ b*log(yy)); else { T6 = aup-1.0+b; upterm = exp(alngam(&T6)-alngam(&aup)-alngam(&b)+(aup-1.0)*log(xx)+b* log(yy)); } goto S60; S50: if(qsmall(xmult*betup)) goto S70; S60: xmult *= (xnonc/(double)i); i += 1; aup += 1.0; upterm = (aup+b-2.0e0)*xx/(aup-1.0)*upterm; betup -= upterm; sum += (xmult*betup); goto S50; S70: *cum = sum; *ccum = 0.5e0+(0.5e0-*cum); return; #undef qsmall #undef half #undef done } /* END */ /***=====================================================================***/ static void cumgam(double *x,double *a,double *cum,double *ccum) /* ********************************************************************** void cumgam(double *x,double *a,double *cum,double *ccum) Double precision cUMulative incomplete GAMma distribution Function Computes the cumulative of the incomplete gamma distribution, i.e., the integral from 0 to X of (1/GAM(A))*EXP(-T)*T**(A-1) DT where GAM(A) is the complete gamma function of A, i.e., GAM(A) = integral from 0 to infinity of EXP(-T)*T**(A-1) DT Arguments X --> The upper limit of integration of the incomplete gamma. X is DOUBLE PRECISION A --> The shape parameter of the incomplete gamma. A is DOUBLE PRECISION CUM <-- Cumulative incomplete gamma distribution. CUM is DOUBLE PRECISION CCUM <-- Compliment of Cumulative incomplete gamma distribution. CCUM is DOUBLE PRECISIO Method Calls the routine GRATIO. ********************************************************************** */ { static int K1 = 0; /* .. .. Executable Statements .. */ if(!(*x <= 0.0e0)) goto S10; *cum = 0.0e0; *ccum = 1.0e0; return; S10: gratio(a,x,cum,ccum,&K1); /* Call gratio routine */ return; } /* END */ /***=====================================================================***/ static void cumnbn(double *s,double *xn,double *pr,double *ompr, double *cum,double *ccum) /* ********************************************************************** void cumnbn(double *s,double *xn,double *pr,double *ompr, double *cum,double *ccum) CUmulative Negative BINomial distribution Function Returns the probability that it there will be S or fewer failures before there are XN successes, with each binomial trial having a probability of success PR. Prob(# failures = S | XN successes, PR) = ( XN + S - 1 ) ( ) * PR^XN * (1-PR)^S ( S ) Arguments S --> The number of failures S is DOUBLE PRECISION XN --> The number of successes XN is DOUBLE PRECISIO PR --> The probability of success in each binomial trial. PR is DOUBLE PRECISIO OMPR --> 1 - PR OMPR is DOUBLE PRECIS CUM <-- Cumulative negative binomial distribution. CUM is DOUBLE PRECISI CCUM <-- Compliment of Cumulative negative binomial distribution. CCUM is DOUBLE PRECIS Method Formula 26.5.26 of Abramowitz and Stegun, Handbook of Mathematical Functions (1966) is used to reduce the negative binomial distribution to the cumulative beta distribution. ********************************************************************** */ { static double T1; /* .. .. Executable Statements .. */ T1 = *s+1.e0; cumbet(pr,ompr,xn,&T1,cum,ccum); return; } /* END */ /***=====================================================================***/ static void cumnor(double *arg,double *result,double *ccum) /* ********************************************************************** void cumnor(double *arg,double *result,double *ccum) Function Computes the cumulative of the normal distribution, i.e., the integral from -infinity to x of (1/sqrt(2*pi)) exp(-u*u/2) du X --> Upper limit of integration. X is DOUBLE PRECISION RESULT <-- Cumulative normal distribution. RESULT is DOUBLE PRECISION CCUM <-- Compliment of Cumulative normal distribution. CCUM is DOUBLE PRECISION Renaming of function ANORM from: Cody, W.D. (1993). "ALGORITHM 715: SPECFUN - A Portabel FORTRAN Package of Special Function Routines and Test Drivers" acm Transactions on Mathematical Software. 19, 22-32. with slight modifications to return ccum and to deal with machine constants. ********************************************************************** Original Comments: ------------------------------------------------------------------ This function evaluates the normal distribution function: / x 1 | -t*t/2 P(x) = ----------- | e dt sqrt(2 pi) | /-oo The main computation evaluates near-minimax approximations derived from those in "Rational Chebyshev approximations for the error function" by W. J. Cody, Math. Comp., 1969, 631-637. This transportable program uses rational functions that theoretically approximate the normal distribution function to at least 18 significant decimal digits. The accuracy achieved depends on the arithmetic system, the compiler, the intrinsic functions, and proper selection of the machine-dependent constants. ******************************************************************* ******************************************************************* Explanation of machine-dependent constants. MIN = smallest machine representable number. EPS = argument below which anorm(x) may be represented by 0.5 and above which x*x will not underflow. A conservative value is the largest machine number X such that 1.0 + X = 1.0 to machine precision. ******************************************************************* ******************************************************************* Error returns The program returns ANORM = 0 for ARG .LE. XLOW. Intrinsic functions required are: ABS, AINT, EXP Author: W. J. Cody Mathematics and Computer Science Division Argonne National Laboratory Argonne, IL 60439 Latest modification: March 15, 1992 ------------------------------------------------------------------ */ { static double a[5] = { 2.2352520354606839287e00,1.6102823106855587881e02,1.0676894854603709582e03, 1.8154981253343561249e04,6.5682337918207449113e-2 }; static double b[4] = { 4.7202581904688241870e01,9.7609855173777669322e02,1.0260932208618978205e04, 4.5507789335026729956e04 }; static double c[9] = { 3.9894151208813466764e-1,8.8831497943883759412e00,9.3506656132177855979e01, 5.9727027639480026226e02,2.4945375852903726711e03,6.8481904505362823326e03, 1.1602651437647350124e04,9.8427148383839780218e03,1.0765576773720192317e-8 }; static double d[8] = { 2.2266688044328115691e01,2.3538790178262499861e02,1.5193775994075548050e03, 6.4855582982667607550e03,1.8615571640885098091e04,3.4900952721145977266e04, 3.8912003286093271411e04,1.9685429676859990727e04 }; static double half = 0.5e0; static double p[6] = { 2.1589853405795699e-1,1.274011611602473639e-1,2.2235277870649807e-2, 1.421619193227893466e-3,2.9112874951168792e-5,2.307344176494017303e-2 }; static double one = 1.0e0; static double q[5] = { 1.28426009614491121e00,4.68238212480865118e-1,6.59881378689285515e-2, 3.78239633202758244e-3,7.29751555083966205e-5 }; static double sixten = 1.60e0; static double sqrpi = 3.9894228040143267794e-1; static double thrsh = 0.66291e0; static double root32 = 5.656854248e0; static double zero = 0.0e0; static int K1 = 1; static int K2 = 2; static int i; static double del,eps,temp,x,xden,xnum,y,xsq,min; /* ------------------------------------------------------------------ Machine dependent constants ------------------------------------------------------------------ */ eps = spmpar(&K1)*0.5e0; min = spmpar(&K2); x = *arg; y = fabs(x); if(y <= thrsh) { /* ------------------------------------------------------------------ Evaluate anorm for |X| <= 0.66291 ------------------------------------------------------------------ */ xsq = zero; if(y > eps) xsq = x*x; xnum = a[4]*xsq; xden = xsq; for(i=0; i<3; i++) { xnum = (xnum+a[i])*xsq; xden = (xden+b[i])*xsq; } *result = x*(xnum+a[3])/(xden+b[3]); temp = *result; *result = half+temp; *ccum = half-temp; } /* ------------------------------------------------------------------ Evaluate anorm for 0.66291 <= |X| <= sqrt(32) ------------------------------------------------------------------ */ else if(y <= root32) { xnum = c[8]*y; xden = y; for(i=0; i<7; i++) { xnum = (xnum+c[i])*y; xden = (xden+d[i])*y; } *result = (xnum+c[7])/(xden+d[7]); xsq = fifdint(y*sixten)/sixten; del = (y-xsq)*(y+xsq); *result = exp(-(xsq*xsq*half))*exp(-(del*half))**result; *ccum = one-*result; if(x > zero) { temp = *result; *result = *ccum; *ccum = temp; } } /* ------------------------------------------------------------------ Evaluate anorm for |X| > sqrt(32) ------------------------------------------------------------------ */ else { *result = zero; xsq = one/(x*x); xnum = p[5]*xsq; xden = xsq; for(i=0; i<4; i++) { xnum = (xnum+p[i])*xsq; xden = (xden+q[i])*xsq; } *result = xsq*(xnum+p[4])/(xden+q[4]); *result = (sqrpi-*result)/y; xsq = fifdint(x*sixten)/sixten; del = (x-xsq)*(x+xsq); *result = exp(-(xsq*xsq*half))*exp(-(del*half))**result; *ccum = one-*result; if(x > zero) { temp = *result; *result = *ccum; *ccum = temp; } } if(*result < min) *result = 0.0e0; /* ------------------------------------------------------------------ Fix up for negative argument, erf, etc. ------------------------------------------------------------------ ----------Last card of ANORM ---------- */ if(*ccum < min) *ccum = 0.0e0; } /* END */ /***=====================================================================***/ static void cumpoi(double *s,double *xlam,double *cum,double *ccum) /* ********************************************************************** void cumpoi(double *s,double *xlam,double *cum,double *ccum) CUMulative POIsson distribution Function Returns the probability of S or fewer events in a Poisson distribution with mean XLAM. Arguments S --> Upper limit of cumulation of the Poisson. S is DOUBLE PRECISION XLAM --> Mean of the Poisson distribution. XLAM is DOUBLE PRECIS CUM <-- Cumulative poisson distribution. CUM is DOUBLE PRECISION CCUM <-- Compliment of Cumulative poisson distribution. CCUM is DOUBLE PRECIS Method Uses formula 26.4.21 of Abramowitz and Stegun, Handbook of Mathematical Functions to reduce the cumulative Poisson to the cumulative chi-square distribution. ********************************************************************** */ { static double chi,df; /* .. .. Executable Statements .. */ df = 2.0e0*(*s+1.0e0); chi = 2.0e0**xlam; cumchi(&chi,&df,ccum,cum); return; } /* END */ /***=====================================================================***/ static void cumt(double *t,double *df,double *cum,double *ccum) /* ********************************************************************** void cumt(double *t,double *df,double *cum,double *ccum) CUMulative T-distribution Function Computes the integral from -infinity to T of the t-density. Arguments T --> Upper limit of integration of the t-density. T is DOUBLE PRECISION DF --> Degrees of freedom of the t-distribution. DF is DOUBLE PRECISIO CUM <-- Cumulative t-distribution. CCUM is DOUBLE PRECIS CCUM <-- Compliment of Cumulative t-distribution. CCUM is DOUBLE PRECIS Method Formula 26.5.27 of Abramowitz and Stegun, Handbook of Mathematical Functions is used to reduce the t-distribution to an incomplete beta. ********************************************************************** */ { static double K2 = 0.5e0; static double xx,a,oma,tt,yy,dfptt,T1; /* .. .. Executable Statements .. */ tt = *t**t; dfptt = *df+tt; xx = *df/dfptt; yy = tt/dfptt; T1 = 0.5e0**df; cumbet(&xx,&yy,&T1,&K2,&a,&oma); if(!(*t <= 0.0e0)) goto S10; *cum = 0.5e0*a; *ccum = oma+*cum; goto S20; S10: *ccum = 0.5e0*a; *cum = oma+*ccum; S20: return; } /* END */ /***=====================================================================***/ static double dbetrm(double *a,double *b) /* ********************************************************************** double dbetrm(double *a,double *b) Double Precision Sterling Remainder for Complete Beta Function Function Log(Beta(A,B)) = Lgamma(A) + Lgamma(B) - Lgamma(A+B) where Lgamma is the log of the (complete) gamma function Let ZZ be approximation obtained if each log gamma is approximated by Sterling's formula, i.e., Sterling(Z) = LOG( SQRT( 2*PI ) ) + ( Z-0.5 ) * LOG( Z ) - Z Returns Log(Beta(A,B)) - ZZ Arguments A --> One argument of the Beta DOUBLE PRECISION A B --> The other argument of the Beta DOUBLE PRECISION B ********************************************************************** */ { static double dbetrm,T1,T2,T3; /* .. .. Executable Statements .. */ /* Try to sum from smallest to largest */ T1 = *a+*b; dbetrm = -dstrem(&T1); T2 = fifdmax1(*a,*b); dbetrm += dstrem(&T2); T3 = fifdmin1(*a,*b); dbetrm += dstrem(&T3); return dbetrm; } /* END */ /***=====================================================================***/ static double devlpl(double a[],int *n,double *x) /* ********************************************************************** double devlpl(double a[],int *n,double *x) Double precision EVALuate a PoLynomial at X Function returns A(1) + A(2)*X + ... + A(N)*X**(N-1) Arguments A --> Array of coefficients of the polynomial. A is DOUBLE PRECISION(N) N --> Length of A, also degree of polynomial - 1. N is INTEGER X --> Point at which the polynomial is to be evaluated. X is DOUBLE PRECISION ********************************************************************** */ { static double devlpl,term; static int i; /* .. .. Executable Statements .. */ term = a[*n-1]; for(i= *n-1-1; i>=0; i--) term = a[i]+term**x; devlpl = term; return devlpl; } /* END */ /***=====================================================================***/ static double dexpm1(double *x) /* ********************************************************************** double dexpm1(double *x) Evaluation of the function EXP(X) - 1 Arguments X --> Argument at which exp(x)-1 desired DOUBLE PRECISION X Method Renaming of function rexp from code of: DiDinato, A. R. and Morris, A. H. Algorithm 708: Significant Digit Computation of the Incomplete Beta Function Ratios. ACM Trans. Math. Softw. 18 (1993), 360-373. ********************************************************************** */ { static double p1 = .914041914819518e-09; static double p2 = .238082361044469e-01; static double q1 = -.499999999085958e+00; static double q2 = .107141568980644e+00; static double q3 = -.119041179760821e-01; static double q4 = .595130811860248e-03; static double dexpm1,w; /* .. .. Executable Statements .. */ if(fabs(*x) > 0.15e0) goto S10; dexpm1 = *x*(((p2**x+p1)**x+1.0e0)/((((q4**x+q3)**x+q2)**x+q1)**x+1.0e0)); return dexpm1; S10: w = exp(*x); if(*x > 0.0e0) goto S20; dexpm1 = w-0.5e0-0.5e0; return dexpm1; S20: dexpm1 = w*(0.5e0+(0.5e0-1.0e0/w)); return dexpm1; } /* END */ /***=====================================================================***/ static double dinvnr(double *p,double *q) /* ********************************************************************** double dinvnr(double *p,double *q) Double precision NoRmal distribution INVerse Function Returns X such that CUMNOR(X) = P, i.e., the integral from - infinity to X of (1/SQRT(2*PI)) EXP(-U*U/2) dU is P Arguments P --> The probability whose normal deviate is sought. P is DOUBLE PRECISION Q --> 1-P P is DOUBLE PRECISION Method The rational function on page 95 of Kennedy and Gentle, Statistical Computing, Marcel Dekker, NY , 1980 is used as a start value for the Newton method of finding roots. Note If P or Q .lt. machine EPS returns +/- DINVNR(EPS) ********************************************************************** */ { #define maxit 100 #define eps (1.0e-13) #define r2pi 0.3989422804014326e0 #define nhalf (-0.5e0) #define dennor(x) (r2pi*exp(nhalf*(x)*(x))) static double dinvnr,strtx,xcur,cum,ccum,pp,dx; static int i; static unsigned long qporq; /* .. .. Executable Statements .. */ /* FIND MINIMUM OF P AND Q */ qporq = *p <= *q; if(!qporq) goto S10; pp = *p; goto S20; S10: pp = *q; S20: /* INITIALIZATION STEP */ strtx = stvaln(&pp); xcur = strtx; /* NEWTON INTERATIONS */ for(i=1; i<=maxit; i++) { cumnor(&xcur,&cum,&ccum); dx = (cum-pp)/dennor(xcur); xcur -= dx; if(fabs(dx/xcur) < eps) goto S40; } dinvnr = strtx; /* IF WE GET HERE, NEWTON HAS FAILED */ if(!qporq) dinvnr = -dinvnr; return dinvnr; S40: /* IF WE GET HERE, NEWTON HAS SUCCEDED */ dinvnr = xcur; if(!qporq) dinvnr = -dinvnr; return dinvnr; #undef maxit #undef eps #undef r2pi #undef nhalf #undef dennor } /* END */ /***=====================================================================***/ static void E0000(int IENTRY,int *status,double *x,double *fx, unsigned long *qleft,unsigned long *qhi,double *zabsst, double *zabsto,double *zbig,double *zrelst, double *zrelto,double *zsmall,double *zstpmu) { #define qxmon(zx,zy,zz) (int)((zx) <= (zy) && (zy) <= (zz)) static double absstp,abstol,big,fbig,fsmall,relstp,reltol,small,step,stpmul,xhi, xlb,xlo,xsave,xub,yy; static int i99999; static unsigned long qbdd,qcond,qdum1,qdum2,qincr,qlim,qok,qup; switch(IENTRY){case 0: goto DINVR; case 1: goto DSTINV;} DINVR: if(*status > 0) goto S310; qcond = !qxmon(small,*x,big); if(qcond){ ftnstop("SMALL,X,BIG nonmonotone in E0000"); *status=-1; return;} xsave = *x; /* See that SMALL and BIG bound the zero and set QINCR */ *x = small; /* GET-FUNCTION-VALUE */ i99999 = 1; goto S300; S10: fsmall = *fx; *x = big; /* GET-FUNCTION-VALUE */ i99999 = 2; goto S300; S20: fbig = *fx; qincr = fbig > fsmall; if(!qincr) goto S50; if(fsmall <= 0.0e0) goto S30; *status = -1; *qleft = *qhi = 1; return; S30: if(fbig >= 0.0e0) goto S40; *status = -1; *qleft = *qhi = 0; return; S40: goto S80; S50: if(fsmall >= 0.0e0) goto S60; *status = -1; *qleft = 1; *qhi = 0; return; S60: if(fbig <= 0.0e0) goto S70; *status = -1; *qleft = 0; *qhi = 1; return; S80: S70: *x = xsave; step = fifdmax1(absstp,relstp*fabs(*x)); /* YY = F(X) - Y GET-FUNCTION-VALUE */ i99999 = 3; goto S300; S90: yy = *fx; if(!(yy == 0.0e0)) goto S100; *status = 0; qok = 1; return; S100: qup = qincr && yy < 0.0e0 || !qincr && yy > 0.0e0; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HANDLE CASE IN WHICH WE MUST STEP HIGHER ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ if(!qup) goto S170; xlb = xsave; xub = fifdmin1(xlb+step,big); goto S120; S110: if(qcond) goto S150; S120: /* YY = F(XUB) - Y */ *x = xub; /* GET-FUNCTION-VALUE */ i99999 = 4; goto S300; S130: yy = *fx; qbdd = qincr && yy >= 0.0e0 || !qincr && yy <= 0.0e0; qlim = xub >= big; qcond = qbdd || qlim; if(qcond) goto S140; step = stpmul*step; xlb = xub; xub = fifdmin1(xlb+step,big); S140: goto S110; S150: if(!(qlim && !qbdd)) goto S160; *status = -1; *qleft = 0; *qhi = !qincr; *x = big; return; S160: goto S240; S170: /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HANDLE CASE IN WHICH WE MUST STEP LOWER ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ xub = xsave; xlb = fifdmax1(xub-step,small); goto S190; S180: if(qcond) goto S220; S190: /* YY = F(XLB) - Y */ *x = xlb; /* GET-FUNCTION-VALUE */ i99999 = 5; goto S300; S200: yy = *fx; qbdd = qincr && yy <= 0.0e0 || !qincr && yy >= 0.0e0; qlim = xlb <= small; qcond = qbdd || qlim; if(qcond) goto S210; step = stpmul*step; xub = xlb; xlb = fifdmax1(xub-step,small); S210: goto S180; S220: if(!(qlim && !qbdd)) goto S230; *status = -1; *qleft = 1; *qhi = qincr; *x = small; return; S240: S230: dstzr(&xlb,&xub,&abstol,&reltol); /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ IF WE REACH HERE, XLB AND XUB BOUND THE ZERO OF F. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ *status = 0; goto S260; S250: if(!(*status == 1)) goto S290; S260: dzror(status,x,fx,&xlo,&xhi,&qdum1,&qdum2); if(!(*status == 1)) goto S280; /* GET-FUNCTION-VALUE */ i99999 = 6; goto S300; S280: S270: goto S250; S290: *x = xlo; *status = 0; return; DSTINV: small = *zsmall; big = *zbig; absstp = *zabsst; relstp = *zrelst; stpmul = *zstpmu; abstol = *zabsto; reltol = *zrelto; return; S300: /* TO GET-FUNCTION-VALUE */ *status = 1; return; S310: switch((int)i99999){case 1: goto S10;case 2: goto S20;case 3: goto S90;case 4: goto S130;case 5: goto S200;case 6: goto S270;default: break;} #undef qxmon } /* END */ /***=====================================================================***/ static void dinvr(int *status,double *x,double *fx, unsigned long *qleft,unsigned long *qhi) /* ********************************************************************** void dinvr(int *status,double *x,double *fx, unsigned long *qleft,unsigned long *qhi) Double precision bounds the zero of the function and invokes zror Reverse Communication Function Bounds the function and invokes ZROR to perform the zero finding. STINVR must have been called before this routine in order to set its parameters. Arguments STATUS <--> At the beginning of a zero finding problem, STATUS should be set to 0 and INVR invoked. (The value of parameters other than X will be ignored on this cal When INVR needs the function evaluated, it will set STATUS to 1 and return. The value of the function should be set in FX and INVR again called without changing any of its other parameters. When INVR has finished without error, it will return with STATUS 0. In that case X is approximately a root of F(X). If INVR cannot bound the function, it returns status -1 and sets QLEFT and QHI. INTEGER STATUS X <-- The value of X at which F(X) is to be evaluated. DOUBLE PRECISION X FX --> The value of F(X) calculated when INVR returns with STATUS = 1. DOUBLE PRECISION FX QLEFT <-- Defined only if QMFINV returns .FALSE. In that case it is .TRUE. If the stepping search terminated unsucessfully at SMALL. If it is .FALSE. the search terminated unsucessfully at BIG. QLEFT is LOGICAL QHI <-- Defined only if QMFINV returns .FALSE. In that case it is .TRUE. if F(X) .GT. Y at the termination of the search and .FALSE. if F(X) .LT. Y at the termination of the search. QHI is LOGICAL ********************************************************************** */ { E0000(0,status,x,fx,qleft,qhi,NULL,NULL,NULL,NULL,NULL,NULL,NULL); } /* END */ /***=====================================================================***/ static void dstinv(double *zsmall,double *zbig,double *zabsst, double *zrelst,double *zstpmu,double *zabsto, double *zrelto) /* ********************************************************************** void dstinv(double *zsmall,double *zbig,double *zabsst, double *zrelst,double *zstpmu,double *zabsto, double *zrelto) Double Precision - SeT INverse finder - Reverse Communication Function Concise Description - Given a monotone function F finds X such that F(X) = Y. Uses Reverse communication -- see invr. This routine sets quantities needed by INVR. More Precise Description of INVR - F must be a monotone function, the results of QMFINV are otherwise undefined. QINCR must be .TRUE. if F is non- decreasing and .FALSE. if F is non-increasing. QMFINV will return .TRUE. if and only if F(SMALL) and F(BIG) bracket Y, i. e., QINCR is .TRUE. and F(SMALL).LE.Y.LE.F(BIG) or QINCR is .FALSE. and F(BIG).LE.Y.LE.F(SMALL) if QMFINV returns .TRUE., then the X returned satisfies the following condition. let TOL(X) = MAX(ABSTOL,RELTOL*ABS(X)) then if QINCR is .TRUE., F(X-TOL(X)) .LE. Y .LE. F(X+TOL(X)) and if QINCR is .FALSE. F(X-TOL(X)) .GE. Y .GE. F(X+TOL(X)) Arguments SMALL --> The left endpoint of the interval to be searched for a solution. SMALL is DOUBLE PRECISION BIG --> The right endpoint of the interval to be searched for a solution. BIG is DOUBLE PRECISION ABSSTP, RELSTP --> The initial step size in the search is MAX(ABSSTP,RELSTP*ABS(X)). See algorithm. ABSSTP is DOUBLE PRECISION RELSTP is DOUBLE PRECISION STPMUL --> When a step doesn't bound the zero, the step size is multiplied by STPMUL and another step taken. A popular value is 2.0 DOUBLE PRECISION STPMUL ABSTOL, RELTOL --> Two numbers that determine the accuracy of the solution. See function for a precise definition. ABSTOL is DOUBLE PRECISION RELTOL is DOUBLE PRECISION Method Compares F(X) with Y for the input value of X then uses QINCR to determine whether to step left or right to bound the desired x. the initial step size is MAX(ABSSTP,RELSTP*ABS(S)) for the input value of X. Iteratively steps right or left until it bounds X. At each step which doesn't bound X, the step size is doubled. The routine is careful never to step beyond SMALL or BIG. If it hasn't bounded X at SMALL or BIG, QMFINV returns .FALSE. after setting QLEFT and QHI. If X is successfully bounded then Algorithm R of the paper 'Two Efficient Algorithms with Guaranteed Convergence for Finding a Zero of a Function' by J. C. P. Bus and T. J. Dekker in ACM Transactions on Mathematical Software, Volume 1, No. 4 page 330 (DEC. '75) is employed to find the zero of the function F(X)-Y. This is routine QRZERO. ********************************************************************** */ { E0000(1,NULL,NULL,NULL,NULL,NULL,zabsst,zabsto,zbig,zrelst,zrelto,zsmall, zstpmu); } /* END */ /***=====================================================================***/ static double dlanor(double *x) /* ********************************************************************** double dlanor(double *x) Double precision Logarith of the Asymptotic Normal Function Computes the logarithm of the cumulative normal distribution from abs( x ) to infinity for abs( x ) >= 5. Arguments X --> Value at which cumulative normal to be evaluated DOUBLE PRECISION X Method 23 term expansion of formula 26.2.12 of Abramowitz and Stegun. The relative error at X = 5 is about 0.5E-5. Note ABS(X) must be >= 5 else there is an error stop. ********************************************************************** */ { #define dlsqpi 0.91893853320467274177e0 static double coef[12] = { -1.0e0,3.0e0,-15.0e0,105.0e0,-945.0e0,10395.0e0,-135135.0e0,2027025.0e0, -34459425.0e0,654729075.0e0,-13749310575.e0,316234143225.0e0 }; static int K1 = 12; static double dlanor,approx,correc,xx,xx2,T2; /* .. .. Executable Statements .. */ xx = fabs(*x); if(xx < 5.0e0){ ftnstop("Argument too small in DLANOR"); return 66.6; } approx = -dlsqpi-0.5e0*xx*xx-log(xx); xx2 = xx*xx; T2 = 1.0e0/xx2; correc = devlpl(coef,&K1,&T2)/xx2; correc = dln1px(&correc); dlanor = approx+correc; return dlanor; #undef dlsqpi } /* END */ /***=====================================================================***/ static double dln1mx(double *x) /* ********************************************************************** double dln1mx(double *x) Double precision LN(1-X) Function Returns ln(1-x) for small x (good accuracy if x .le. 0.1). Note that the obvious code of LOG(1.0-X) won't work for small X because 1.0-X loses accuracy Arguments X --> Value for which ln(1-x) is desired. X is DOUBLE PRECISION Method If X > 0.1, the obvious code above is used ELSE The Taylor series for 1-x is expanded to 20 terms. ********************************************************************** */ { static double dln1mx,T1; /* .. .. Executable Statements .. */ T1 = -*x; dln1mx = dln1px(&T1); return dln1mx; } /* END */ /***=====================================================================***/ static double dln1px(double *a) /* ********************************************************************** double dln1px(double *a) Double precision LN(1+X) Function Returns ln(1+x) Note that the obvious code of LOG(1.0+X) won't work for small X because 1.0+X loses accuracy Arguments X --> Value for which ln(1-x) is desired. X is DOUBLE PRECISION Method Renames ALNREL from: DiDinato, A. R. and Morris, A. H. Algorithm 708: Significant Digit Computation of the Incomplete Beta Function Ratios. ACM Trans. Math. Softw. 18 (1993), 360-373. ********************************************************************** ----------------------------------------------------------------------- EVALUATION OF THE FUNCTION LN(1 + A) ----------------------------------------------------------------------- */ { static double p1 = -.129418923021993e+01; static double p2 = .405303492862024e+00; static double p3 = -.178874546012214e-01; static double q1 = -.162752256355323e+01; static double q2 = .747811014037616e+00; static double q3 = -.845104217945565e-01; static double dln1px,t,t2,w,x; /* .. .. Executable Statements .. */ if(fabs(*a) > 0.375e0) goto S10; t = *a/(*a+2.0e0); t2 = t*t; w = (((p3*t2+p2)*t2+p1)*t2+1.0e0)/(((q3*t2+q2)*t2+q1)*t2+1.0e0); dln1px = 2.0e0*t*w; return dln1px; S10: x = 1.e0+*a; dln1px = log(x); return dln1px; } /* END */ /***=====================================================================***/ static double dlnbet(double *a0,double *b0) /* ********************************************************************** double dlnbet(a0,b0) Double precision LN of the complete BETa Function Returns the natural log of the complete beta function, i.e., ln( Gamma(a)*Gamma(b) / Gamma(a+b) Arguments A,B --> The (symmetric) arguments to the complete beta DOUBLE PRECISION A, B Method Renames BETALN from: DiDinato, A. R. and Morris, A. H. Algorithm 708: Significant Digit Computation of the Incomplete Beta Function Ratios. ACM Trans. Math. Softw. 18 (1993), 360-373. ********************************************************************** ----------------------------------------------------------------------- EVALUATION OF THE LOGARITHM OF THE BETA FUNCTION ----------------------------------------------------------------------- E = 0.5*LN(2*PI) -------------------------- */ { static double e = .918938533204673e0; static double dlnbet,a,b,c,h,u,v,w,z; static int i,n; static double T1; /* .. .. Executable Statements .. */ a = fifdmin1(*a0,*b0); b = fifdmax1(*a0,*b0); if(a >= 8.0e0) goto S100; if(a >= 1.0e0) goto S20; /* ----------------------------------------------------------------------- PROCEDURE WHEN A .LT. 1 ----------------------------------------------------------------------- */ if(b >= 8.0e0) goto S10; T1 = a+b; dlnbet = gamln(&a)+(gamln(&b)-gamln(&T1)); return dlnbet; S10: dlnbet = gamln(&a)+algdiv(&a,&b); return dlnbet; S20: /* ----------------------------------------------------------------------- PROCEDURE WHEN 1 .LE. A .LT. 8 ----------------------------------------------------------------------- */ if(a > 2.0e0) goto S40; if(b > 2.0e0) goto S30; dlnbet = gamln(&a)+gamln(&b)-gsumln(&a,&b); return dlnbet; S30: w = 0.0e0; if(b < 8.0e0) goto S60; dlnbet = gamln(&a)+algdiv(&a,&b); return dlnbet; S40: /* REDUCTION OF A WHEN B .LE. 1000 */ if(b > 1000.0e0) goto S80; n = a-1.0e0; w = 1.0e0; for(i=1; i<=n; i++) { a -= 1.0e0; h = a/b; w *= (h/(1.0e0+h)); } w = log(w); if(b < 8.0e0) goto S60; dlnbet = w+gamln(&a)+algdiv(&a,&b); return dlnbet; S60: /* REDUCTION OF B WHEN B .LT. 8 */ n = b-1.0e0; z = 1.0e0; for(i=1; i<=n; i++) { b -= 1.0e0; z *= (b/(a+b)); } dlnbet = w+log(z)+(gamln(&a)+(gamln(&b)-gsumln(&a,&b))); return dlnbet; S80: /* REDUCTION OF A WHEN B .GT. 1000 */ n = a-1.0e0; w = 1.0e0; for(i=1; i<=n; i++) { a -= 1.0e0; w *= (a/(1.0e0+a/b)); } dlnbet = log(w)-(double)n*log(b)+(gamln(&a)+algdiv(&a,&b)); return dlnbet; S100: /* ----------------------------------------------------------------------- PROCEDURE WHEN A .GE. 8 ----------------------------------------------------------------------- */ w = bcorr(&a,&b); h = a/b; c = h/(1.0e0+h); u = -((a-0.5e0)*log(c)); v = b*alnrel(&h); if(u <= v) goto S110; dlnbet = -(0.5e0*log(b))+e+w-v-u; return dlnbet; S110: dlnbet = -(0.5e0*log(b))+e+w-u-v; return dlnbet; } /* END */ /***=====================================================================***/ static double dlngam(double *a) /* ********************************************************************** double dlngam(double *a) Double precision LN of the GAMma function Function Returns the natural logarithm of GAMMA(X). Arguments X --> value at which scaled log gamma is to be returned X is DOUBLE PRECISION Method Renames GAMLN from: DiDinato, A. R. and Morris, A. H. Algorithm 708: Significant Digit Computation of the Incomplete Beta Function Ratios. ACM Trans. Math. Softw. 18 (1993), 360-373. ********************************************************************** ----------------------------------------------------------------------- EVALUATION OF LN(GAMMA(A)) FOR POSITIVE A ----------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS NAVAL SURFACE WARFARE CENTER DAHLGREN, VIRGINIA -------------------------- D = 0.5*(LN(2*PI) - 1) -------------------------- */ { static double c0 = .833333333333333e-01; static double c1 = -.277777777760991e-02; static double c2 = .793650666825390e-03; static double c3 = -.595202931351870e-03; static double c4 = .837308034031215e-03; static double c5 = -.165322962780713e-02; static double d = .418938533204673e0; static double dlngam,t,w; static int i,n; static double T1; /* .. .. Executable Statements .. */ if(*a > 0.8e0) goto S10; dlngam = gamln1(a)-log(*a); return dlngam; S10: if(*a > 2.25e0) goto S20; t = *a-0.5e0-0.5e0; dlngam = gamln1(&t); return dlngam; S20: if(*a >= 10.0e0) goto S40; n = *a-1.25e0; t = *a; w = 1.0e0; for(i=1; i<=n; i++) { t -= 1.0e0; w = t*w; } T1 = t-1.0e0; dlngam = gamln1(&T1)+log(w); return dlngam; S40: t = pow(1.0e0/ *a,2.0); w = (((((c5*t+c4)*t+c3)*t+c2)*t+c1)*t+c0)/ *a; dlngam = d+w+(*a-0.5e0)*(log(*a)-1.0e0); return dlngam; } /* END */ /***=====================================================================***/ static double dstrem(double *z) { /* ********************************************************************** double dstrem(double *z) Double precision Sterling Remainder Function Returns Log(Gamma(Z)) - Sterling(Z) where Sterling(Z) is Sterling's Approximation to Log(Gamma(Z)) Sterling(Z) = LOG( SQRT( 2*PI ) ) + ( Z-0.5 ) * LOG( Z ) - Z Arguments Z --> Value at which Sterling remainder calculated Must be positive. DOUBLE PRECISION Z Method If Z >= 6 uses 9 terms of series in Bernoulli numbers (Values calculated using Maple) Otherwise computes difference explicitly ********************************************************************** */ #define hln2pi 0.91893853320467274178e0 #define ncoef 10 static double coef[ncoef] = { 0.0e0,0.0833333333333333333333333333333e0, -0.00277777777777777777777777777778e0,0.000793650793650793650793650793651e0, -0.000595238095238095238095238095238e0, 0.000841750841750841750841750841751e0,-0.00191752691752691752691752691753e0, 0.00641025641025641025641025641026e0,-0.0295506535947712418300653594771e0, 0.179644372368830573164938490016e0 }; static int K1 = 10; static double dstrem,sterl,T2; /* .. .. Executable Statements .. */ /* For information, here are the next 11 coefficients of the remainder term in Sterling's formula -1.39243221690590111642743221691 13.4028640441683919944789510007 -156.848284626002017306365132452 2193.10333333333333333333333333 -36108.7712537249893571732652192 691472.268851313067108395250776 -0.152382215394074161922833649589D8 0.382900751391414141414141414141D9 -0.108822660357843910890151491655D11 0.347320283765002252252252252252D12 -0.123696021422692744542517103493D14 */ if(*z <= 0.0e0){ ftnstop("nonpositive argument in DSTREM"); return 66.6; } if(!(*z > 6.0e0)) goto S10; T2 = 1.0e0/pow(*z,2.0); dstrem = devlpl(coef,&K1,&T2)**z; goto S20; S10: sterl = hln2pi+(*z-0.5e0)*log(*z)-*z; dstrem = dlngam(z)-sterl; S20: return dstrem; #undef hln2pi #undef ncoef } /* END */ /***=====================================================================***/ static double dt1(double *p,double *q,double *df) /* ********************************************************************** double dt1(double *p,double *q,double *df) Double precision Initalize Approximation to INVerse of the cumulative T distribution Function Returns the inverse of the T distribution function, i.e., the integral from 0 to INVT of the T density is P. This is an initial approximation Arguments P --> The p-value whose inverse from the T distribution is desired. P is DOUBLE PRECISION Q --> 1-P. Q is DOUBLE PRECISION DF --> Degrees of freedom of the T distribution. DF is DOUBLE PRECISION ********************************************************************** */ { static double coef[4][5] = { 1.0e0,1.0e0,0.0e0,0.0e0,0.0e0,3.0e0,16.0e0,5.0e0,0.0e0,0.0e0,-15.0e0,17.0e0, 19.0e0,3.0e0,0.0e0,-945.0e0,-1920.0e0,1482.0e0,776.0e0,79.0e0 }; static double denom[4] = { 4.0e0,96.0e0,384.0e0,92160.0e0 }; static int ideg[4] = { 2,3,4,5 }; static double dt1,denpow,sum,term,x,xp,xx; static int i; /* .. .. Executable Statements .. */ x = fabs(dinvnr(p,q)); xx = x*x; sum = x; denpow = 1.0e0; for(i=0; i<4; i++) { term = devlpl(&coef[i][0],&ideg[i],&xx)*x; denpow *= *df; sum += (term/(denpow*denom[i])); } if(!(*p >= 0.5e0)) goto S20; xp = sum; goto S30; S20: xp = -sum; S30: dt1 = xp; return dt1; } /* END */ /***=====================================================================***/ static void E0001(int IENTRY,int *status,double *x,double *fx, double *xlo,double *xhi,unsigned long *qleft, unsigned long *qhi,double *zabstl,double *zreltl, double *zxhi,double *zxlo) { #define ftol(zx) (0.5e0*fifdmax1(abstol,reltol*fabs((zx)))) static double a,abstol,b,c,d,fa,fb,fc,fd,fda,fdb,m,mb,p,q,reltol,tol,w,xxhi,xxlo; static int ext,i99999; static unsigned long first,qrzero; switch(IENTRY){case 0: goto DZROR; case 1: goto DSTZR;} DZROR: if(*status > 0) goto S280; *xlo = xxlo; *xhi = xxhi; b = *x = *xlo; /* GET-FUNCTION-VALUE */ i99999 = 1; goto S270; S10: fb = *fx; *xlo = *xhi; a = *x = *xlo; /* GET-FUNCTION-VALUE */ i99999 = 2; goto S270; S20: /* Check that F(ZXLO) < 0 < F(ZXHI) or F(ZXLO) > 0 > F(ZXHI) */ if(!(fb < 0.0e0)) goto S40; if(!(*fx < 0.0e0)) goto S30; *status = -1; *qleft = *fx < fb; *qhi = 0; return; S40: S30: if(!(fb > 0.0e0)) goto S60; if(!(*fx > 0.0e0)) goto S50; *status = -1; *qleft = *fx > fb; *qhi = 1; return; S60: S50: fa = *fx; first = 1; S70: c = a; fc = fa; ext = 0; S80: if(!(fabs(fc) < fabs(fb))) goto S100; if(!(c != a)) goto S90; d = a; fd = fa; S90: a = b; fa = fb; *xlo = c; b = *xlo; fb = fc; c = a; fc = fa; S100: tol = ftol(*xlo); m = (c+b)*.5e0; mb = m-b; if(!(fabs(mb) > tol)) goto S240; if(!(ext > 3)) goto S110; w = mb; goto S190; S110: tol = fifdsign(tol,mb); p = (b-a)*fb; if(!first) goto S120; q = fa-fb; first = 0; goto S130; S120: fdb = (fd-fb)/(d-b); fda = (fd-fa)/(d-a); p = fda*p; q = fdb*fa-fda*fb; S130: if(!(p < 0.0e0)) goto S140; p = -p; q = -q; S140: if(ext == 3) p *= 2.0e0; if(!(p*1.0e0 == 0.0e0 || p <= q*tol)) goto S150; w = tol; goto S180; S150: if(!(p < mb*q)) goto S160; w = p/q; goto S170; S160: w = mb; S190: S180: S170: d = a; fd = fa; a = b; fa = fb; b += w; *xlo = b; *x = *xlo; /* GET-FUNCTION-VALUE */ i99999 = 3; goto S270; S200: fb = *fx; if(!(fc*fb >= 0.0e0)) goto S210; goto S70; S210: if(!(w == mb)) goto S220; ext = 0; goto S230; S220: ext += 1; S230: goto S80; S240: *xhi = c; qrzero = fc >= 0.0e0 && fb <= 0.0e0 || fc < 0.0e0 && fb >= 0.0e0; if(!qrzero) goto S250; *status = 0; goto S260; S250: *status = -1; S260: return; DSTZR: xxlo = *zxlo; xxhi = *zxhi; abstol = *zabstl; reltol = *zreltl; return; S270: /* TO GET-FUNCTION-VALUE */ *status = 1; return; S280: switch((int)i99999){case 1: goto S10;case 2: goto S20;case 3: goto S200; default: break;} #undef ftol } /* END */ /***=====================================================================***/ static void dzror(int *status,double *x,double *fx,double *xlo, double *xhi,unsigned long *qleft,unsigned long *qhi) /* ********************************************************************** void dzror(int *status,double *x,double *fx,double *xlo, double *xhi,unsigned long *qleft,unsigned long *qhi) Double precision ZeRo of a function -- Reverse Communication Function Performs the zero finding. STZROR must have been called before this routine in order to set its parameters. Arguments STATUS <--> At the beginning of a zero finding problem, STATUS should be set to 0 and ZROR invoked. (The value of other parameters will be ignored on this call.) When ZROR needs the function evaluated, it will set STATUS to 1 and return. The value of the function should be set in FX and ZROR again called without changing any of its other parameters. When ZROR has finished without error, it will return with STATUS 0. In that case (XLO,XHI) bound the answe If ZROR finds an error (which implies that F(XLO)-Y an F(XHI)-Y have the same sign, it returns STATUS -1. In this case, XLO and XHI are undefined. INTEGER STATUS X <-- The value of X at which F(X) is to be evaluated. DOUBLE PRECISION X FX --> The value of F(X) calculated when ZROR returns with STATUS = 1. DOUBLE PRECISION FX XLO <-- When ZROR returns with STATUS = 0, XLO bounds the inverval in X containing the solution below. DOUBLE PRECISION XLO XHI <-- When ZROR returns with STATUS = 0, XHI bounds the inverval in X containing the solution above. DOUBLE PRECISION XHI QLEFT <-- .TRUE. if the stepping search terminated unsucessfully at XLO. If it is .FALSE. the search terminated unsucessfully at XHI. QLEFT is LOGICAL QHI <-- .TRUE. if F(X) .GT. Y at the termination of the search and .FALSE. if F(X) .LT. Y at the termination of the search. QHI is LOGICAL ********************************************************************** */ { E0001(0,status,x,fx,xlo,xhi,qleft,qhi,NULL,NULL,NULL,NULL); } /* END */ /***=====================================================================***/ static void dstzr(double *zxlo,double *zxhi,double *zabstl,double *zreltl) /* ********************************************************************** void dstzr(double *zxlo,double *zxhi,double *zabstl,double *zreltl) Double precision SeT ZeRo finder - Reverse communication version Function Sets quantities needed by ZROR. The function of ZROR and the quantities set is given here. Concise Description - Given a function F find XLO such that F(XLO) = 0. More Precise Description - Input condition. F is a double precision function of a single double precision argument and XLO and XHI are such that F(XLO)*F(XHI) .LE. 0.0 If the input condition is met, QRZERO returns .TRUE. and output values of XLO and XHI satisfy the following F(XLO)*F(XHI) .LE. 0. ABS(F(XLO) .LE. ABS(F(XHI) ABS(XLO-XHI) .LE. TOL(X) where TOL(X) = MAX(ABSTOL,RELTOL*ABS(X)) If this algorithm does not find XLO and XHI satisfying these conditions then QRZERO returns .FALSE. This implies that the input condition was not met. Arguments XLO --> The left endpoint of the interval to be searched for a solution. XLO is DOUBLE PRECISION XHI --> The right endpoint of the interval to be for a solution. XHI is DOUBLE PRECISION ABSTOL, RELTOL --> Two numbers that determine the accuracy of the solution. See function for a precise definition. ABSTOL is DOUBLE PRECISION RELTOL is DOUBLE PRECISION Method Algorithm R of the paper 'Two Efficient Algorithms with Guaranteed Convergence for Finding a Zero of a Function' by J. C. P. Bus and T. J. Dekker in ACM Transactions on Mathematical Software, Volume 1, no. 4 page 330 (Dec. '75) is employed to find the zero of F(X)-Y. ********************************************************************** */ { E0001(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,zabstl,zreltl,zxhi,zxlo); } /* END */ /***=====================================================================***/ static double erf1(double *x) /* ----------------------------------------------------------------------- EVALUATION OF THE REAL ERROR FUNCTION ----------------------------------------------------------------------- */ { static double c = .564189583547756e0; static double a[5] = { .771058495001320e-04,-.133733772997339e-02,.323076579225834e-01, .479137145607681e-01,.128379167095513e+00 }; static double b[3] = { .301048631703895e-02,.538971687740286e-01,.375795757275549e+00 }; static double p[8] = { -1.36864857382717e-07,5.64195517478974e-01,7.21175825088309e+00, 4.31622272220567e+01,1.52989285046940e+02,3.39320816734344e+02, 4.51918953711873e+02,3.00459261020162e+02 }; static double q[8] = { 1.00000000000000e+00,1.27827273196294e+01,7.70001529352295e+01, 2.77585444743988e+02,6.38980264465631e+02,9.31354094850610e+02, 7.90950925327898e+02,3.00459260956983e+02 }; static double r[5] = { 2.10144126479064e+00,2.62370141675169e+01,2.13688200555087e+01, 4.65807828718470e+00,2.82094791773523e-01 }; static double s[4] = { 9.41537750555460e+01,1.87114811799590e+02,9.90191814623914e+01, 1.80124575948747e+01 }; static double erf1,ax,bot,t,top,x2; /* .. .. Executable Statements .. */ ax = fabs(*x); if(ax > 0.5e0) goto S10; t = *x**x; top = (((a[0]*t+a[1])*t+a[2])*t+a[3])*t+a[4]+1.0e0; bot = ((b[0]*t+b[1])*t+b[2])*t+1.0e0; erf1 = *x*(top/bot); return erf1; S10: if(ax > 4.0e0) goto S20; top = ((((((p[0]*ax+p[1])*ax+p[2])*ax+p[3])*ax+p[4])*ax+p[5])*ax+p[6])*ax+p[ 7]; bot = ((((((q[0]*ax+q[1])*ax+q[2])*ax+q[3])*ax+q[4])*ax+q[5])*ax+q[6])*ax+q[ 7]; erf1 = 0.5e0+(0.5e0-exp(-(*x**x))*top/bot); if(*x < 0.0e0) erf1 = -erf1; return erf1; S20: if(ax >= 5.8e0) goto S30; x2 = *x**x; t = 1.0e0/x2; top = (((r[0]*t+r[1])*t+r[2])*t+r[3])*t+r[4]; bot = (((s[0]*t+s[1])*t+s[2])*t+s[3])*t+1.0e0; erf1 = (c-top/(x2*bot))/ax; erf1 = 0.5e0+(0.5e0-exp(-x2)*erf1); if(*x < 0.0e0) erf1 = -erf1; return erf1; S30: erf1 = fifdsign(1.0e0,*x); return erf1; } /* END */ /***=====================================================================***/ static double erfc1(int *ind,double *x) /* ----------------------------------------------------------------------- EVALUATION OF THE COMPLEMENTARY ERROR FUNCTION ERFC1(IND,X) = ERFC(X) IF IND = 0 ERFC1(IND,X) = EXP(X*X)*ERFC(X) OTHERWISE ----------------------------------------------------------------------- */ { static double c = .564189583547756e0; static double a[5] = { .771058495001320e-04,-.133733772997339e-02,.323076579225834e-01, .479137145607681e-01,.128379167095513e+00 }; static double b[3] = { .301048631703895e-02,.538971687740286e-01,.375795757275549e+00 }; static double p[8] = { -1.36864857382717e-07,5.64195517478974e-01,7.21175825088309e+00, 4.31622272220567e+01,1.52989285046940e+02,3.39320816734344e+02, 4.51918953711873e+02,3.00459261020162e+02 }; static double q[8] = { 1.00000000000000e+00,1.27827273196294e+01,7.70001529352295e+01, 2.77585444743988e+02,6.38980264465631e+02,9.31354094850610e+02, 7.90950925327898e+02,3.00459260956983e+02 }; static double r[5] = { 2.10144126479064e+00,2.62370141675169e+01,2.13688200555087e+01, 4.65807828718470e+00,2.82094791773523e-01 }; static double s[4] = { 9.41537750555460e+01,1.87114811799590e+02,9.90191814623914e+01, 1.80124575948747e+01 }; static int K1 = 1; static double erfc1,ax,bot,e,t,top,w; /* .. .. Executable Statements .. */ /* ABS(X) .LE. 0.5 */ ax = fabs(*x); if(ax > 0.5e0) goto S10; t = *x**x; top = (((a[0]*t+a[1])*t+a[2])*t+a[3])*t+a[4]+1.0e0; bot = ((b[0]*t+b[1])*t+b[2])*t+1.0e0; erfc1 = 0.5e0+(0.5e0-*x*(top/bot)); if(*ind != 0) erfc1 = exp(t)*erfc1; return erfc1; S10: /* 0.5 .LT. ABS(X) .LE. 4 */ if(ax > 4.0e0) goto S20; top = ((((((p[0]*ax+p[1])*ax+p[2])*ax+p[3])*ax+p[4])*ax+p[5])*ax+p[6])*ax+p[ 7]; bot = ((((((q[0]*ax+q[1])*ax+q[2])*ax+q[3])*ax+q[4])*ax+q[5])*ax+q[6])*ax+q[ 7]; erfc1 = top/bot; goto S40; S20: /* ABS(X) .GT. 4 */ if(*x <= -5.6e0) goto S60; if(*ind != 0) goto S30; if(*x > 100.0e0) goto S70; if(*x**x > -exparg(&K1)) goto S70; S30: t = pow(1.0e0/ *x,2.0); top = (((r[0]*t+r[1])*t+r[2])*t+r[3])*t+r[4]; bot = (((s[0]*t+s[1])*t+s[2])*t+s[3])*t+1.0e0; erfc1 = (c-t*top/bot)/ax; S40: /* FINAL ASSEMBLY */ if(*ind == 0) goto S50; if(*x < 0.0e0) erfc1 = 2.0e0*exp(*x**x)-erfc1; return erfc1; S50: w = *x**x; t = w; e = w-t; erfc1 = (0.5e0+(0.5e0-e))*exp(-t)*erfc1; if(*x < 0.0e0) erfc1 = 2.0e0-erfc1; return erfc1; S60: /* LIMIT VALUE FOR LARGE NEGATIVE X */ erfc1 = 2.0e0; if(*ind != 0) erfc1 = 2.0e0*exp(*x**x); return erfc1; S70: /* LIMIT VALUE FOR LARGE POSITIVE X WHEN IND = 0 */ erfc1 = 0.0e0; return erfc1; } /* END */ /***=====================================================================***/ static double esum(int *mu,double *x) /* ----------------------------------------------------------------------- EVALUATION OF EXP(MU + X) ----------------------------------------------------------------------- */ { static double esum,w; /* .. .. Executable Statements .. */ if(*x > 0.0e0) goto S10; if(*mu < 0) goto S20; w = (double)*mu+*x; if(w > 0.0e0) goto S20; esum = exp(w); return esum; S10: if(*mu > 0) goto S20; w = (double)*mu+*x; if(w < 0.0e0) goto S20; esum = exp(w); return esum; S20: w = *mu; esum = exp(w)*exp(*x); return esum; } /* END */ /***=====================================================================***/ static double exparg(int *l) /* -------------------------------------------------------------------- IF L = 0 THEN EXPARG(L) = THE LARGEST POSITIVE W FOR WHICH EXP(W) CAN BE COMPUTED. IF L IS NONZERO THEN EXPARG(L) = THE LARGEST NEGATIVE W FOR WHICH THE COMPUTED VALUE OF EXP(W) IS NONZERO. NOTE... ONLY AN APPROXIMATE VALUE FOR EXPARG(L) IS NEEDED. -------------------------------------------------------------------- */ { static int K1 = 4; static int K2 = 9; static int K3 = 10; static double exparg,lnb; static int b,m; /* .. .. Executable Statements .. */ b = ipmpar(&K1); if(b != 2) goto S10; lnb = .69314718055995e0; goto S40; S10: if(b != 8) goto S20; lnb = 2.0794415416798e0; goto S40; S20: if(b != 16) goto S30; lnb = 2.7725887222398e0; goto S40; S30: lnb = log((double)b); S40: if(*l == 0) goto S50; m = ipmpar(&K2)-1; exparg = 0.99999e0*((double)m*lnb); return exparg; S50: m = ipmpar(&K3); exparg = 0.99999e0*((double)m*lnb); return exparg; } /* END */ /***=====================================================================***/ static double fpser(double *a,double *b,double *x,double *eps) /* ----------------------------------------------------------------------- EVALUATION OF I (A,B) X FOR B .LT. MIN(EPS,EPS*A) AND X .LE. 0.5. ----------------------------------------------------------------------- SET FPSER = X**A */ { static int K1 = 1; static double fpser,an,c,s,t,tol; /* .. .. Executable Statements .. */ fpser = 1.0e0; if(*a <= 1.e-3**eps) goto S10; fpser = 0.0e0; t = *a*log(*x); if(t < exparg(&K1)) return fpser; fpser = exp(t); S10: /* NOTE THAT 1/B(A,B) = B */ fpser = *b/ *a*fpser; tol = *eps/ *a; an = *a+1.0e0; t = *x; s = t/an; S20: an += 1.0e0; t = *x*t; c = t/an; s += c; if(fabs(c) > tol) goto S20; fpser *= (1.0e0+*a*s); return fpser; } /* END */ /***=====================================================================***/ static double gam1(double *a) /* ------------------------------------------------------------------ COMPUTATION OF 1/GAMMA(A+1) - 1 FOR -0.5 .LE. A .LE. 1.5 ------------------------------------------------------------------ */ { static double s1 = .273076135303957e+00; static double s2 = .559398236957378e-01; static double p[7] = { .577215664901533e+00,-.409078193005776e+00,-.230975380857675e+00, .597275330452234e-01,.766968181649490e-02,-.514889771323592e-02, .589597428611429e-03 }; static double q[5] = { .100000000000000e+01,.427569613095214e+00,.158451672430138e+00, .261132021441447e-01,.423244297896961e-02 }; static double r[9] = { -.422784335098468e+00,-.771330383816272e+00,-.244757765222226e+00, .118378989872749e+00,.930357293360349e-03,-.118290993445146e-01, .223047661158249e-02,.266505979058923e-03,-.132674909766242e-03 }; static double gam1,bot,d,t,top,w,T1; /* .. .. Executable Statements .. */ t = *a; d = *a-0.5e0; if(d > 0.0e0) t = d-0.5e0; T1 = t; if(T1 < 0) goto S40; else if(T1 == 0) goto S10; else goto S20; S10: gam1 = 0.0e0; return gam1; S20: top = (((((p[6]*t+p[5])*t+p[4])*t+p[3])*t+p[2])*t+p[1])*t+p[0]; bot = (((q[4]*t+q[3])*t+q[2])*t+q[1])*t+1.0e0; w = top/bot; if(d > 0.0e0) goto S30; gam1 = *a*w; return gam1; S30: gam1 = t/ *a*(w-0.5e0-0.5e0); return gam1; S40: top = (((((((r[8]*t+r[7])*t+r[6])*t+r[5])*t+r[4])*t+r[3])*t+r[2])*t+r[1])*t+ r[0]; bot = (s2*t+s1)*t+1.0e0; w = top/bot; if(d > 0.0e0) goto S50; gam1 = *a*(w+0.5e0+0.5e0); return gam1; S50: gam1 = t*w/ *a; return gam1; } /* END */ /***=====================================================================***/ static void gaminv(double *a,double *x,double *x0,double *p,double *q, int *ierr) /* ---------------------------------------------------------------------- INVERSE INCOMPLETE GAMMA RATIO FUNCTION GIVEN POSITIVE A, AND NONEGATIVE P AND Q WHERE P + Q = 1. THEN X IS COMPUTED WHERE P(A,X) = P AND Q(A,X) = Q. SCHRODER ITERATION IS EMPLOYED. THE ROUTINE ATTEMPTS TO COMPUTE X TO 10 SIGNIFICANT DIGITS IF THIS IS POSSIBLE FOR THE PARTICULAR COMPUTER ARITHMETIC BEING USED. ------------ X IS A VARIABLE. IF P = 0 THEN X IS ASSIGNED THE VALUE 0, AND IF Q = 0 THEN X IS SET TO THE LARGEST FLOATING POINT NUMBER AVAILABLE. OTHERWISE, GAMINV ATTEMPTS TO OBTAIN A SOLUTION FOR P(A,X) = P AND Q(A,X) = Q. IF THE ROUTINE IS SUCCESSFUL THEN THE SOLUTION IS STORED IN X. X0 IS AN OPTIONAL INITIAL APPROXIMATION FOR X. IF THE USER DOES NOT WISH TO SUPPLY AN INITIAL APPROXIMATION, THEN SET X0 .LE. 0. IERR IS A VARIABLE THAT REPORTS THE STATUS OF THE RESULTS. WHEN THE ROUTINE TERMINATES, IERR HAS ONE OF THE FOLLOWING VALUES ... IERR = 0 THE SOLUTION WAS OBTAINED. ITERATION WAS NOT USED. IERR.GT.0 THE SOLUTION WAS OBTAINED. IERR ITERATIONS WERE PERFORMED. IERR = -2 (INPUT ERROR) A .LE. 0 IERR = -3 NO SOLUTION WAS OBTAINED. THE RATIO Q/A IS TOO LARGE. IERR = -4 (INPUT ERROR) P + Q .NE. 1 IERR = -6 20 ITERATIONS WERE PERFORMED. THE MOST RECENT VALUE OBTAINED FOR X IS GIVEN. THIS CANNOT OCCUR IF X0 .LE. 0. IERR = -7 ITERATION FAILED. NO VALUE IS GIVEN FOR X. THIS MAY OCCUR WHEN X IS APPROXIMATELY 0. IERR = -8 A VALUE FOR X HAS BEEN OBTAINED, BUT THE ROUTINE IS NOT CERTAIN OF ITS ACCURACY. ITERATION CANNOT BE PERFORMED IN THIS CASE. IF X0 .LE. 0, THIS CAN OCCUR ONLY WHEN P OR Q IS APPROXIMATELY 0. IF X0 IS POSITIVE THEN THIS CAN OCCUR WHEN A IS EXCEEDINGLY CLOSE TO X AND A IS EXTREMELY LARGE (SAY A .GE. 1.E20). ---------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS, JR. NAVAL SURFACE WEAPONS CENTER DAHLGREN, VIRGINIA ------------------- */ { static double a0 = 3.31125922108741e0; static double a1 = 11.6616720288968e0; static double a2 = 4.28342155967104e0; static double a3 = .213623493715853e0; static double b1 = 6.61053765625462e0; static double b2 = 6.40691597760039e0; static double b3 = 1.27364489782223e0; static double b4 = .036117081018842e0; static double c = .577215664901533e0; static double ln10 = 2.302585e0; static double tol = 1.e-5; static double amin[2] = { 500.0e0,100.0e0 }; static double bmin[2] = { 1.e-28,1.e-13 }; static double dmin[2] = { 1.e-06,1.e-04 }; static double emin[2] = { 2.e-03,6.e-03 }; static double eps0[2] = { 1.e-10,1.e-08 }; static int K1 = 1; static int K2 = 2; static int K3 = 3; static int K8 = 0; static double am1,amax,ap1,ap2,ap3,apn,b,c1,c2,c3,c4,c5,d,e,e2,eps,g,h,pn,qg,qn, r,rta,s,s2,sum,t,u,w,xmax,xmin,xn,y,z; static int iop; static double T4,T5,T6,T7,T9; /* .. .. Executable Statements .. */ /* ****** E, XMIN, AND XMAX ARE MACHINE DEPENDENT CONSTANTS. E IS THE SMALLEST NUMBER FOR WHICH 1.0 + E .GT. 1.0. XMIN IS THE SMALLEST POSITIVE NUMBER AND XMAX IS THE LARGEST POSITIVE NUMBER. */ e = spmpar(&K1); xmin = spmpar(&K2); xmax = spmpar(&K3); *x = 0.0e0; if(*a <= 0.0e0) goto S300; t = *p+*q-1.e0; if(fabs(t) > e) goto S320; *ierr = 0; if(*p == 0.0e0) return; if(*q == 0.0e0) goto S270; if(*a == 1.0e0) goto S280; e2 = 2.0e0*e; amax = 0.4e-10/(e*e); iop = 1; if(e > 1.e-10) iop = 2; eps = eps0[iop-1]; xn = *x0; if(*x0 > 0.0e0) goto S160; /* SELECTION OF THE INITIAL APPROXIMATION XN OF X WHEN A .LT. 1 */ if(*a > 1.0e0) goto S80; T4 = *a+1.0e0; g = Xgamm(&T4); qg = *q*g; if(qg == 0.0e0) goto S360; b = qg/ *a; if(qg > 0.6e0**a) goto S40; if(*a >= 0.30e0 || b < 0.35e0) goto S10; t = exp(-(b+c)); u = t*exp(t); xn = t*exp(u); goto S160; S10: if(b >= 0.45e0) goto S40; if(b == 0.0e0) goto S360; y = -log(b); s = 0.5e0+(0.5e0-*a); z = log(y); t = y-s*z; if(b < 0.15e0) goto S20; xn = y-s*log(t)-log(1.0e0+s/(t+1.0e0)); goto S220; S20: if(b <= 0.01e0) goto S30; u = ((t+2.0e0*(3.0e0-*a))*t+(2.0e0-*a)*(3.0e0-*a))/((t+(5.0e0-*a))*t+2.0e0); xn = y-s*log(t)-log(u); goto S220; S30: c1 = -(s*z); c2 = -(s*(1.0e0+c1)); c3 = s*((0.5e0*c1+(2.0e0-*a))*c1+(2.5e0-1.5e0**a)); c4 = -(s*(((c1/3.0e0+(2.5e0-1.5e0**a))*c1+((*a-6.0e0)**a+7.0e0))*c1+( (11.0e0**a-46.0)**a+47.0e0)/6.0e0)); c5 = -(s*((((-(c1/4.0e0)+(11.0e0**a-17.0e0)/6.0e0)*c1+((-(3.0e0**a)+13.0e0)* *a-13.0e0))*c1+0.5e0*(((2.0e0**a-25.0e0)**a+72.0e0)**a-61.0e0))*c1+(( (25.0e0**a-195.0e0)**a+477.0e0)**a-379.0e0)/12.0e0)); xn = (((c5/y+c4)/y+c3)/y+c2)/y+c1+y; if(*a > 1.0e0) goto S220; if(b > bmin[iop-1]) goto S220; *x = xn; return; S40: if(b**q > 1.e-8) goto S50; xn = exp(-(*q/ *a+c)); goto S70; S50: if(*p <= 0.9e0) goto S60; T5 = -*q; xn = exp((alnrel(&T5)+gamln1(a))/ *a); goto S70; S60: xn = exp(log(*p*g)/ *a); S70: if(xn == 0.0e0) goto S310; t = 0.5e0+(0.5e0-xn/(*a+1.0e0)); xn /= t; goto S160; S80: /* SELECTION OF THE INITIAL APPROXIMATION XN OF X WHEN A .GT. 1 */ if(*q <= 0.5e0) goto S90; w = log(*p); goto S100; S90: w = log(*q); S100: t = sqrt(-(2.0e0*w)); s = t-(((a3*t+a2)*t+a1)*t+a0)/((((b4*t+b3)*t+b2)*t+b1)*t+1.0e0); if(*q > 0.5e0) s = -s; rta = sqrt(*a); s2 = s*s; xn = *a+s*rta+(s2-1.0e0)/3.0e0+s*(s2-7.0e0)/(36.0e0*rta)-((3.0e0*s2+7.0e0)* s2-16.0e0)/(810.0e0**a)+s*((9.0e0*s2+256.0e0)*s2-433.0e0)/(38880.0e0**a* rta); xn = fifdmax1(xn,0.0e0); if(*a < amin[iop-1]) goto S110; *x = xn; d = 0.5e0+(0.5e0-*x/ *a); if(fabs(d) <= dmin[iop-1]) return; S110: if(*p <= 0.5e0) goto S130; if(xn < 3.0e0**a) goto S220; y = -(w+gamln(a)); d = fifdmax1(2.0e0,*a*(*a-1.0e0)); if(y < ln10*d) goto S120; s = 1.0e0-*a; z = log(y); goto S30; S120: t = *a-1.0e0; T6 = -(t/(xn+1.0e0)); xn = y+t*log(xn)-alnrel(&T6); T7 = -(t/(xn+1.0e0)); xn = y+t*log(xn)-alnrel(&T7); goto S220; S130: ap1 = *a+1.0e0; if(xn > 0.70e0*ap1) goto S170; w += gamln(&ap1); if(xn > 0.15e0*ap1) goto S140; ap2 = *a+2.0e0; ap3 = *a+3.0e0; *x = exp((w+*x)/ *a); *x = exp((w+*x-log(1.0e0+*x/ap1*(1.0e0+*x/ap2)))/ *a); *x = exp((w+*x-log(1.0e0+*x/ap1*(1.0e0+*x/ap2)))/ *a); *x = exp((w+*x-log(1.0e0+*x/ap1*(1.0e0+*x/ap2*(1.0e0+*x/ap3))))/ *a); xn = *x; if(xn > 1.e-2*ap1) goto S140; if(xn <= emin[iop-1]*ap1) return; goto S170; S140: apn = ap1; t = xn/apn; sum = 1.0e0+t; S150: apn += 1.0e0; t *= (xn/apn); sum += t; if(t > 1.e-4) goto S150; t = w-log(sum); xn = exp((xn+t)/ *a); xn *= (1.0e0-(*a*log(xn)-xn-t)/(*a-xn)); goto S170; S160: /* SCHRODER ITERATION USING P */ if(*p > 0.5e0) goto S220; S170: if(*p <= 1.e10*xmin) goto S350; am1 = *a-0.5e0-0.5e0; S180: if(*a <= amax) goto S190; d = 0.5e0+(0.5e0-xn/ *a); if(fabs(d) <= e2) goto S350; S190: if(*ierr >= 20) goto S330; *ierr += 1; gratio(a,&xn,&pn,&qn,&K8); if(pn == 0.0e0 || qn == 0.0e0) goto S350; r = rcomp(a,&xn); if(r == 0.0e0) goto S350; t = (pn-*p)/r; w = 0.5e0*(am1-xn); if(fabs(t) <= 0.1e0 && fabs(w*t) <= 0.1e0) goto S200; *x = xn*(1.0e0-t); if(*x <= 0.0e0) goto S340; d = fabs(t); goto S210; S200: h = t*(1.0e0+w*t); *x = xn*(1.0e0-h); if(*x <= 0.0e0) goto S340; if(fabs(w) >= 1.0e0 && fabs(w)*t*t <= eps) return; d = fabs(h); S210: xn = *x; if(d > tol) goto S180; if(d <= eps) return; if(fabs(*p-pn) <= tol**p) return; goto S180; S220: /* SCHRODER ITERATION USING Q */ if(*q <= 1.e10*xmin) goto S350; am1 = *a-0.5e0-0.5e0; S230: if(*a <= amax) goto S240; d = 0.5e0+(0.5e0-xn/ *a); if(fabs(d) <= e2) goto S350; S240: if(*ierr >= 20) goto S330; *ierr += 1; gratio(a,&xn,&pn,&qn,&K8); if(pn == 0.0e0 || qn == 0.0e0) goto S350; r = rcomp(a,&xn); if(r == 0.0e0) goto S350; t = (*q-qn)/r; w = 0.5e0*(am1-xn); if(fabs(t) <= 0.1e0 && fabs(w*t) <= 0.1e0) goto S250; *x = xn*(1.0e0-t); if(*x <= 0.0e0) goto S340; d = fabs(t); goto S260; S250: h = t*(1.0e0+w*t); *x = xn*(1.0e0-h); if(*x <= 0.0e0) goto S340; if(fabs(w) >= 1.0e0 && fabs(w)*t*t <= eps) return; d = fabs(h); S260: xn = *x; if(d > tol) goto S230; if(d <= eps) return; if(fabs(*q-qn) <= tol**q) return; goto S230; S270: /* SPECIAL CASES */ *x = xmax; return; S280: if(*q < 0.9e0) goto S290; T9 = -*p; *x = -alnrel(&T9); return; S290: *x = -log(*q); return; S300: /* ERROR RETURN */ *ierr = -2; return; S310: *ierr = -3; return; S320: *ierr = -4; return; S330: *ierr = -6; return; S340: *ierr = -7; return; S350: *x = xn; *ierr = -8; return; S360: *x = xmax; *ierr = -8; return; } /* END */ /***=====================================================================***/ static double gamln(double *a) /* ----------------------------------------------------------------------- EVALUATION OF LN(GAMMA(A)) FOR POSITIVE A ----------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS NAVAL SURFACE WARFARE CENTER DAHLGREN, VIRGINIA -------------------------- D = 0.5*(LN(2*PI) - 1) -------------------------- */ { static double c0 = .833333333333333e-01; static double c1 = -.277777777760991e-02; static double c2 = .793650666825390e-03; static double c3 = -.595202931351870e-03; static double c4 = .837308034031215e-03; static double c5 = -.165322962780713e-02; static double d = .418938533204673e0; static double gamln,t,w; static int i,n; static double T1; /* .. .. Executable Statements .. */ if(*a > 0.8e0) goto S10; gamln = gamln1(a)-log(*a); return gamln; S10: if(*a > 2.25e0) goto S20; t = *a-0.5e0-0.5e0; gamln = gamln1(&t); return gamln; S20: if(*a >= 10.0e0) goto S40; n = *a-1.25e0; t = *a; w = 1.0e0; for(i=1; i<=n; i++) { t -= 1.0e0; w = t*w; } T1 = t-1.0e0; gamln = gamln1(&T1)+log(w); return gamln; S40: t = pow(1.0e0/ *a,2.0); w = (((((c5*t+c4)*t+c3)*t+c2)*t+c1)*t+c0)/ *a; gamln = d+w+(*a-0.5e0)*(log(*a)-1.0e0); return gamln; } /* END */ /***=====================================================================***/ static double gamln1(double *a) /* ----------------------------------------------------------------------- EVALUATION OF LN(GAMMA(1 + A)) FOR -0.2 .LE. A .LE. 1.25 ----------------------------------------------------------------------- */ { static double p0 = .577215664901533e+00; static double p1 = .844203922187225e+00; static double p2 = -.168860593646662e+00; static double p3 = -.780427615533591e+00; static double p4 = -.402055799310489e+00; static double p5 = -.673562214325671e-01; static double p6 = -.271935708322958e-02; static double q1 = .288743195473681e+01; static double q2 = .312755088914843e+01; static double q3 = .156875193295039e+01; static double q4 = .361951990101499e+00; static double q5 = .325038868253937e-01; static double q6 = .667465618796164e-03; static double r0 = .422784335098467e+00; static double r1 = .848044614534529e+00; static double r2 = .565221050691933e+00; static double r3 = .156513060486551e+00; static double r4 = .170502484022650e-01; static double r5 = .497958207639485e-03; static double s1 = .124313399877507e+01; static double s2 = .548042109832463e+00; static double s3 = .101552187439830e+00; static double s4 = .713309612391000e-02; static double s5 = .116165475989616e-03; static double gamln1,w,x; /* .. .. Executable Statements .. */ if(*a >= 0.6e0) goto S10; w = ((((((p6**a+p5)**a+p4)**a+p3)**a+p2)**a+p1)**a+p0)/((((((q6**a+q5)**a+ q4)**a+q3)**a+q2)**a+q1)**a+1.0e0); gamln1 = -(*a*w); return gamln1; S10: x = *a-0.5e0-0.5e0; w = (((((r5*x+r4)*x+r3)*x+r2)*x+r1)*x+r0)/(((((s5*x+s4)*x+s3)*x+s2)*x+s1)*x +1.0e0); gamln1 = x*w; return gamln1; } /* END */ /***=====================================================================***/ static double Xgamm(double *a) /* ----------------------------------------------------------------------- EVALUATION OF THE GAMMA FUNCTION FOR REAL ARGUMENTS ----------- GAMMA(A) IS ASSIGNED THE VALUE 0 WHEN THE GAMMA FUNCTION CANNOT BE COMPUTED. ----------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS, JR. NAVAL SURFACE WEAPONS CENTER DAHLGREN, VIRGINIA ----------------------------------------------------------------------- */ { static double d = .41893853320467274178e0; static double pi = 3.1415926535898e0; static double r1 = .820756370353826e-03; static double r2 = -.595156336428591e-03; static double r3 = .793650663183693e-03; static double r4 = -.277777777770481e-02; static double r5 = .833333333333333e-01; static double p[7] = { .539637273585445e-03,.261939260042690e-02,.204493667594920e-01, .730981088720487e-01,.279648642639792e+00,.553413866010467e+00,1.0e0 }; static double q[7] = { -.832979206704073e-03,.470059485860584e-02,.225211131035340e-01, -.170458969313360e+00,-.567902761974940e-01,.113062953091122e+01,1.0e0 }; static int K2 = 3; static int K3 = 0; static double Xgamm,bot,g,lnx,s,t,top,w,x,z; static int i,j,m,n,T1; /* .. .. Executable Statements .. */ Xgamm = 0.0e0; x = *a; if(fabs(*a) >= 15.0e0) goto S110; /* ----------------------------------------------------------------------- EVALUATION OF GAMMA(A) FOR ABS(A) .LT. 15 ----------------------------------------------------------------------- */ t = 1.0e0; m = fifidint(*a)-1; /* LET T BE THE PRODUCT OF A-J WHEN A .GE. 2 */ T1 = m; if(T1 < 0) goto S40; else if(T1 == 0) goto S30; else goto S10; S10: for(j=1; j<=m; j++) { x -= 1.0e0; t = x*t; } S30: x -= 1.0e0; goto S80; S40: /* LET T BE THE PRODUCT OF A+J WHEN A .LT. 1 */ t = *a; if(*a > 0.0e0) goto S70; m = -m-1; if(m == 0) goto S60; for(j=1; j<=m; j++) { x += 1.0e0; t = x*t; } S60: x += (0.5e0+0.5e0); t = x*t; if(t == 0.0e0) return Xgamm; S70: /* THE FOLLOWING CODE CHECKS IF 1/T CAN OVERFLOW. THIS CODE MAY BE OMITTED IF DESIRED. */ if(fabs(t) >= 1.e-30) goto S80; if(fabs(t)*spmpar(&K2) <= 1.0001e0) return Xgamm; Xgamm = 1.0e0/t; return Xgamm; S80: /* COMPUTE GAMMA(1 + X) FOR 0 .LE. X .LT. 1 */ top = p[0]; bot = q[0]; for(i=1; i<7; i++) { top = p[i]+x*top; bot = q[i]+x*bot; } Xgamm = top/bot; /* TERMINATION */ if(*a < 1.0e0) goto S100; Xgamm *= t; return Xgamm; S100: Xgamm /= t; return Xgamm; S110: /* ----------------------------------------------------------------------- EVALUATION OF GAMMA(A) FOR ABS(A) .GE. 15 ----------------------------------------------------------------------- */ if(fabs(*a) >= 1.e3) return Xgamm; if(*a > 0.0e0) goto S120; x = -*a; n = x; t = x-(double)n; if(t > 0.9e0) t = 1.0e0-t; s = sin(pi*t)/pi; if(fifmod(n,2) == 0) s = -s; if(s == 0.0e0) return Xgamm; S120: /* COMPUTE THE MODIFIED ASYMPTOTIC SUM */ t = 1.0e0/(x*x); g = ((((r1*t+r2)*t+r3)*t+r4)*t+r5)/x; /* ONE MAY REPLACE THE NEXT STATEMENT WITH LNX = ALOG(X) BUT LESS ACCURACY WILL NORMALLY BE OBTAINED. */ lnx = log(x); /* FINAL ASSEMBLY */ z = x; g = d+g+(z-0.5e0)*(lnx-1.e0); w = g; t = g-w; if(w > 0.99999e0*exparg(&K3)) return Xgamm; Xgamm = exp(w)*(1.0e0+t); if(*a < 0.0e0) Xgamm = 1.0e0/(Xgamm*s)/x; return Xgamm; } /* END */ /***=====================================================================***/ static void grat1(double *a,double *x,double *r,double *p,double *q, double *eps) { static int K2 = 0; static double a2n,a2nm1,am0,an,an0,b2n,b2nm1,c,cma,g,h,j,l,sum,t,tol,w,z,T1,T3; /* .. .. Executable Statements .. */ /* ----------------------------------------------------------------------- EVALUATION OF THE INCOMPLETE GAMMA RATIO FUNCTIONS P(A,X) AND Q(A,X) IT IS ASSUMED THAT A .LE. 1. EPS IS THE TOLERANCE TO BE USED. THE INPUT ARGUMENT R HAS THE VALUE E**(-X)*X**A/GAMMA(A). ----------------------------------------------------------------------- */ if(*a**x == 0.0e0) goto S120; if(*a == 0.5e0) goto S100; if(*x < 1.1e0) goto S10; goto S60; S10: /* TAYLOR SERIES FOR P(A,X)/X**A */ an = 3.0e0; c = *x; sum = *x/(*a+3.0e0); tol = 0.1e0**eps/(*a+1.0e0); S20: an += 1.0e0; c = -(c*(*x/an)); t = c/(*a+an); sum += t; if(fabs(t) > tol) goto S20; j = *a**x*((sum/6.0e0-0.5e0/(*a+2.0e0))**x+1.0e0/(*a+1.0e0)); z = *a*log(*x); h = gam1(a); g = 1.0e0+h; if(*x < 0.25e0) goto S30; if(*a < *x/2.59e0) goto S50; goto S40; S30: if(z > -.13394e0) goto S50; S40: w = exp(z); *p = w*g*(0.5e0+(0.5e0-j)); *q = 0.5e0+(0.5e0-*p); return; S50: l = rexp(&z); w = 0.5e0+(0.5e0+l); *q = (w*j-l)*g-h; if(*q < 0.0e0) goto S90; *p = 0.5e0+(0.5e0-*q); return; S60: /* CONTINUED FRACTION EXPANSION */ a2nm1 = a2n = 1.0e0; b2nm1 = *x; b2n = *x+(1.0e0-*a); c = 1.0e0; S70: a2nm1 = *x*a2n+c*a2nm1; b2nm1 = *x*b2n+c*b2nm1; am0 = a2nm1/b2nm1; c += 1.0e0; cma = c-*a; a2n = a2nm1+cma*a2n; b2n = b2nm1+cma*b2n; an0 = a2n/b2n; if(fabs(an0-am0) >= *eps*an0) goto S70; *q = *r*an0; *p = 0.5e0+(0.5e0-*q); return; S80: /* SPECIAL CASES */ *p = 0.0e0; *q = 1.0e0; return; S90: *p = 1.0e0; *q = 0.0e0; return; S100: if(*x >= 0.25e0) goto S110; T1 = sqrt(*x); *p = erf1(&T1); *q = 0.5e0+(0.5e0-*p); return; S110: T3 = sqrt(*x); *q = erfc1(&K2,&T3); *p = 0.5e0+(0.5e0-*q); return; S120: if(*x <= *a) goto S80; goto S90; } /* END */ /***=====================================================================***/ static void gratio(double *a,double *x,double *ans,double *qans,int *ind) /* ---------------------------------------------------------------------- EVALUATION OF THE INCOMPLETE GAMMA RATIO FUNCTIONS P(A,X) AND Q(A,X) ---------- IT IS ASSUMED THAT A AND X ARE NONNEGATIVE, WHERE A AND X ARE NOT BOTH 0. ANS AND QANS ARE VARIABLES. GRATIO ASSIGNS ANS THE VALUE P(A,X) AND QANS THE VALUE Q(A,X). IND MAY BE ANY INTEGER. IF IND = 0 THEN THE USER IS REQUESTING AS MUCH ACCURACY AS POSSIBLE (UP TO 14 SIGNIFICANT DIGITS). OTHERWISE, IF IND = 1 THEN ACCURACY IS REQUESTED TO WITHIN 1 UNIT OF THE 6-TH SIGNIFICANT DIGIT, AND IF IND .NE. 0,1 THEN ACCURACY IS REQUESTED TO WITHIN 1 UNIT OF THE 3RD SIGNIFICANT DIGIT. ERROR RETURN ... ANS IS ASSIGNED THE VALUE 2 WHEN A OR X IS NEGATIVE, WHEN A*X = 0, OR WHEN P(A,X) AND Q(A,X) ARE INDETERMINANT. P(A,X) AND Q(A,X) ARE COMPUTATIONALLY INDETERMINANT WHEN X IS EXCEEDINGLY CLOSE TO A AND A IS EXTREMELY LARGE. ---------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS, JR. NAVAL SURFACE WEAPONS CENTER DAHLGREN, VIRGINIA -------------------- */ { static double alog10 = 2.30258509299405e0; static double d10 = -.185185185185185e-02; static double d20 = .413359788359788e-02; static double d30 = .649434156378601e-03; static double d40 = -.861888290916712e-03; static double d50 = -.336798553366358e-03; static double d60 = .531307936463992e-03; static double d70 = .344367606892378e-03; static double rt2pin = .398942280401433e0; static double rtpi = 1.77245385090552e0; static double third = .333333333333333e0; static double acc0[3] = { 5.e-15,5.e-7,5.e-4 }; static double big[3] = { 20.0e0,14.0e0,10.0e0 }; static double d0[13] = { .833333333333333e-01,-.148148148148148e-01,.115740740740741e-02, .352733686067019e-03,-.178755144032922e-03,.391926317852244e-04, -.218544851067999e-05,-.185406221071516e-05,.829671134095309e-06, -.176659527368261e-06,.670785354340150e-08,.102618097842403e-07, -.438203601845335e-08 }; static double d1[12] = { -.347222222222222e-02,.264550264550265e-02,-.990226337448560e-03, .205761316872428e-03,-.401877572016461e-06,-.180985503344900e-04, .764916091608111e-05,-.161209008945634e-05,.464712780280743e-08, .137863344691572e-06,-.575254560351770e-07,.119516285997781e-07 }; static double d2[10] = { -.268132716049383e-02,.771604938271605e-03,.200938786008230e-05, -.107366532263652e-03,.529234488291201e-04,-.127606351886187e-04, .342357873409614e-07,.137219573090629e-05,-.629899213838006e-06, .142806142060642e-06 }; static double d3[8] = { .229472093621399e-03,-.469189494395256e-03,.267720632062839e-03, -.756180167188398e-04,-.239650511386730e-06,.110826541153473e-04, -.567495282699160e-05,.142309007324359e-05 }; static double d4[6] = { .784039221720067e-03,-.299072480303190e-03,-.146384525788434e-05, .664149821546512e-04,-.396836504717943e-04,.113757269706784e-04 }; static double d5[4] = { -.697281375836586e-04,.277275324495939e-03,-.199325705161888e-03, .679778047793721e-04 }; static double d6[2] = { -.592166437353694e-03,.270878209671804e-03 }; static double e00[3] = { .25e-3,.25e-1,.14e0 }; static double x00[3] = { 31.0e0,17.0e0,9.7e0 }; static int K1 = 1; static int K2 = 0; static double a2n,a2nm1,acc,am0,amn,an,an0,apn,b2n,b2nm1,c,c0,c1,c2,c3,c4,c5,c6, cma,e,e0,g,h,j,l,r,rta,rtx,s,sum,t,t1,tol,twoa,u,w,x0,y,z; static int i,iop,m,max,n; static double wk[20],T3; static int T4,T5; static double T6,T7; /* .. .. Executable Statements .. */ /* -------------------- ****** E IS A MACHINE DEPENDENT CONSTANT. E IS THE SMALLEST FLOATING POINT NUMBER FOR WHICH 1.0 + E .GT. 1.0 . */ e = spmpar(&K1); if(*a < 0.0e0 || *x < 0.0e0) goto S430; if(*a == 0.0e0 && *x == 0.0e0) goto S430; if(*a**x == 0.0e0) goto S420; iop = *ind+1; if(iop != 1 && iop != 2) iop = 3; acc = fifdmax1(acc0[iop-1],e); e0 = e00[iop-1]; x0 = x00[iop-1]; /* SELECT THE APPROPRIATE ALGORITHM */ if(*a >= 1.0e0) goto S10; if(*a == 0.5e0) goto S390; if(*x < 1.1e0) goto S160; t1 = *a*log(*x)-*x; u = *a*exp(t1); if(u == 0.0e0) goto S380; r = u*(1.0e0+gam1(a)); goto S250; S10: if(*a >= big[iop-1]) goto S30; if(*a > *x || *x >= x0) goto S20; twoa = *a+*a; m = fifidint(twoa); if(twoa != (double)m) goto S20; i = m/2; if(*a == (double)i) goto S210; goto S220; S20: t1 = *a*log(*x)-*x; r = exp(t1)/Xgamm(a); goto S40; S30: l = *x/ *a; if(l == 0.0e0) goto S370; s = 0.5e0+(0.5e0-l); z = rlog(&l); if(z >= 700.0e0/ *a) goto S410; y = *a*z; rta = sqrt(*a); if(fabs(s) <= e0/rta) goto S330; if(fabs(s) <= 0.4e0) goto S270; t = pow(1.0e0/ *a,2.0); t1 = (((0.75e0*t-1.0e0)*t+3.5e0)*t-105.0e0)/(*a*1260.0e0); t1 -= y; r = rt2pin*rta*exp(t1); S40: if(r == 0.0e0) goto S420; if(*x <= fifdmax1(*a,alog10)) goto S50; if(*x < x0) goto S250; goto S100; S50: /* TAYLOR SERIES FOR P/R */ apn = *a+1.0e0; t = *x/apn; wk[0] = t; for(n=2; n<=20; n++) { apn += 1.0e0; t *= (*x/apn); if(t <= 1.e-3) goto S70; wk[n-1] = t; } n = 20; S70: sum = t; tol = 0.5e0*acc; S80: apn += 1.0e0; t *= (*x/apn); sum += t; if(t > tol) goto S80; max = n-1; for(m=1; m<=max; m++) { n -= 1; sum += wk[n-1]; } *ans = r/ *a*(1.0e0+sum); *qans = 0.5e0+(0.5e0-*ans); return; S100: /* ASYMPTOTIC EXPANSION */ amn = *a-1.0e0; t = amn/ *x; wk[0] = t; for(n=2; n<=20; n++) { amn -= 1.0e0; t *= (amn/ *x); if(fabs(t) <= 1.e-3) goto S120; wk[n-1] = t; } n = 20; S120: sum = t; S130: if(fabs(t) <= acc) goto S140; amn -= 1.0e0; t *= (amn/ *x); sum += t; goto S130; S140: max = n-1; for(m=1; m<=max; m++) { n -= 1; sum += wk[n-1]; } *qans = r/ *x*(1.0e0+sum); *ans = 0.5e0+(0.5e0-*qans); return; S160: /* TAYLOR SERIES FOR P(A,X)/X**A */ an = 3.0e0; c = *x; sum = *x/(*a+3.0e0); tol = 3.0e0*acc/(*a+1.0e0); S170: an += 1.0e0; c = -(c*(*x/an)); t = c/(*a+an); sum += t; if(fabs(t) > tol) goto S170; j = *a**x*((sum/6.0e0-0.5e0/(*a+2.0e0))**x+1.0e0/(*a+1.0e0)); z = *a*log(*x); h = gam1(a); g = 1.0e0+h; if(*x < 0.25e0) goto S180; if(*a < *x/2.59e0) goto S200; goto S190; S180: if(z > -.13394e0) goto S200; S190: w = exp(z); *ans = w*g*(0.5e0+(0.5e0-j)); *qans = 0.5e0+(0.5e0-*ans); return; S200: l = rexp(&z); w = 0.5e0+(0.5e0+l); *qans = (w*j-l)*g-h; if(*qans < 0.0e0) goto S380; *ans = 0.5e0+(0.5e0-*qans); return; S210: /* FINITE SUMS FOR Q WHEN A .GE. 1 AND 2*A IS AN INTEGER */ sum = exp(-*x); t = sum; n = 1; c = 0.0e0; goto S230; S220: rtx = sqrt(*x); sum = erfc1(&K2,&rtx); t = exp(-*x)/(rtpi*rtx); n = 0; c = -0.5e0; S230: if(n == i) goto S240; n += 1; c += 1.0e0; t = *x*t/c; sum += t; goto S230; S240: *qans = sum; *ans = 0.5e0+(0.5e0-*qans); return; S250: /* CONTINUED FRACTION EXPANSION */ tol = fifdmax1(5.0e0*e,acc); a2nm1 = a2n = 1.0e0; b2nm1 = *x; b2n = *x+(1.0e0-*a); c = 1.0e0; S260: a2nm1 = *x*a2n+c*a2nm1; b2nm1 = *x*b2n+c*b2nm1; am0 = a2nm1/b2nm1; c += 1.0e0; cma = c-*a; a2n = a2nm1+cma*a2n; b2n = b2nm1+cma*b2n; an0 = a2n/b2n; if(fabs(an0-am0) >= tol*an0) goto S260; *qans = r*an0; *ans = 0.5e0+(0.5e0-*qans); return; S270: /* GENERAL TEMME EXPANSION */ if(fabs(s) <= 2.0e0*e && *a*e*e > 3.28e-3) goto S430; c = exp(-y); T3 = sqrt(y); w = 0.5e0*erfc1(&K1,&T3); u = 1.0e0/ *a; z = sqrt(z+z); if(l < 1.0e0) z = -z; T4 = iop-2; if(T4 < 0) goto S280; else if(T4 == 0) goto S290; else goto S300; S280: if(fabs(s) <= 1.e-3) goto S340; c0 = ((((((((((((d0[12]*z+d0[11])*z+d0[10])*z+d0[9])*z+d0[8])*z+d0[7])*z+d0[ 6])*z+d0[5])*z+d0[4])*z+d0[3])*z+d0[2])*z+d0[1])*z+d0[0])*z-third; c1 = (((((((((((d1[11]*z+d1[10])*z+d1[9])*z+d1[8])*z+d1[7])*z+d1[6])*z+d1[5] )*z+d1[4])*z+d1[3])*z+d1[2])*z+d1[1])*z+d1[0])*z+d10; c2 = (((((((((d2[9]*z+d2[8])*z+d2[7])*z+d2[6])*z+d2[5])*z+d2[4])*z+d2[3])*z+ d2[2])*z+d2[1])*z+d2[0])*z+d20; c3 = (((((((d3[7]*z+d3[6])*z+d3[5])*z+d3[4])*z+d3[3])*z+d3[2])*z+d3[1])*z+ d3[0])*z+d30; c4 = (((((d4[5]*z+d4[4])*z+d4[3])*z+d4[2])*z+d4[1])*z+d4[0])*z+d40; c5 = (((d5[3]*z+d5[2])*z+d5[1])*z+d5[0])*z+d50; c6 = (d6[1]*z+d6[0])*z+d60; t = ((((((d70*u+c6)*u+c5)*u+c4)*u+c3)*u+c2)*u+c1)*u+c0; goto S310; S290: c0 = (((((d0[5]*z+d0[4])*z+d0[3])*z+d0[2])*z+d0[1])*z+d0[0])*z-third; c1 = (((d1[3]*z+d1[2])*z+d1[1])*z+d1[0])*z+d10; c2 = d2[0]*z+d20; t = (c2*u+c1)*u+c0; goto S310; S300: t = ((d0[2]*z+d0[1])*z+d0[0])*z-third; S310: if(l < 1.0e0) goto S320; *qans = c*(w+rt2pin*t/rta); *ans = 0.5e0+(0.5e0-*qans); return; S320: *ans = c*(w-rt2pin*t/rta); *qans = 0.5e0+(0.5e0-*ans); return; S330: /* TEMME EXPANSION FOR L = 1 */ if(*a*e*e > 3.28e-3) goto S430; c = 0.5e0+(0.5e0-y); w = (0.5e0-sqrt(y)*(0.5e0+(0.5e0-y/3.0e0))/rtpi)/c; u = 1.0e0/ *a; z = sqrt(z+z); if(l < 1.0e0) z = -z; T5 = iop-2; if(T5 < 0) goto S340; else if(T5 == 0) goto S350; else goto S360; S340: c0 = ((((((d0[6]*z+d0[5])*z+d0[4])*z+d0[3])*z+d0[2])*z+d0[1])*z+d0[0])*z- third; c1 = (((((d1[5]*z+d1[4])*z+d1[3])*z+d1[2])*z+d1[1])*z+d1[0])*z+d10; c2 = ((((d2[4]*z+d2[3])*z+d2[2])*z+d2[1])*z+d2[0])*z+d20; c3 = (((d3[3]*z+d3[2])*z+d3[1])*z+d3[0])*z+d30; c4 = (d4[1]*z+d4[0])*z+d40; c5 = (d5[1]*z+d5[0])*z+d50; c6 = d6[0]*z+d60; t = ((((((d70*u+c6)*u+c5)*u+c4)*u+c3)*u+c2)*u+c1)*u+c0; goto S310; S350: c0 = (d0[1]*z+d0[0])*z-third; c1 = d1[0]*z+d10; t = (d20*u+c1)*u+c0; goto S310; S360: t = d0[0]*z-third; goto S310; S370: /* SPECIAL CASES */ *ans = 0.0e0; *qans = 1.0e0; return; S380: *ans = 1.0e0; *qans = 0.0e0; return; S390: if(*x >= 0.25e0) goto S400; T6 = sqrt(*x); *ans = erf1(&T6); *qans = 0.5e0+(0.5e0-*ans); return; S400: T7 = sqrt(*x); *qans = erfc1(&K2,&T7); *ans = 0.5e0+(0.5e0-*qans); return; S410: if(fabs(s) <= 2.0e0*e) goto S430; S420: if(*x <= *a) goto S370; goto S380; S430: /* ERROR RETURN */ *ans = 2.0e0; return; } /* END */ /***=====================================================================***/ static double gsumln(double *a,double *b) /* ----------------------------------------------------------------------- EVALUATION OF THE FUNCTION LN(GAMMA(A + B)) FOR 1 .LE. A .LE. 2 AND 1 .LE. B .LE. 2 ----------------------------------------------------------------------- */ { static double gsumln,x,T1,T2; /* .. .. Executable Statements .. */ x = *a+*b-2.e0; if(x > 0.25e0) goto S10; T1 = 1.0e0+x; gsumln = gamln1(&T1); return gsumln; S10: if(x > 1.25e0) goto S20; gsumln = gamln1(&x)+alnrel(&x); return gsumln; S20: T2 = x-1.0e0; gsumln = gamln1(&T2)+log(x*(1.0e0+x)); return gsumln; } /* END */ /***=====================================================================***/ static double psi(double *xx) /* --------------------------------------------------------------------- EVALUATION OF THE DIGAMMA FUNCTION ----------- PSI(XX) IS ASSIGNED THE VALUE 0 WHEN THE DIGAMMA FUNCTION CANNOT BE COMPUTED. THE MAIN COMPUTATION INVOLVES EVALUATION OF RATIONAL CHEBYSHEV APPROXIMATIONS PUBLISHED IN MATH. COMP. 27, 123-127(1973) BY CODY, STRECOK AND THACHER. --------------------------------------------------------------------- PSI WAS WRITTEN AT ARGONNE NATIONAL LABORATORY FOR THE FUNPACK PACKAGE OF SPECIAL FUNCTION SUBROUTINES. PSI WAS MODIFIED BY A.H. MORRIS (NSWC). --------------------------------------------------------------------- */ { static double dx0 = 1.461632144968362341262659542325721325e0; static double piov4 = .785398163397448e0; static double p1[7] = { .895385022981970e-02,.477762828042627e+01,.142441585084029e+03, .118645200713425e+04,.363351846806499e+04,.413810161269013e+04, .130560269827897e+04 }; static double p2[4] = { -.212940445131011e+01,-.701677227766759e+01,-.448616543918019e+01, -.648157123766197e+00 }; static double q1[6] = { .448452573429826e+02,.520752771467162e+03,.221000799247830e+04, .364127349079381e+04,.190831076596300e+04,.691091682714533e-05 }; static double q2[4] = { .322703493791143e+02,.892920700481861e+02,.546117738103215e+02, .777788548522962e+01 }; static int K1 = 3; static int K2 = 1; static double psi,aug,den,sgn,upper,w,x,xmax1,xmx0,xsmall,z; static int i,m,n,nq; /* .. .. Executable Statements .. */ /* --------------------------------------------------------------------- MACHINE DEPENDENT CONSTANTS ... XMAX1 = THE SMALLEST POSITIVE FLOATING POINT CONSTANT WITH ENTIRELY INTEGER REPRESENTATION. ALSO USED AS NEGATIVE OF LOWER BOUND TRUE ACCEPTABLE NEGATIVE ARGUMENTS AND AS THE POSITIVE ARGUMENT BEYOND WHICH PSI MAY BE REPRESENTED AS ALOG(X). XSMALL = ABSOLUTE ARGUMENT BELOW WHICH PI*COTAN(PI*X) MAY BE REPRESENTED BY 1/X. --------------------------------------------------------------------- */ xmax1 = ipmpar(&K1); xmax1 = fifdmin1(xmax1,1.0e0/spmpar(&K2)); xsmall = 1.e-9; x = *xx; aug = 0.0e0; if(x >= 0.5e0) goto S50; /* --------------------------------------------------------------------- X .LT. 0.5, USE REFLECTION FORMULA PSI(1-X) = PSI(X) + PI * COTAN(PI*X) --------------------------------------------------------------------- */ if(fabs(x) > xsmall) goto S10; if(x == 0.0e0) goto S100; /* --------------------------------------------------------------------- 0 .LT. ABS(X) .LE. XSMALL. USE 1/X AS A SUBSTITUTE FOR PI*COTAN(PI*X) --------------------------------------------------------------------- */ aug = -(1.0e0/x); goto S40; S10: /* --------------------------------------------------------------------- REDUCTION OF ARGUMENT FOR COTAN --------------------------------------------------------------------- */ w = -x; sgn = piov4; if(w > 0.0e0) goto S20; w = -w; sgn = -sgn; S20: /* --------------------------------------------------------------------- MAKE AN ERROR EXIT IF X .LE. -XMAX1 --------------------------------------------------------------------- */ if(w >= xmax1) goto S100; nq = fifidint(w); w -= (double)nq; nq = fifidint(w*4.0e0); w = 4.0e0*(w-(double)nq*.25e0); /* --------------------------------------------------------------------- W IS NOW RELATED TO THE FRACTIONAL PART OF 4.0 * X. ADJUST ARGUMENT TO CORRESPOND TO VALUES IN FIRST QUADRANT AND DETERMINE SIGN --------------------------------------------------------------------- */ n = nq/2; if(n+n != nq) w = 1.0e0-w; z = piov4*w; m = n/2; if(m+m != n) sgn = -sgn; /* --------------------------------------------------------------------- DETERMINE FINAL VALUE FOR -PI*COTAN(PI*X) --------------------------------------------------------------------- */ n = (nq+1)/2; m = n/2; m += m; if(m != n) goto S30; /* --------------------------------------------------------------------- CHECK FOR SINGULARITY --------------------------------------------------------------------- */ if(z == 0.0e0) goto S100; /* --------------------------------------------------------------------- USE COS/SIN AS A SUBSTITUTE FOR COTAN, AND SIN/COS AS A SUBSTITUTE FOR TAN --------------------------------------------------------------------- */ aug = sgn*(cos(z)/sin(z)*4.0e0); goto S40; S30: aug = sgn*(sin(z)/cos(z)*4.0e0); S40: x = 1.0e0-x; S50: if(x > 3.0e0) goto S70; /* --------------------------------------------------------------------- 0.5 .LE. X .LE. 3.0 --------------------------------------------------------------------- */ den = x; upper = p1[0]*x; for(i=1; i<=5; i++) { den = (den+q1[i-1])*x; upper = (upper+p1[i+1-1])*x; } den = (upper+p1[6])/(den+q1[5]); xmx0 = x-dx0; psi = den*xmx0+aug; return psi; S70: /* --------------------------------------------------------------------- IF X .GE. XMAX1, PSI = LN(X) --------------------------------------------------------------------- */ if(x >= xmax1) goto S90; /* --------------------------------------------------------------------- 3.0 .LT. X .LT. XMAX1 --------------------------------------------------------------------- */ w = 1.0e0/(x*x); den = w; upper = p2[0]*w; for(i=1; i<=3; i++) { den = (den+q2[i-1])*w; upper = (upper+p2[i+1-1])*w; } aug = upper/(den+q2[3])-0.5e0/x+aug; S90: psi = aug+log(x); return psi; S100: /* --------------------------------------------------------------------- ERROR RETURN --------------------------------------------------------------------- */ psi = 0.0e0; return psi; } /* END */ /***=====================================================================***/ static double rcomp(double *a,double *x) /* ------------------- EVALUATION OF EXP(-X)*X**A/GAMMA(A) ------------------- RT2PIN = 1/SQRT(2*PI) ------------------- */ { static double rt2pin = .398942280401433e0; static double rcomp,t,t1,u; /* .. .. Executable Statements .. */ rcomp = 0.0e0; if(*a >= 20.0e0) goto S20; t = *a*log(*x)-*x; if(*a >= 1.0e0) goto S10; rcomp = *a*exp(t)*(1.0e0+gam1(a)); return rcomp; S10: rcomp = exp(t)/Xgamm(a); return rcomp; S20: u = *x/ *a; if(u == 0.0e0) return rcomp; t = pow(1.0e0/ *a,2.0); t1 = (((0.75e0*t-1.0e0)*t+3.5e0)*t-105.0e0)/(*a*1260.0e0); t1 -= (*a*rlog(&u)); rcomp = rt2pin*sqrt(*a)*exp(t1); return rcomp; } /* END */ /***=====================================================================***/ static double rexp(double *x) /* ----------------------------------------------------------------------- EVALUATION OF THE FUNCTION EXP(X) - 1 ----------------------------------------------------------------------- */ { static double p1 = .914041914819518e-09; static double p2 = .238082361044469e-01; static double q1 = -.499999999085958e+00; static double q2 = .107141568980644e+00; static double q3 = -.119041179760821e-01; static double q4 = .595130811860248e-03; static double rexp,w; /* .. .. Executable Statements .. */ if(fabs(*x) > 0.15e0) goto S10; rexp = *x*(((p2**x+p1)**x+1.0e0)/((((q4**x+q3)**x+q2)**x+q1)**x+1.0e0)); return rexp; S10: w = exp(*x); if(*x > 0.0e0) goto S20; rexp = w-0.5e0-0.5e0; return rexp; S20: rexp = w*(0.5e0+(0.5e0-1.0e0/w)); return rexp; } /* END */ /***=====================================================================***/ static double rlog(double *x) /* ------------------- COMPUTATION OF X - 1 - LN(X) ------------------- */ { static double a = .566749439387324e-01; static double b = .456512608815524e-01; static double p0 = .333333333333333e+00; static double p1 = -.224696413112536e+00; static double p2 = .620886815375787e-02; static double q1 = -.127408923933623e+01; static double q2 = .354508718369557e+00; static double rlog,r,t,u,w,w1; /* .. .. Executable Statements .. */ if(*x < 0.61e0 || *x > 1.57e0) goto S40; if(*x < 0.82e0) goto S10; if(*x > 1.18e0) goto S20; /* ARGUMENT REDUCTION */ u = *x-0.5e0-0.5e0; w1 = 0.0e0; goto S30; S10: u = *x-0.7e0; u /= 0.7e0; w1 = a-u*0.3e0; goto S30; S20: u = 0.75e0**x-1.e0; w1 = b+u/3.0e0; S30: /* SERIES EXPANSION */ r = u/(u+2.0e0); t = r*r; w = ((p2*t+p1)*t+p0)/((q2*t+q1)*t+1.0e0); rlog = 2.0e0*t*(1.0e0/(1.0e0-r)-r*w)+w1; return rlog; S40: r = *x-0.5e0-0.5e0; rlog = r-log(*x); return rlog; } /* END */ /***=====================================================================***/ static double rlog1(double *x) /* ----------------------------------------------------------------------- EVALUATION OF THE FUNCTION X - LN(1 + X) ----------------------------------------------------------------------- */ { static double a = .566749439387324e-01; static double b = .456512608815524e-01; static double p0 = .333333333333333e+00; static double p1 = -.224696413112536e+00; static double p2 = .620886815375787e-02; static double q1 = -.127408923933623e+01; static double q2 = .354508718369557e+00; static double rlog1,h,r,t,w,w1; /* .. .. Executable Statements .. */ if(*x < -0.39e0 || *x > 0.57e0) goto S40; if(*x < -0.18e0) goto S10; if(*x > 0.18e0) goto S20; /* ARGUMENT REDUCTION */ h = *x; w1 = 0.0e0; goto S30; S10: h = *x+0.3e0; h /= 0.7e0; w1 = a-h*0.3e0; goto S30; S20: h = 0.75e0**x-0.25e0; w1 = b+h/3.0e0; S30: /* SERIES EXPANSION */ r = h/(h+2.0e0); t = r*r; w = ((p2*t+p1)*t+p0)/((q2*t+q1)*t+1.0e0); rlog1 = 2.0e0*t*(1.0e0/(1.0e0-r)-r*w)+w1; return rlog1; S40: w = *x+0.5e0+0.5e0; rlog1 = *x-log(w); return rlog1; } /* END */ /***=====================================================================***/ static double spmpar(int *i) /* ----------------------------------------------------------------------- SPMPAR PROVIDES THE SINGLE PRECISION MACHINE CONSTANTS FOR THE COMPUTER BEING USED. IT IS ASSUMED THAT THE ARGUMENT I IS AN INTEGER HAVING ONE OF THE VALUES 1, 2, OR 3. IF THE SINGLE PRECISION ARITHMETIC BEING USED HAS M BASE B DIGITS AND ITS SMALLEST AND LARGEST EXPONENTS ARE EMIN AND EMAX, THEN SPMPAR(1) = B**(1 - M), THE MACHINE PRECISION, SPMPAR(2) = B**(EMIN - 1), THE SMALLEST MAGNITUDE, SPMPAR(3) = B**EMAX*(1 - B**(-M)), THE LARGEST MAGNITUDE. ----------------------------------------------------------------------- WRITTEN BY ALFRED H. MORRIS, JR. NAVAL SURFACE WARFARE CENTER DAHLGREN VIRGINIA ----------------------------------------------------------------------- ----------------------------------------------------------------------- MODIFIED BY BARRY W. BROWN TO RETURN DOUBLE PRECISION MACHINE CONSTANTS FOR THE COMPUTER BEING USED. THIS MODIFICATION WAS MADE AS PART OF CONVERTING BRATIO TO DOUBLE PRECISION ----------------------------------------------------------------------- */ { static int K1 = 4; static int K2 = 8; static int K3 = 9; static int K4 = 10; static double spmpar,b,binv,bm1,one,w,z; static int emax,emin,ibeta,m; /* .. .. Executable Statements .. */ if(*i > 1) goto S10; b = ipmpar(&K1); m = ipmpar(&K2); spmpar = pow(b,(double)(1-m)); return spmpar; S10: if(*i > 2) goto S20; b = ipmpar(&K1); emin = ipmpar(&K3); one = 1.0; binv = one/b; w = pow(b,(double)(emin+2)); spmpar = w*binv*binv*binv; return spmpar; S20: ibeta = ipmpar(&K1); m = ipmpar(&K2); emax = ipmpar(&K4); b = ibeta; bm1 = ibeta-1; one = 1.0; z = pow(b,(double)(m-1)); w = ((z-one)*b+bm1)/(b*z); z = pow(b,(double)(emax-2)); spmpar = w*z*b*b; return spmpar; } /* END */ /***=====================================================================***/ static double stvaln(double *p) /* ********************************************************************** double stvaln(double *p) STarting VALue for Neton-Raphon calculation of Normal distribution Inverse Function Returns X such that CUMNOR(X) = P, i.e., the integral from - infinity to X of (1/SQRT(2*PI)) EXP(-U*U/2) dU is P Arguments P --> The probability whose normal deviate is sought. P is DOUBLE PRECISION Method The rational function on page 95 of Kennedy and Gentle, Statistical Computing, Marcel Dekker, NY , 1980. ********************************************************************** */ { static double xden[5] = { 0.993484626060e-1,0.588581570495e0,0.531103462366e0,0.103537752850e0, 0.38560700634e-2 }; static double xnum[5] = { -0.322232431088e0,-1.000000000000e0,-0.342242088547e0,-0.204231210245e-1, -0.453642210148e-4 }; static int K1 = 5; static double stvaln,sign,y,z; /* .. .. Executable Statements .. */ if(!(*p <= 0.5e0)) goto S10; sign = -1.0e0; z = *p; goto S20; S10: sign = 1.0e0; z = 1.0e0-*p; S20: y = sqrt(-(2.0e0*log(z))); stvaln = y+devlpl(xnum,&K1,&y)/devlpl(xden,&K1,&y); stvaln = sign*stvaln; return stvaln; } /* END */ /***=====================================================================***/ static double fifdint(double a) /************************************************************************ FIFDINT: Truncates a double precision number to an integer and returns the value in a double. ************************************************************************/ /* a - number to be truncated */ { return (double) ((int) a); } /* END */ /***=====================================================================***/ static double fifdmax1(double a,double b) /************************************************************************ FIFDMAX1: returns the maximum of two numbers a and b ************************************************************************/ /* a - first number */ /* b - second number */ { if (a < b) return b; else return a; } /* END */ /***=====================================================================***/ static double fifdmin1(double a,double b) /************************************************************************ FIFDMIN1: returns the minimum of two numbers a and b ************************************************************************/ /* a - first number */ /* b - second number */ { if (a < b) return a; else return b; } /* END */ /***=====================================================================***/ static double fifdsign(double mag,double sign) /************************************************************************ FIFDSIGN: transfers the sign of the variable "sign" to the variable "mag" ************************************************************************/ /* mag - magnitude */ /* sign - sign to be transfered */ { if (mag < 0) mag = -mag; if (sign < 0) mag = -mag; return mag; } /* END */ /***=====================================================================***/ static long fifidint(double a) /************************************************************************ FIFIDINT: Truncates a double precision number to a long integer ************************************************************************/ /* a - number to be truncated */ { if (a < 1.0) return (long) 0; else return (long) a; } /* END */ /***=====================================================================***/ static long fifmod(long a,long b) /************************************************************************ FIFMOD: returns the modulo of a and b ************************************************************************/ /* a - numerator */ /* b - denominator */ { return a % b; } /* END */ /***=====================================================================***/ static void ftnstop(char* msg) /************************************************************************ FTNSTOP: Prints msg to standard error and then exits ************************************************************************/ /* msg - error message */ { if (msg != NULL) fprintf(stderr,"*** CDFLIB ERROR: %s\n",msg); /** exit(1); **/ /** RWCox - DON'T EXIT */ } /* END */ /***=====================================================================***/ static int ipmpar(int *i) /* ----------------------------------------------------------------------- IPMPAR PROVIDES THE INTEGER MACHINE CONSTANTS FOR THE COMPUTER THAT IS USED. IT IS ASSUMED THAT THE ARGUMENT I IS AN INTEGER HAVING ONE OF THE VALUES 1-10. IPMPAR(I) HAS THE VALUE ... INTEGERS. ASSUME INTEGERS ARE REPRESENTED IN THE N-DIGIT, BASE-A FORM SIGN ( X(N-1)*A**(N-1) + ... + X(1)*A + X(0) ) WHERE 0 .LE. X(I) .LT. A FOR I=0,...,N-1. IPMPAR(1) = A, THE BASE. IPMPAR(2) = N, THE NUMBER OF BASE-A DIGITS. IPMPAR(3) = A**N - 1, THE LARGEST MAGNITUDE. FLOATING-POINT NUMBERS. IT IS ASSUMED THAT THE SINGLE AND DOUBLE PRECISION FLOATING POINT ARITHMETICS HAVE THE SAME BASE, SAY B, AND THAT THE NONZERO NUMBERS ARE REPRESENTED IN THE FORM SIGN (B**E) * (X(1)/B + ... + X(M)/B**M) WHERE X(I) = 0,1,...,B-1 FOR I=1,...,M, X(1) .GE. 1, AND EMIN .LE. E .LE. EMAX. IPMPAR(4) = B, THE BASE. SINGLE-PRECISION IPMPAR(5) = M, THE NUMBER OF BASE-B DIGITS. IPMPAR(6) = EMIN, THE SMALLEST EXPONENT E. IPMPAR(7) = EMAX, THE LARGEST EXPONENT E. DOUBLE-PRECISION IPMPAR(8) = M, THE NUMBER OF BASE-B DIGITS. IPMPAR(9) = EMIN, THE SMALLEST EXPONENT E. IPMPAR(10) = EMAX, THE LARGEST EXPONENT E. ----------------------------------------------------------------------- TO DEFINE THIS FUNCTION FOR THE COMPUTER BEING USED REMOVE THE COMMENT DELIMITORS FROM THE DEFINITIONS DIRECTLY BELOW THE NAME OF THE MACHINE *** RWCox: at this time, the IEEE parameters are enabled. ----------------------------------------------------------------------- IPMPAR IS AN ADAPTATION OF THE FUNCTION I1MACH, WRITTEN BY P.A. FOX, A.D. HALL, AND N.L. SCHRYER (BELL LABORATORIES). IPMPAR WAS FORMED BY A.H. MORRIS (NSWC). THE CONSTANTS ARE FROM BELL LABORATORIES, NSWC, AND OTHER SOURCES. ----------------------------------------------------------------------- .. Scalar Arguments .. */ { static int imach[11]; static int outval ; /* MACHINE CONSTANTS FOR AMDAHL MACHINES. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 16; imach[5] = 6; imach[6] = -64; imach[7] = 63; imach[8] = 14; imach[9] = -64; imach[10] = 63; */ /* MACHINE CONSTANTS FOR THE AT&T 3B SERIES, AT&T PC 7300, AND AT&T 6300. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR THE BURROUGHS 1700 SYSTEM. */ /* imach[1] = 2; imach[2] = 33; imach[3] = 8589934591; imach[4] = 2; imach[5] = 24; imach[6] = -256; imach[7] = 255; imach[8] = 60; imach[9] = -256; imach[10] = 255; */ /* MACHINE CONSTANTS FOR THE BURROUGHS 5700 SYSTEM. */ /* imach[1] = 2; imach[2] = 39; imach[3] = 549755813887; imach[4] = 8; imach[5] = 13; imach[6] = -50; imach[7] = 76; imach[8] = 26; imach[9] = -50; imach[10] = 76; */ /* MACHINE CONSTANTS FOR THE BURROUGHS 6700/7700 SYSTEMS. */ /* imach[1] = 2; imach[2] = 39; imach[3] = 549755813887; imach[4] = 8; imach[5] = 13; imach[6] = -50; imach[7] = 76; imach[8] = 26; imach[9] = -32754; imach[10] = 32780; */ /* MACHINE CONSTANTS FOR THE CDC 6000/7000 SERIES 60 BIT ARITHMETIC, AND THE CDC CYBER 995 64 BIT ARITHMETIC (NOS OPERATING SYSTEM). */ /* imach[1] = 2; imach[2] = 48; imach[3] = 281474976710655; imach[4] = 2; imach[5] = 48; imach[6] = -974; imach[7] = 1070; imach[8] = 95; imach[9] = -926; imach[10] = 1070; */ /* MACHINE CONSTANTS FOR THE CDC CYBER 995 64 BIT ARITHMETIC (NOS/VE OPERATING SYSTEM). */ /* imach[1] = 2; imach[2] = 63; imach[3] = 9223372036854775807; imach[4] = 2; imach[5] = 48; imach[6] = -4096; imach[7] = 4095; imach[8] = 96; imach[9] = -4096; imach[10] = 4095; */ /* MACHINE CONSTANTS FOR THE CRAY 1, XMP, 2, AND 3. */ /* imach[1] = 2; imach[2] = 63; imach[3] = 9223372036854775807; imach[4] = 2; imach[5] = 47; imach[6] = -8189; imach[7] = 8190; imach[8] = 94; imach[9] = -8099; imach[10] = 8190; */ /* MACHINE CONSTANTS FOR THE DATA GENERAL ECLIPSE S/200. */ /* imach[1] = 2; imach[2] = 15; imach[3] = 32767; imach[4] = 16; imach[5] = 6; imach[6] = -64; imach[7] = 63; imach[8] = 14; imach[9] = -64; imach[10] = 63; */ /* MACHINE CONSTANTS FOR THE HARRIS 220. */ /* imach[1] = 2; imach[2] = 23; imach[3] = 8388607; imach[4] = 2; imach[5] = 23; imach[6] = -127; imach[7] = 127; imach[8] = 38; imach[9] = -127; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE HONEYWELL 600/6000 AND DPS 8/70 SERIES. */ /* imach[1] = 2; imach[2] = 35; imach[3] = 34359738367; imach[4] = 2; imach[5] = 27; imach[6] = -127; imach[7] = 127; imach[8] = 63; imach[9] = -127; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE HP 2100 3 WORD DOUBLE PRECISION OPTION WITH FTN4 */ /* imach[1] = 2; imach[2] = 15; imach[3] = 32767; imach[4] = 2; imach[5] = 23; imach[6] = -128; imach[7] = 127; imach[8] = 39; imach[9] = -128; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE HP 2100 4 WORD DOUBLE PRECISION OPTION WITH FTN4 */ /* imach[1] = 2; imach[2] = 15; imach[3] = 32767; imach[4] = 2; imach[5] = 23; imach[6] = -128; imach[7] = 127; imach[8] = 55; imach[9] = -128; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE HP 9000. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -126; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR THE IBM 360/370 SERIES, THE ICL 2900, THE ITEL AS/6, THE XEROX SIGMA 5/7/9 AND THE SEL SYSTEMS 85/86. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 16; imach[5] = 6; imach[6] = -64; imach[7] = 63; imach[8] = 14; imach[9] = -64; imach[10] = 63; */ /* MACHINE CONSTANTS FOR THE IBM PC. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR THE MACINTOSH II - ABSOFT MACFORTRAN II. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR THE MICROVAX - VMS FORTRAN. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -127; imach[7] = 127; imach[8] = 56; imach[9] = -127; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE PDP-10 (KA PROCESSOR). */ /* imach[1] = 2; imach[2] = 35; imach[3] = 34359738367; imach[4] = 2; imach[5] = 27; imach[6] = -128; imach[7] = 127; imach[8] = 54; imach[9] = -101; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE PDP-10 (KI PROCESSOR). */ /* imach[1] = 2; imach[2] = 35; imach[3] = 34359738367; imach[4] = 2; imach[5] = 27; imach[6] = -128; imach[7] = 127; imach[8] = 62; imach[9] = -128; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE PDP-11 FORTRAN SUPPORTING 32-BIT INTEGER ARITHMETIC. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -127; imach[7] = 127; imach[8] = 56; imach[9] = -127; imach[10] = 127; */ /* MACHINE CONSTANTS FOR THE SEQUENT BALANCE 8000. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR THE SILICON GRAPHICS IRIS-4D SERIES (MIPS R3000 PROCESSOR). */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; */ /* MACHINE CONSTANTS FOR IEEE ARITHMETIC MACHINES, SUCH AS THE AT&T 3B SERIES, MOTOROLA 68000 BASED MACHINES (E.G. SUN 3 AND AT&T PC 7300), AND 8087 BASED MICROS (E.G. IBM PC AND AT&T 6300). */ imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -125; imach[7] = 128; imach[8] = 53; imach[9] = -1021; imach[10] = 1024; /* MACHINE CONSTANTS FOR THE UNIVAC 1100 SERIES. */ /* imach[1] = 2; imach[2] = 35; imach[3] = 34359738367; imach[4] = 2; imach[5] = 27; imach[6] = -128; imach[7] = 127; imach[8] = 60; imach[9] = -1024; imach[10] = 1023; */ /* MACHINE CONSTANTS FOR THE VAX 11/780. */ /* imach[1] = 2; imach[2] = 31; imach[3] = 2147483647; imach[4] = 2; imach[5] = 24; imach[6] = -127; imach[7] = 127; imach[8] = 56; imach[9] = -127; imach[10] = 127; */ outval = imach[*i]; return outval ; } /*************************************************************************/ /*************************************************************************/ /************************ End of cdflib inclusion ************************/ /*************************************************************************/ /*************************************************************************/ /*-----------------------------------------------------------------------*/ typedef struct { double p,q ; } pqpair ; /* for returning p=cdf q=1-cdf */ /*-----------------------------------------------------------------------*/ #undef BIGG #define BIGG 9.99e+37 /* a really big number (duh) */ /*-----------------------------------------------------------------------*/ /*************************************************************************/ /******** Internal functions for various statistical computations ********/ /*************************************************************************/ /*--------------------------------------------------------------- F statistic -----------------------------------------------------------------*/ static double fstat_pq2s( pqpair pq , double dofnum , double dofden ) { int which , status ; double p , q , f , dfn , dfd , bound ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return BIGG ; f = 0.0 ; dfn = dofnum ; dfd = dofden ; cdff( &which , &p , &q , &f , &dfn , &dfd , &status , &bound ) ; return f ; } /*------------------------------*/ static pqpair fstat_s2pq( double ff , double dofnum , double dofden ) { int which , status ; double p , q , f , dfn , dfd , bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; f = ff ; if( f <= 0.0 ) return pq; dfn = dofnum ; if( dfn <= 0.0 ) return pq ; dfd = dofden ; if( dfd <= 0.0 ) return pq ; cdff( &which , &p , &q , &f , &dfn , &dfd , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*--------------------------------------------------------------- noncentral F statistic -----------------------------------------------------------------*/ static double fnonc_pq2s( pqpair pq , double dofnum , double dofden , double nonc ) { int which , status ; double p , q , f , dfn , dfd , bound , pnonc ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return BIGG ; f = 0.0 ; dfn = dofnum ; dfd = dofden ; pnonc = nonc ; cdffnc( &which , &p , &q , &f , &dfn , &dfd , &pnonc , &status , &bound ) ; return f ; } /*------------------------------*/ static pqpair fnonc_s2pq( double ff , double dofnum , double dofden , double nonc ) { int which , status ; double p , q , f , dfn , dfd , bound , pnonc ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; f = ff ; if( f <= 0.0 ) return pq ; dfn = dofnum ; if( dfn <= 0.0 ) return pq ; dfd = dofden ; if( dfd <= 0.0 ) return pq ; pnonc = nonc ; if( pnonc < 0.0 ) return pq ; cdffnc( &which , &p , &q , &f , &dfn , &dfd , &pnonc , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*--------------------------------------------------------------- Standard Normal distribution -----------------------------------------------------------------*/ static pqpair normal_s2pq( double zz ) { double p , q , x=zz ; pqpair pq ; cumnor( &x, &p, &q ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double normal_pq2s( pqpair pq ) { double p=pq.p , q=pq.q ; if( p <= 0.0 ) return -BIGG ; if( q <= 0.0 ) return BIGG ; return dinvnr( &p,&q ) ; } /*---------------------------------------------------------------- Chi-square ------------------------------------------------------------------*/ static pqpair chisq_s2pq( double xx , double dof ) { int which , status ; double p,q,x,df,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; x = xx ; if( x <= 0.0 ) return pq ; df = dof ; if( dof <= 0.0 ) return pq ; cdfchi( &which , &p , &q , &x , &df , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double chisq_pq2s( pqpair pq , double dof ) { int which , status ; double p,q,x,df,bound ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return BIGG ; x = 0.0 ; df = dof ; cdfchi( &which , &p , &q , &x , &df , &status , &bound ) ; return x ; } /*---------------------------------------------------------------- noncentral Chi-square ------------------------------------------------------------------*/ static pqpair chsqnonc_s2pq( double xx , double dof , double nonc ) { int which , status ; double p,q,x,df,bound , pnonc ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; x = xx ; if( x <= 0.0 ) return pq ; df = dof ; if( df <= 0.0 ) return pq ; pnonc = nonc ; if( pnonc < 0.0 ) return pq ; cdfchn( &which , &p , &q , &x , &df , &pnonc , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double chsqnonc_pq2s( pqpair pq , double dof , double nonc ) { int which , status ; double p,q,x,df,bound , pnonc ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return BIGG ; x = 0.0 ; df = dof ; pnonc = nonc ; cdfchn( &which , &p , &q , &x , &df , &pnonc , &status , &bound ) ; return x ; } /*---------------------------------------------------------------- Beta distribution ------------------------------------------------------------------*/ static pqpair beta_s2pq( double xx , double aa , double bb ) { int which , status ; double p,q,x,y,a,b,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; x = xx ; if( x <= 0.0 ) return pq ; y = 1.0 - xx ; if( y <= 0.0 ){ pq.p=1.0; pq.q=0.0; return pq; } a = aa ; if( a < 0.0 ) return pq ; b = bb ; if( b < 0.0 ) return pq ; cdfbet( &which , &p , &q , &x , &y , &a , &b , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double beta_pq2s( pqpair pq , double aa , double bb ) { int which , status ; double p,q,x,y,a,b,bound ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return 1.0 ; x = 0.0 ; y = 1.0 ; a = aa ; b = bb ; cdfbet( &which , &p , &q , &x , &y , &a , &b , &status , &bound ) ; return x ; } /*---------------------------------------------------------------- Binomial distribution (that is, the probability that more than ss out of ntrial trials were successful). ------------------------------------------------------------------*/ static pqpair binomial_s2pq( double ss , double ntrial , double ptrial ) { int which , status ; double p,q, s,xn,pr,ompr,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; s = ss ; if( s < 0.0 ) return pq ; xn = ntrial ; if( xn <= 0.0 ) return pq ; pr = ptrial ; if( pr < 0.0 ) return pq ; ompr = 1.0 - ptrial ; cdfbin( &which , &p , &q , &s , &xn , &pr , &ompr , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double binomial_pq2s( pqpair pq , double ntrial , double ptrial ) { int which , status ; double p,q, s,xn,pr,ompr,bound ; which = 2 ; p = pq.p ; q = pq.q ; s = 0.0 ; xn = ntrial ; pr = ptrial ; ompr = 1.0 - ptrial ; cdfbin( &which , &p , &q , &s , &xn , &pr , &ompr , &status , &bound ) ; return s ; } /*---------------------------------------------------------------- Gamma distribution. ------------------------------------------------------------------*/ static pqpair gamma_s2pq( double xx , double sh , double sc ) { int which , status ; double p,q, x,shape,scale,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; x = xx ; if( x <= 0.0 ) return pq ; shape = sh ; if( shape <= 0.0 ) return pq ; scale = sc ; if( scale <= 0.0 ) return pq ; cdfgam( &which , &p , &q , &x , &shape , &scale , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double gamma_pq2s( pqpair pq , double sh , double sc ) { int which , status ; double p,q, x,shape,scale,bound ; which = 2 ; p = pq.p ; if( p <= 0.0 ) return 0.0 ; q = pq.q ; if( q <= 0.0 ) return BIGG ; x = 0.0 ; shape = sh ; scale = sc ; cdfgam( &which , &p , &q , &x , &shape , &scale , &status , &bound ) ; return x ; } /*---------------------------------------------------------------- Poisson distribution ------------------------------------------------------------------*/ static pqpair poisson_s2pq( double xx , double lambda ) { int which , status ; double p,q, s,xlam,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; s = xx ; if( s < 0.0 ) return pq ; xlam = lambda ; if( xlam < 0.0 ) return pq ; cdfpoi( &which , &p , &q , &s , &xlam , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double poisson_pq2s( pqpair pq , double lambda ) { int which , status ; double p,q, s,xlam,bound ; which = 2 ; p = pq.p ; q = pq.q ; s = 0.0 ; xlam = lambda ; cdfpoi( &which , &p , &q , &s , &xlam , &status , &bound ) ; return s ; } /*---------------------------------------------------------------- T distribution. ------------------------------------------------------------------*/ static pqpair student_s2pq( double xx , double dof ) { int which , status ; double p,q, s,xlam,bound ; pqpair pq={0.0,1.0} ; which = 1 ; p = 0.0 ; q = 1.0 ; s = xx ; xlam = dof ; if( xlam <= 0.0 ) return pq ; cdft( &which , &p , &q , &s , &xlam , &status , &bound ) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ double student_pq2s( pqpair pq , double dof ) { int which , status ; double p,q, s,xlam,bound ; which = 2 ; p = pq.p ; q = pq.q ; s = 0.0 ; xlam = dof ; cdft( &which , &p , &q , &s , &xlam , &status , &bound ) ; return s ; } /****************************************************************************/ /* For the distributions below here, cdflib can't do what we want directly. */ /****************************************************************************/ /*---------------------------------------------------------------- Null correlation distribution. Let x = (rr+1)/2; then x is Beta(dof/2,dof/2). ------------------------------------------------------------------*/ static pqpair correl_s2pq( double rr , double dof ) /* fake it with cdflib */ { return beta_s2pq( 0.5*(rr+1.0) , 0.5*dof , 0.5*dof ) ; } /*------------------------------*/ static double correl_pq2s( pqpair pq , double dof ) { double xx = beta_pq2s( pq , 0.5*dof , 0.5*dof ) ; return (2.0*xx-1.0) ; } /*---------------------------------------------------------------- Uniform U(0,1) distribution. ------------------------------------------------------------------*/ static pqpair uniform_s2pq( double xx ) /* this isn't too hard */ { pqpair pq ; if( xx <= 0.0 ) pq.p = 0.0 ; else if( xx >= 1.0 ) pq.p = 1.0 ; else pq.p = xx ; pq.q = 1.0-xx ; return pq ; } /*------------------------------*/ static double uniform_pq2s( pqpair pq ) { return pq.p ; /* that was easy */ } /*---------------------------------------------------------------- standard Logistic distribution. ------------------------------------------------------------------*/ static pqpair logistic_s2pq( double xx ) /* this isn't hard, either */ { pqpair pq ; if( xx >= 0.0 ){ pq.q = 1.0/(1.0+exp( xx)); pq.p = 1.0-pq.q; } else { pq.p = 1.0/(1.0+exp(-xx)); pq.q = 1.0-pq.p; } return pq ; } /*------------------------------*/ static double logistic_pq2s( pqpair pq ) { if( pq.p <= 0.0 ) return -BIGG ; else if( pq.q <= 0.0 ) return BIGG ; if( pq.p < pq.q ) return -log(1.0/pq.p-1.0) ; else return log(1.0/pq.q-1.0) ; } /*---------------------------------------------------------------- standard Laplace distribution. ------------------------------------------------------------------*/ static pqpair laplace_s2pq( double xx ) /* easy */ { pqpair pq ; if( xx >= 0.0 ){ pq.q = 0.5*exp(-xx) ; pq.p = 1.0-pq.q ; } else { pq.p = 0.5*exp( xx) ; pq.q = 1.0-pq.p ; } return pq ; } /*------------------------------*/ static double laplace_pq2s( pqpair pq ) { if( pq.p <= 0.0 ) return -BIGG ; else if( pq.q <= 0.0 ) return BIGG ; if( pq.p < pq.q ) return log(2.0*pq.p) ; else return -log(2.0*pq.q) ; } /*---------------------------------------------------------------- noncentral T distribution = hard calculation ------------------------------------------------------------------*/ /**************************************************************************** Noncentral t distribution function by Professor K. Krishnamoorthy Department of Mathematics University of Louisiana at Lafayette Manually translated from Fortran by RWC. *****************************************************************************/ #if 0 static double alng( double x ) /* log(Gamma(x)) from K */ { int indx ; double xx,fterm,sum,valg ; double b[9] = { 0.0 , 8.33333333333333e-2, 3.33333333333333e-2, 2.52380952380952e-1, 5.25606469002695e-1, 1.01152306812684e0, 1.51747364915329e0, 2.26948897420496e0, 3.00991738325940e0 } ; if( x < 8.0 ){ xx = x + 8.0 ; indx = 1 ; } else { xx = x ; indx = 0 ; } fterm = (xx-0.5)*log(xx) - xx + 9.1893853320467e-1 ; sum = b[1]/(xx+b[2]/(xx+b[3]/(xx+b[4]/(xx+b[5]/(xx+b[6]/ (xx+b[7]/(xx+b[8]))))))) ; valg = sum + fterm ; if(indx) valg = valg-log(x+7.0)-log(x+6.0)-log(x+5.0) -log(x+4.0)-log(x+3.0)-log(x+2.0)-log(x+1.0)-log(x) ; return valg ; } #else static double alng( double x ) /*-- replace with cdflib function --*/ { double xx=x ; return alngam( &xx ) ; } #endif /*---------------------------------------------------------------------------*/ #if 0 static double gaudf( double x ) /* N(0,1) cdf from K */ { static double p0=913.16744211475570 , p1=1024.60809538333800, p2=580.109897562908800, p3=202.102090717023000, p4=46.0649519338751400, p5=6.81311678753268400, p6=6.047379926867041e-1,p7=2.493381293151434e-2 ; static double q0=1826.33488422951125, q1=3506.420597749092, q2=3044.77121163622200, q3=1566.104625828454, q4=523.596091947383490, q5=116.9795245776655, q6=17.1406995062577800, q7=1.515843318555982, q8=6.25e-2 ; static double sqr2pi=2.506628274631001 ; int check ; double reslt,z , first,phi ; if(x > 0.0){ z = x ; check = 1 ; } else { z =-x ; check = 0 ; } if( z > 32.0 ) return (x > 0.0) ? 1.0 : 0.0 ; first = exp(-0.5*z*z) ; phi = first/sqr2pi ; if (z < 7.0) reslt = first* (((((((p7*z+p6)*z+p5)*z+p4)*z+p3)*z+p2)*z+p1)*z+p0) /((((((((q8*z+q7)*z+q6)*z+q5)*z+q4)*z+q3)*z+q2)*z+q1)*z+q0); else reslt = phi/(z+1.0/(z+2.0/(z+3.0/(z+4.0/(z+6.0/(z+7.0)))))) ; if(check) reslt = 1.0 - reslt ; return reslt ; } #else static double gaudf( double x ) /*-- replace with cdflib func --*/ { double xx=x , p,q ; cumnor( &xx, &p, &q ); return p; } #endif /*---------------------------------------------------------------------------*/ #if 0 static double betadf( double x , double p , double q ) /* Beta cdf from K */ { int check , ns ; double result,betf,psq,xx,cx,pp,qq ; double term,ai,rx,temp ; if( x >= 1.0 ) return 1.0 ; if( x <= 0.0 ) return 0.0 ; betf = alng(p)+alng(q)-alng(p+q) ; result=x ; psq=p+q ; cx=1.0-x ; if(p < psq*x){ xx=cx ; cx=x ; pp=q ; qq=p ; check=1 ; } else { xx=x ; pp=p ; qq=q ; check=0 ; } term=1.0 ; ai=1.0 ; result=1.0 ; ns=(int)(qq+cx*psq) ; rx=xx/cx ; L3: temp=qq-ai ; if(ns == 0) rx=xx ; L4: term=term*temp*rx/(pp+ai) ; result=result+term ; temp=fabs(term) ; if(temp <= 1.e-14 && temp <= 1.e-14*result) goto L5 ; ai=ai+1.0 ; ns=ns-1 ; if(ns >= 0) goto L3 ; temp=psq ; psq=psq+1.0 ; goto L4 ; L5: result=result*exp(pp*log(xx)+(qq-1.0)*log(cx)-betf)/pp ; if(check) result=1.0-result ; return result ; } #else static double betadf( double x , double p , double q ) /*-- cdflib func --*/ { double xx=x,yy=1.0-x , aa=p,bb=q , pp,qq ; cumbet( &xx,&yy , &aa,&bb , &pp,&qq ) ; return pp ; } #endif /*---------------------------------------------------------------------------*/ /* Krishnamoorthy's function for cdf of noncentral t, for df > 0, translated into C by RW Cox [Mar 2004]. Note the original fails for delta=0, so we call the cdflib func for this. A couple of other minor fixes are also included. -----------------------------------------------------------------------------*/ static pqpair tnonc_s2pq( double t , double df , double delta ) { int indx , k , i ; double x,del,tnd,ans,y,dels,a,b,c ; double pkf,pkb,qkf,qkb , pgamf,pgamb,qgamf,qgamb ; double pbetaf,pbetab,qbetaf,qbetab ; double ptermf,qtermf,ptermb,qtermb,term ; double rempois,delosq2,sum,cons,error ; pqpair pq={0.0,1.0} ; /* will be return value */ double ab1 ; /*-- stupid user? --*/ if( df <= 0.0 ) return pq ; /*-- non-centrality = 0? --*/ if( fabs(delta) < 1.e-8 ) return student_s2pq(t,df) ; /*-- start K's code here --*/ if( t < 0.0 ){ x = -t ; del = -delta ; indx = 1 ; } /* x will be */ else { x = t ; del = delta ; indx = 0 ; } /* positive */ ans = gaudf(-del) ; /* prob that x <= 0 = Normal cdf */ /*-- the nearly trivial case of x=0 --*/ if( x == 0.0 ){ pq.p = ans; pq.q = 1.0-ans; return pq; } if( df == 1.0 ) df = 1.0000001 ; /** df=1 is BAD **/ y = x*x/(df+x*x) ; /* between 0 and 1 */ dels = 0.5*del*del ; /* will be positive */ k = (int)dels ; /* 0, 1, 2, ... */ a = k+0.5 ; /* might be as small as 0.5 */ c = k+1.0 ; b = 0.5*df ; /* might be as small as 0.0 */ pkf = exp(-dels+k*log(dels)-alng(k+1.0)) ; pkb = pkf ; qkf = exp(-dels+k*log(dels)-alng(k+1.0+0.5)) ; qkb = qkf ; pbetaf = betadf(y, a, b) ; pbetab = pbetaf ; qbetaf = betadf(y, c, b) ; qbetab = qbetaf ; ab1 = a+b-1.0 ; /* might be as small as -0.5 */ /*-- RWCox: if a+b-1 < 0, log(Gamma(a+b-1)) won't work; instead, use Gamma(a+b-1)=Gamma(a+b)/(a+b-1) --*/ if( ab1 > 0.0 ) pgamf = exp(alng(ab1)-alng(a)-alng(b)+(a-1.0)*log(y)+b*log(1.0-y)) ; else pgamf = exp(alng(a+b)-alng(a)-alng(b)+(a-1.0)*log(y)+b*log(1.0-y))/ab1 ; pgamb = pgamf*y*(ab1)/a ; /*-- we can't have c+b-1 < 0, so the above patchup isn't needed --*/ qgamf = exp(alng(c+b-1.0)-alng(c)-alng(b)+(c-1.0)*log(y) + b*log(1.0-y)) ; qgamb = qgamf*y*(c+b-1.0)/c ; rempois = 1.0 - pkf ; delosq2 = del/1.4142135623731 ; sum = pkf*pbetaf+delosq2*qkf*qbetaf ; cons = 0.5*(1.0 + 0.5*fabs(delta)) ; i = 0 ; L1: i = i + 1 ; pgamf = pgamf*y*(a+b+i-2.0)/(a+i-1.0) ; pbetaf = pbetaf - pgamf ; pkf = pkf*dels/(k+i) ; ptermf = pkf*pbetaf ; qgamf = qgamf*y*(c+b+i-2.0)/(c+i-1.0) ; qbetaf = qbetaf - qgamf ; qkf = qkf*dels/(k+i-1.0+1.5) ; qtermf = qkf*qbetaf ; term = ptermf + delosq2*qtermf ; sum = sum + term ; error = rempois*cons*pbetaf ; rempois = rempois - pkf ; if( i > k ){ if( error <= 1.e-12 || i >= 9999 ) goto L2 ; goto L1 ; } else { pgamb = pgamb*(a-i+1.0)/(y*(a+b-i)) ; pbetab = pbetab + pgamb ; pkb = (k-i+1.0)*pkb/dels ; ptermb = pkb*pbetab ; qgamb = qgamb*(c-i+1.0)/(y*(c+b-i)) ; qbetab = qbetab + qgamb ; qkb = (k-i+1.0+0.5)*qkb/dels ; qtermb = qkb*qbetab ; term = ptermb + delosq2*qtermb ; sum = sum + term ; rempois = rempois - pkb ; if (rempois <= 1.e-12 || i >= 9999) goto L2 ; goto L1 ; } L2: tnd = 0.5*sum + ans ; /*-- return a pqpair, not just the cdf --*/ if( indx ){ pq.p = 1.0-tnd; pq.q = tnd ; } else { pq.p = tnd ; pq.q = 1.0-tnd; } return pq ; } /*------------------------------*/ /* Inverse to above function; uses cdflib dstinv()/dinvr() to solve the equation. --------------------------------*/ static double tnonc_pq2s( pqpair pq , double dof , double nonc ) { double t ; /* will be result */ double tbot,ttop , dt ; double T6=1.e-50,T7=1.e-8 ; double K4=0.5,K5=5.0 ; double fx ; unsigned long qhi,qleft ; int status , qporq , ite ; pqpair tpq ; if( dof <= 0.0 ) return BIGG ; /* bad user */ if( pq.p <= 0.0 ) return -BIGG ; if( pq.q <= 0.0 ) return BIGG ; t = student_pq2s(pq,dof) ; /* initial guess */ if( fabs(nonc) < 1.e-8 ) return t ; t += 0.5*nonc ; /* adjust up or down */ dt = 0.1 * fabs(t) ; if( dt < 1.0 ) dt = 1.0 ; /* stepsize */ /* scan down for lower bound, below which cdf is < p */ tbot = t ; for( ite=0 ; ite < 1000 ; ite++ ){ tpq = tnonc_s2pq( tbot , dof , nonc ) ; if( tpq.p <= pq.p ) break ; tbot -= dt ; } if( ite >= 1000 ) return -BIGG ; /* scan up for upper bound, above which cdf is > p */ ttop = tbot+0.5*dt ; for( ite=0 ; ite < 1000 ; ite++ ){ tpq = tnonc_s2pq( ttop , dof , nonc ) ; if( tpq.p >= pq.p ) break ; ttop += dt ; } if( ite >= 1000 ) return BIGG ; t = 0.5*(tbot+ttop) ; /* initial guess in middle */ /* initialize searching parameters */ dstinv(&tbot,&ttop,&K4,&K4,&K5,&T6,&T7); status = 0 ; qporq = (pq.p <= pq.q) ; while(1){ dinvr(&status,&t,&fx,&qleft,&qhi) ; if( status != 1 ) return t ; /* done! */ tpq = tnonc_s2pq( t , dof , nonc ) ; /* get cdf */ /* goal of dinvr is to drive fx to zero */ fx = (qporq) ? pq.p-tpq.p : pq.q-tpq.q ; } return BIGG ; /* unreachable */ } /*---------------------------------------------------------------- Chi distribution (sqrt of chi-squared, duh). ------------------------------------------------------------------*/ static pqpair chi_s2pq( double xx , double dof ) { pqpair pq={0.0,1.0} ; if( xx <= 0.0 || dof <= 0.0 ) return pq ; return chisq_s2pq( xx*xx , dof ) ; } /*------------------------------*/ static double chi_pq2s( pqpair pq , double dof ) { if( pq.p <= 0.0 ) return 0.0 ; if( pq.q <= 0.0 ) return BIGG ; return sqrt(chisq_pq2s(pq,dof)) ; } /*---------------------------------------------------------------- Extreme value type I: cdf(x) = exp(-exp(-x)). ------------------------------------------------------------------*/ static pqpair extval1_s2pq( double x ) { double p,q,y ; pqpair pq ; if( x > -5.0 ){ y = exp(-x) ; p = exp(-y) ; } else { y = 1.0 ; p = 0.0 ; } if( y >= 1.e-4 ) q = 1.0-p ; else q = y*(1.0+y*(-0.5+y*(1.0/6.0-y/24.0))) ; pq.p = p ; pq.q = q ; return pq ; } /*------------------------------*/ static double extval1_pq2s( pqpair pq ) { if( pq.p <= 0.0 ) return -BIGG ; else if( pq.p >= 1.0 ) return BIGG ; return -log(-log(pq.p)) ; } /*---------------------------------------------------------------- Weibull distribution: cdf(x) = 1 - exp( -x^c ) for x>0 and c>0. ------------------------------------------------------------------*/ static pqpair weibull_s2pq( double x , double c ) { double y ; pqpair pq={0.0,1.0} ; if( x <= 0.0 || c <= 0.0 ) return pq ; y = pow(x,c) ; pq.q = exp(-y) ; if( y >= 1.e-4 ) pq.p = 1.0-pq.q ; else pq.p = y*(1.0+y*(-0.5+y*(1.0/6.0-y/24.0))) ; return pq ; } /*------------------------------*/ static double weibull_pq2s( pqpair pq , double c ) { if( pq.p <= 0.0 || c <= 0.0 ) return 0.0 ; else if( pq.q <= 0.0 ) return BIGG ; return pow( -log(pq.q) , 1.0/c ) ; } /*---------------------------------------------------------------- Inverse Gaussian: density proportional to exp(-0.5*c(x+1/x))/x^1.5 (x,c >0). ------------------------------------------------------------------*/ static pqpair invgauss_s2pq( double x, double c ) { double y , p1,q1 , p2,q2 , v ; pqpair pq={0.0,1.0} ; if( x <= 0.0 || c <= 0.0 ) return pq ; y = sqrt(c/x) ; v = y*(x-1.0) ; cumnor( &v , &p1,&q1 ) ; v = -y*(x+1.0) ; cumnor( &v , &p2,&q2 ) ; pq.p = p1 ; if( p2 > 0.0 ) pq.p += exp(2.0*c+log(p2)) ; pq.q = 1.0-pq.p ; return pq ; } /*------------------------------*/ /* Inverse to above function; uses cdflib dstinv()/dinvr() to solve the equation. --------------------------------*/ static double invgauss_pq2s( pqpair pq , double c ) { double t ; /* will be result */ double tbot,ttop , dt ; double T6=1.e-50,T7=1.e-8 ; double K4=0.5,K5=5.0 ; double fx ; unsigned long qhi,qleft ; int status , qporq , ite ; pqpair tpq ; if( c <= 0.0 ) return BIGG ; /* bad user */ if( pq.p <= 0.0 ) return 0.0 ; if( pq.q <= 0.0 ) return BIGG ; /* initial guess is t=1; scan down for lower bound */ tbot = 1.01 ; dt = 0.9 ; for( ite=0 ; ite < 1000 ; ite++ ){ tpq = invgauss_s2pq( tbot , c ) ; if( tpq.p <= pq.p ) break ; tbot *= dt ; } if( ite >= 1000 ) return 0.0 ; /* scan up for upper bound */ dt = 1.1 ; ttop = tbot*dt ; for( ite=0 ; ite < 1000 ; ite++ ){ tpq = invgauss_s2pq( ttop , c ) ; if( tpq.p >= pq.p ) break ; ttop *= dt ; } if( ite >= 1000 ) return BIGG ; t = sqrt(tbot*ttop) ; /* start at geometric mean */ /* initialize searching parameters */ dstinv(&tbot,&ttop,&K4,&K4,&K5,&T6,&T7); status = 0 ; qporq = (pq.p <= pq.q) ; while(1){ dinvr(&status,&t,&fx,&qleft,&qhi) ; if( status != 1 ) return t ; /* done! */ tpq = invgauss_s2pq( t , c ) ; /* goal is to drive fx to zero */ fx = (qporq) ? pq.p-tpq.p : pq.q-tpq.q ; } return BIGG ; /* unreachable */ } /*--------------------------------------------------------------------------*/ /*! Given a value, calculate both its cdf and reversed cdf (1.0-cdf). If an error occurs, you'll probably get back {0.0,1.0}. All the actual work is done in utility functions for each distribution. ----------------------------------------------------------------------------*/ static pqpair stat2pq( double val, int code, double p1,double p2,double p3 ) { pqpair pq={0.0,1.0} ; switch( code ){ case NIFTI_INTENT_CORREL: pq = correl_s2pq ( val, p1 ) ; break; case NIFTI_INTENT_TTEST: pq = student_s2pq ( val, p1 ) ; break; case NIFTI_INTENT_FTEST: pq = fstat_s2pq ( val, p1,p2 ) ; break; case NIFTI_INTENT_ZSCORE: pq = normal_s2pq ( val ) ; break; case NIFTI_INTENT_CHISQ: pq = chisq_s2pq ( val, p1 ) ; break; case NIFTI_INTENT_BETA: pq = beta_s2pq ( val, p1,p2 ) ; break; case NIFTI_INTENT_BINOM: pq = binomial_s2pq( val, p1,p2 ) ; break; case NIFTI_INTENT_GAMMA: pq = gamma_s2pq ( val, p1,p2 ) ; break; case NIFTI_INTENT_POISSON: pq = poisson_s2pq ( val, p1 ) ; break; case NIFTI_INTENT_FTEST_NONC: pq = fnonc_s2pq ( val, p1,p2,p3 ); break; case NIFTI_INTENT_CHISQ_NONC: pq = chsqnonc_s2pq( val, p1,p2 ); break; case NIFTI_INTENT_TTEST_NONC: pq = tnonc_s2pq ( val, p1,p2 ) ; break; case NIFTI_INTENT_CHI: pq = chi_s2pq ( val, p1 ) ; break; /* these distributions are shifted and scaled copies of a standard case */ case NIFTI_INTENT_INVGAUSS: if( p1 > 0.0 && p2 > 0.0 ) pq = invgauss_s2pq( val/p1,p2/p1 ) ; break; case NIFTI_INTENT_WEIBULL: if( p2 > 0.0 && p3 > 0.0 ) pq = weibull_s2pq ((val-p1)/p2,p3) ; break; case NIFTI_INTENT_EXTVAL: if( p2 > 0.0 ) pq = extval1_s2pq ( (val-p1)/p2 ) ; break; case NIFTI_INTENT_NORMAL: if( p2 > 0.0 ) pq = normal_s2pq ( (val-p1)/p2 ) ; break; case NIFTI_INTENT_LOGISTIC: if( p2 > 0.0 ) pq = logistic_s2pq( (val-p1)/p2 ) ; break; case NIFTI_INTENT_LAPLACE: if( p2 > 0.0 ) pq = laplace_s2pq ( (val-p1)/p2 ) ; break; case NIFTI_INTENT_UNIFORM: if( p2 > p1 ) pq = uniform_s2pq((val-p1)/(p2-p1)); break; /* this case is trivial */ case NIFTI_INTENT_PVAL: pq.p = 1.0-val ; pq.q = val ; break; } return pq ; } /*--------------------------------------------------------------------------*/ /*! Given a pq value (cdf and 1-cdf), compute the value that gives this. If an error occurs, you'll probably get back a BIGG number. All the actual work is done in utility functions for each distribution. ----------------------------------------------------------------------------*/ static double pq2stat( pqpair pq, int code, double p1,double p2,double p3 ) { double val=BIGG ; if( pq.p < 0.0 || pq.q < 0.0 || pq.p > 1.0 || pq.q > 1.0 ) return val ; switch( code ){ case NIFTI_INTENT_CORREL: val = correl_pq2s ( pq , p1 ) ; break; case NIFTI_INTENT_TTEST: val = student_pq2s ( pq , p1 ) ; break; case NIFTI_INTENT_FTEST: val = fstat_pq2s ( pq , p1,p2 ) ; break; case NIFTI_INTENT_ZSCORE: val = normal_pq2s ( pq ) ; break; case NIFTI_INTENT_CHISQ: val = chisq_pq2s ( pq , p1 ) ; break; case NIFTI_INTENT_BETA: val = beta_pq2s ( pq , p1,p2 ) ; break; case NIFTI_INTENT_BINOM: val = binomial_pq2s( pq , p1,p2 ) ; break; case NIFTI_INTENT_GAMMA: val = gamma_pq2s ( pq , p1,p2 ) ; break; case NIFTI_INTENT_POISSON: val = poisson_pq2s ( pq , p1 ) ; break; case NIFTI_INTENT_FTEST_NONC: val = fnonc_pq2s ( pq , p1,p2,p3 ); break; case NIFTI_INTENT_CHISQ_NONC: val = chsqnonc_pq2s( pq , p1,p2 ); break; case NIFTI_INTENT_TTEST_NONC: val = tnonc_pq2s ( pq , p1,p2 ) ; break; case NIFTI_INTENT_CHI: val = chi_pq2s ( pq , p1 ) ; break; /* these distributions are shifted and scaled copies of a standard case */ case NIFTI_INTENT_INVGAUSS: if( p1 > 0.0 && p2 > 0.0 ) val = p1*invgauss_pq2s ( pq,p2/p1); break; case NIFTI_INTENT_WEIBULL: if( p2 > 0.0 && p3 > 0.0 ) val = p1+p2*weibull_pq2s ( pq, p3 ) ; break; case NIFTI_INTENT_EXTVAL: if( p2 > 0.0 ) val = p1+p2*extval1_pq2s ( pq ) ; break; case NIFTI_INTENT_NORMAL: if( p2 > 0.0 ) val = p1+p2*normal_pq2s ( pq ) ; break; case NIFTI_INTENT_LOGISTIC: if( p2 > 0.0 ) val = p1+p2*logistic_pq2s( pq ) ; break; case NIFTI_INTENT_LAPLACE: if( p2 > 0.0 ) val = p1+p2*laplace_pq2s ( pq ) ; break; case NIFTI_INTENT_UNIFORM: if( p2 > p1 ) val = p1+(p2-p1)*uniform_pq2s(pq) ; break; /* this case is trivial */ case NIFTI_INTENT_PVAL: val = pq.q ; break; } return val ; } /****************************************************************************/ /*[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]*/ /*..........................................................................*/ /*............. AT LAST! Functions to be called by the user! ..............*/ /*..........................................................................*/ /*[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]*/ /****************************************************************************/ /**************************************************************************** Statistical codes implemented here: NIFTI_INTENT_CORREL = correlation statistic NIFTI_INTENT_TTEST = t statistic (central) NIFTI_INTENT_FTEST = F statistic (central) NIFTI_INTENT_ZSCORE = N(0,1) statistic NIFTI_INTENT_CHISQ = Chi-squared (central) NIFTI_INTENT_BETA = Beta variable (central) NIFTI_INTENT_BINOM = Binomial variable NIFTI_INTENT_GAMMA = Gamma distribution NIFTI_INTENT_POISSON = Poisson distribution NIFTI_INTENT_FTEST_NONC = noncentral F statistic NIFTI_INTENT_CHISQ_NONC = noncentral chi-squared NIFTI_INTENT_TTEST_NONC = noncentral t statistic NIFTI_INTENT_CHI = Chi statistic (central) NIFTI_INTENT_INVGAUSS = inverse Gaussian variable NIFTI_INTENT_WEIBULL = Weibull distribution NIFTI_INTENT_EXTVAL = Extreme value type I NIFTI_INTENT_NORMAL = N(mu,variance) normal NIFTI_INTENT_LOGISTIC = Logistic distribution NIFTI_INTENT_LAPLACE = Laplace distribution NIFTI_INTENT_UNIFORM = Uniform distribution NIFTI_INTENT_PVAL = "p-value" *****************************************************************************/ static char *inam[]={ NULL , NULL , "CORREL" , "TTEST" , "FTEST" , "ZSCORE" , "CHISQ" , "BETA" , "BINOM" , "GAMMA" , "POISSON" , "NORMAL" , "FTEST_NONC" , "CHISQ_NONC" , "LOGISTIC" , "LAPLACE" , "UNIFORM" , "TTEST_NONC" , "WEIBULL" , "CHI" , "INVGAUSS" , "EXTVAL" , "PVAL" , NULL } ; #include #include /*--------------------------------------------------------------------------*/ /*! Given a string name for a statistic, return its integer code. Returns -1 if not found. ----------------------------------------------------------------------------*/ int nifti_intent_code( char *name ) { char *unam , *upt ; int ii ; if( name == NULL || *name == '\0' ) return -1 ; unam = strdup(name) ; for( upt=unam ; *upt != '\0' ; upt++ ) *upt = (char)toupper(*upt) ; for( ii=NIFTI_FIRST_STATCODE ; ii <= NIFTI_LAST_STATCODE ; ii++ ) if( strcmp(inam[ii],unam) == 0 ) break ; free(unam) ; return (ii <= NIFTI_LAST_STATCODE) ? ii : -1 ; } /*--------------------------------------------------------------------------*/ /*! Given a value, return its cumulative distribution function (cdf): - val = statistic - code = NIFTI_INTENT_* statistical code - p1,p2,p3 = parameters of the distribution If an error occurs, you'll probably get back 0.0. ----------------------------------------------------------------------------*/ double nifti_stat2cdf( double val, int code, double p1,double p2,double p3 ) { pqpair pq ; pq = stat2pq( val, code, p1,p2,p3 ) ; return pq.p ; } /*--------------------------------------------------------------------------*/ /*! Given a value, return its reversed cumulative distribution function (1-cdf): - val = statistic - code = NIFTI_INTENT_* statistical code - p1,p2,p3 = parameters of the distribution If an error transpires, you'll probably get back 1.0. ----------------------------------------------------------------------------*/ double nifti_stat2rcdf( double val, int code, double p1,double p2,double p3 ) { pqpair pq ; pq = stat2pq( val, code, p1,p2,p3 ) ; return pq.q ; } /*--------------------------------------------------------------------------*/ /*! Given a cdf probability, find the value that gave rise to it. - p = cdf; 0 < p < 1 - code = NIFTI_INTENT_* statistical code - p1,p2,p3 = parameters of the distribution If an error transpires, you'll probably get back a BIGG number. ----------------------------------------------------------------------------*/ double nifti_cdf2stat( double p , int code, double p1,double p2,double p3 ) { pqpair pq ; pq.p = p ; pq.q = 1.0-p ; return pq2stat(pq,code,p1,p2,p3) ; } /*--------------------------------------------------------------------------*/ /*! Given a reversed cdf probability, find the value that gave rise to it. - q = 1-cdf; 0 < q < 1 - code = NIFTI_INTENT_* statistical code - p1,p2,p3 = parameters of the distribution If an error transpires, you'll probably get back a BIGG number. ----------------------------------------------------------------------------*/ double nifti_rcdf2stat( double q , int code, double p1,double p2,double p3 ) { pqpair pq ; pq.p = 1.0-q ; pq.q = q ; return pq2stat(pq,code,p1,p2,p3) ; } /*--------------------------------------------------------------------------*/ /*! Given a statistic, compute a z-score from it. That is, the output is z such that cdf(z) of a N(0,1) variable is the same as the cdf of the given distribution at val. ----------------------------------------------------------------------------*/ double nifti_stat2zscore( double val , int code, double p1,double p2,double p3 ) { pqpair pq ; if( code == NIFTI_INTENT_ZSCORE ) return val ; /* trivial */ if( code == NIFTI_INTENT_NORMAL ) return (val-p1)/p2 ; /* almost so */ pq = stat2pq( val, code, p1,p2,p3 ) ; /* find cdf */ return normal_pq2s( pq ) ; /* find z */ } /*--------------------------------------------------------------------------*/ /*! Given a statistic, compute a half-z-score from it. That is, the output is z such that cdf(z) of a half-N(0,1) variable is the same as the cdf of the given distribution at val. A half-N(0,1) variable has density zero for z < 0 and twice the usual N(0,1) density for z > 0. ----------------------------------------------------------------------------*/ double nifti_stat2hzscore( double val, int code, double p1,double p2,double p3 ) { pqpair pq ; pq = stat2pq( val, code, p1,p2,p3 ) ; /* find cdf */ pq.q = 0.5*(1.0-pq.p) ; pq.p = 0.5*(1.0+pq.p) ; /* mangle it */ return normal_pq2s( pq ) ; /* find z */ } /****************************************************************************/ /*[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]*/ /****************************************************************************/ /*--------------------------------------------------------------------------*/ /* Sample program to test the above functions. Otherwise unimportant. ----------------------------------------------------------------------------*/ int main( int argc , char *argv[] ) { double val , p , q , p1=0.0,p2=0.0,p3=0.0 ; double vbot,vtop,vdel ; int code , iarg=1 , doq=0 , dod=0 , doi=0 , doz=0 , doh=0 ; /*-- print some help for the pitiful user --*/ if( argc < 3 || strstr(argv[1],"help") != NULL ){ int ii ; printf("\n") ; printf("Demo program for computing NIfTI statistical functions.\n") ; printf("Usage: nifti_stats [-q|-d|-1|-z] val CODE [p1 p2 p3]\n") ; printf(" val can be a single number or in the form bot:top:step.\n") ; printf(" default ==> output p = Prob(statistic < val).\n") ; printf(" -q ==> output is 1-p.\n") ; printf(" -d ==> output is density.\n") ; printf(" -1 ==> output is x such that Prob(statistic < x) = val.\n") ; printf(" -z ==> output is z such that Normal cdf(z) = p(val).\n") ; printf(" -h ==> output is z such that 1/2-Normal cdf(z) = p(val).\n"); printf(" Allowable CODEs:\n") ; for( ii=NIFTI_FIRST_STATCODE ; ii <= NIFTI_LAST_STATCODE ; ii++ ){ printf(" %-10s",inam[ii]); if((ii-NIFTI_FIRST_STATCODE)%6==5)printf("\n"); } printf("\n") ; printf(" Following CODE are distributional parameters, as needed.\n"); printf("\n") ; printf("Results are written to stdout, 1 number per output line.\n") ; printf("Example (piping output into AFNI program 1dplot):\n") ; printf(" nifti_stats -d 0:4:.001 INVGAUSS 1 3 | 1dplot -dx 0.001 -stdin\n"); printf("\n") ; printf("Author - RW Cox - SSCC/NIMH/NIH/DHHS/USA/EARTH - March 2004\n") ; printf("\n") ; exit(0) ; } /*-- check first arg to see if it is an output option; if so, set the appropriate output flag to determine what to compute --*/ if( strcmp(argv[iarg],"-q") == 0 ){ doq = 1 ; iarg++ ; } else if( strcmp(argv[iarg],"-d") == 0 ){ dod = 1 ; iarg++ ; } else if( strcmp(argv[iarg],"-1") == 0 ){ doi = 1 ; iarg++ ; } else if( strcmp(argv[iarg],"-z") == 0 ){ doz = 1 ; iarg++ ; } else if( strcmp(argv[iarg],"-h") == 0 ){ doh = 1 ; iarg++ ; } /*-- get the value(s) to process --*/ vbot=vtop=vdel = 0.0 ; sscanf( argv[iarg++] , "%lf:%lf:%lf" , &vbot,&vtop,&vdel ) ; if( vbot >= vtop ) vdel = 0.0 ; if( vdel <= 0.0 ) vtop = vbot ; /*-- decode the CODE into the integer signifying the distribution --*/ code = nifti_intent_code(argv[iarg++]) ; if( code < 0 ){ fprintf(stderr,"illegal code=%s\n",argv[iarg-1]); exit(1); } /*-- get the parameters, if present (defaults are 0) --*/ if( argc > iarg ) p1 = strtod(argv[iarg++],NULL) ; if( argc > iarg ) p2 = strtod(argv[iarg++],NULL) ; if( argc > iarg ) p3 = strtod(argv[iarg++],NULL) ; /*-- loop over input value(s), compute output, write to stdout --*/ for( val=vbot ; val <= vtop ; val += vdel ){ if( doq ) /* output = 1-cdf */ p = nifti_stat2rcdf( val , code,p1,p2,p3 ) ; else if( dod ) /* output = density */ p = 1000.0*( nifti_stat2cdf(val+.001,code,p1,p2,p3) -nifti_stat2cdf(val ,code,p1,p2,p3)) ; else if( doi ) /* output = inverse */ p = nifti_cdf2stat( val , code,p1,p2,p3 ) ; else if( doz ) /* output = z score */ p = nifti_stat2zscore( val , code,p1,p2,p3 ) ; else if( doh ) /* output = halfz score */ p = nifti_stat2hzscore( val , code,p1,p2,p3 ) ; else /* output = cdf */ p = nifti_stat2cdf( val , code,p1,p2,p3 ) ; printf("%.9g\n",p) ; if( vdel <= 0.0 ) break ; /* the case of just 1 value */ } /*-- terminus est --*/ exit(0) ; } minc-tools-2.3.00+dfsg/conversion/nifti1/nifti1_local.h0000644000175000000620000000321312574624760021715 0ustar stevestaff/* Additional things we define for the NIfTI-1 format. Some of these * should probably be included in the header files. */ /* NIfTI-1 (Analyze 7.5) files can have at most 8 dimensions. This should * be defined in nifti1.h */ #define MAX_NII_DIMS 8 /* Length of NIfTI-1 description field. Should be defined in nifti1.h */ #define MAX_NII_DESCRIP 80 /* File types. These should be part of the nifti1_io.h header. */ #define FT_UNSPECIFIED (-1) #define FT_ANALYZE 0 #define FT_NIFTI_SINGLE 1 #define FT_NIFTI_DUAL 2 #define FT_NIFTI_ASCII 3 /****** * Private stuff we use for mapping NIfTI-1 dimensions onto MINC dimensions. **/ /* # spatial dimensions */ #define MAX_SPACE_DIMS 3 /* World coordinates */ #define DIM_X 0 #define DIM_Y 1 #define DIM_Z 2 /* Voxel coordinates */ #define DIM_I 0 #define DIM_J 1 #define DIM_K 2 #define DIMORDER_ZYX 0 #define DIMORDER_YZX 1 #define DIMORDER_XZY 2 #define DIMORDER_XYZ 3 #define DIMORDER_ZXY 4 #define DIMORDER_YXZ 5 /* Map dimension index from the actual mapping of the data array to the * "internal header array order". * * In other words, NIfTI-1 seems to store the lengths of dimensions in this * order: X, Y, Z, T, V in the dim[8] entry. * But data is actually stored with the vector dimension varying _slowest_, * with the X dimension varying _fastest_, i.e. as if it were a C array * declared array[V][T][Z][Y][X]; */ static const int dimmap[MAX_NII_DIMS] = { 4, 3, 2, 1, 0, -1, -1, -1 }; /* Names of MINC spatial dimensions, in our "standard" world ordering. */ static const char *mnc_spatial_names[MAX_SPACE_DIMS] = { MIxspace, MIyspace, MIzspace }; minc-tools-2.3.00+dfsg/conversion/nifti1/mnc2nii.man10000644000175000000620000000531012574624760021315 0ustar stevestaff.TH mnc2mnc 1 "Apr 22 2005" "$Revision: 1.3 $" "" .SH NAME .B mnc2nii - convert a MINC format file to a NIfTI-1 or Analyze format file. .SH SYNOPSIS .B mnc2nii .I [] .B mnc2nii .I -help .SH DESCRIPTION The .B mnc2nii command is used to convert MINC format files to either NIfTI-1 or Analyze format. The NIfTI-1 format was developed by the members of the Neuroinformatics Technology Initiative's Data Format Working Group (DFWG). The NIfTI-1 format is based upon the Mayo Clinic's Analyze 7.5 format. The name of the program is derived from the common filename suffixes used for NIfTI-1 and MINC files. NIfTI-1 defines two possible formats, a "header plus raw image" 2-file format, and a single-file format that includes both header information and the image data. As with Analyze 7.5, the 2-file format consists of one file with the suffix ".hdr" and another file with the extension ".img". In NIfTI-1 single-file format, the two files may be combined into a single file with a ".nii" filename suffix. In addition to the NIfTI-1 formats, .B mnc2nii can convert MINC files to the Analyze 7.5 format. .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .SH "Output file format" .TP .BI -dual Save file in NIfTI-1 two-file format (.hdr and .img) .TP .BI -ASCII Save file in non-standard NIfTI-1 ASCII file format .TP .BI -nii Save file in NIfTI-1 single-file format (.nii) .TP .BI -analyze Save file in Analyze 7.5 compatible format .SH "Output voxel format" .TP .BI -float Save voxels in 32-bit floating point format .TP .BI -double Save voxels in 64-bit floating point format .TP .BI -byte Save voxels in 8-bit integer format .TP .BI -short Save voxels in 16-bit integer format .TP .BI -int Save voxels in 32-bit integer format .TP .BI -signed Save voxels in signed (2's complement) integer format .TP .BI -unsigned Save voxels in unsigned integer format .SH "Other options" .TP .BI -noscanrange Don't scan data to determine valid range. .TP .BI -quiet Quiet operation - do not print progress or debugging information. .SH "Generic options for all commands" .TP .BI -help Print summary of command\-line options and abort .TP .BI -version Print the program and library versions and abort .SH "KNOWN BUGS" Current handling of NIfTI-1 qform and sform coordinate transforms should probably be revised as the NIfTI group clarifies the correct usage of these fields. .SH "SEE ALSO" .IR nii2mnc .SH AUTHOR Robert Vincent (bert@bic.mni.mcgill.ca) with assistance from the NIfTI-1 library authored by Robert Cox et al. .SH "COPYRIGHTS" Copyrights 2005 by Robert Vincent for the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/0002755000175000000620000000000012574624760021567 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/t/0002755000175000000620000000000012574624760022032 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/t/test.t0000644000175000000620000000071112574624760023173 0ustar stevestaff# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl siemens_mosaic2mnc.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use Test::More tests => 1; BEGIN { }; ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/Makefile.PL0000644000175000000620000000141412574624760023537 0ustar stevestaffuse ExtUtils::MakeMaker; # # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # # If any modules outside of the core perl distribution are required, # these should be included as a PREREQ_PM entry in WriteMakefile below, # as indicated in the example. This example requires the modules # MOD1 and MOD2 to be installed, with minimal versions 1 and 5, # respectively. If the version number is 0, any version is sufficient. require 5.004; WriteMakefile( NAME => 'siemens_mosaic2mnc', VERSION_FROM => 'siemens_mosaic2mnc', PREREQ_PM => {'Getopt::Tabular' => 0.3}, 'dist' => { COMPRESS => 'gzip', SUFFIX => 'gz' }, 'EXE_FILES' => [ 'siemens_mosaic2mnc' ], ); minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/INSTALL0000644000175000000620000000111512574624760022614 0ustar stevestaff To install the script and man pages in the standard areas, give the sequence of commands perl Makefile.PL make make test make install If you want to install the script in your own private space, use perl Makefile.PL PREFIX=/my/private/perllib \ INSTALLMAN1DIR=/my/private/perllib/man/man1 \ INSTALLMAN3DIR=/my/private/perllib/man/man3 make make test make install Any libraries installed in such non-standard places may then need to have the appropriate path to them specified in the script. Note that `make test` may not necessarily be enabled. minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/ChangeLog0000644000175000000620000000066412574624760023345 0ustar stevestaffRevision history for Perl script siemens_mosaic2mnc 0.1 2002-10-24 Andrew L. Janke - initial version based upon guesswork 0.2 2002-10-31 Andrew L. Janke - somewhat working version with starts and steps 0.9 2004-10-09 Andrew L. Janke - first version with working direction cosines and starts - inital release to minc-users minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/ASCCONV_info0000644000175000000620000001764412574624760023633 0ustar stevestaff> From: Doug Greve > Date: Wed Nov 27, 2002 4:57:31 AM Australia/Brisbane > To: FSL@JISCMAIL.AC.UK > Subject: Re: [FSL] Siemens Allegra file formats > Reply-To: "FSL - FMRIB's Software Library" > > > I have written routines which will convert NUMARIS4 DICOM (including > mosaics) to analyze format (NUMARIS4 is the Siemens' WinNT system). The > routines are part of mri_convert, which is available with FreeSurfer > (surfer.nmr.mgh.harvard.edu). I've also attached a file with the rules > that I use to read and decode the DICOM. These rules refer to the > "ASCII > Header". Siemens DICOM files have some data stored as a > block of ASCII text which contains information needed to decode the > mosaics (both matrix size and geometry). Each line in the block has the > form: > VariableName = VariableValue > The begining of the block is delineated by the line > ### ASCCONV BEGIN ### > The end of the block is delineated by the line > ### ASCCONV END ### > > good luck! > > doug > > > Greg Miller wrote: >> >> Question for Siemens Allegra 3T sites - Our console will be upgraded >> from SunOS to WinNT soon. We'd like to get a head start to ensure that >> we know what to do with the raw data files, in terms of file >> formats. Our group uses FSL, SPM, and MEDx. Any advice, URLs, code, >> whatever would be appreciated. >> >> Gregory A. Miller, Director >> Biomedical Imaging Center >> University of Illinois at Urbana-Champaign > > -- > Douglas N. Greve, Ph.D. > MGH-NMR Center > greve@nmr.mgh.harvard.edu > Phone Number: 617-724-2358 > Fax: 617-726-7422 > > Critical Parameters for a given series, and the rules Doug Greve > currently uses to determine them: > > 1. Files that belong to a series. > > Read the series number from the DICOM header (20,11). This > apparently won't work when "Multiple Series" is checked. > > 2. Whether each file contains an individual image, a mosaic, or > supermosaic. > > I use the Phase Encode FoV ("sSliceArray.asSlice[0].dPhaseFOV") and > the Readout FoV ("sSliceArray.asSlice[0].dReadoutFOV") from the > ASCII header, the Phase Encode Direction (18,1312), and the row and > column resolutions (28,30) to compute an expected number of rows > and columns. I then compare these numbers to the number of rows > (28,10) and columns (28,11)in the image. If they are the same, then > it is not a mosaic or supermosaic. If they are not, I assume it's a > mosaic. I don't know how to tell if its a supermosaic. > > 3. If a file contains a mosaic, the number of rows and cols in the > mosaic. > > This is just the number of rows (28,10) and columns (28,11) in the > image. > > 4. If a file contains a supermosaic, the number of mosaics in the > super mosaic (as well as the dimension of each mosaic). ??? > > 5. Volume dimensions (ie, number of rows, columns, slices). > > a. Non-mosaics - the number of rows and columns are determined > directly from the DICOM header ((28,10) and (28,11)). The number of > slices is determined by counting the number of files in the series > with different slice prescriptions. > > b. Mosaics - the number of rows and columns are determined from the > Phase Encode and Readout FoVs as described in #2. The number of > slices is determined from the ASCII header (sSliceArray.lSize). > > c. Supermosaics - ??? > > 6. Volume resolution (ie, distance between the centers of adjacent > rows, cols, and slices). > > For rows and columns, the resolutions are obtained from DICOM > (28,30), which is a string of the form "colres\rowres". The > distance between slices is obtained from DICOM (18,88). If that > does not exist, then the slice thickness (18,50) is used, but this > will not include the distance factor or gap. Supposedly, the skip > can be obtained from (21,1344), but special software is needed to > read odd DICOM groups. There is also an element in the ASCII header > ("sGroupArray.asGroup[0].dDistFact"). One can also compute the > slice resolution as the distance between adjacent slices using > "sSliceArray.asSlice[N].sPosition.dAAA" where AAA is Sag (x), Cor > (y), and Tra (z) from the ASCII header. > > 7. The direction cosines (DCs) for the row, col, and slice. > > The DCs for the row and column are obtained from DICOM (20,37), > which is a string of the form "cx\cy\cz\rx\ry\rz". The slice DC is > obtained from the ASCII header. The x, y, and z components are from > three lines of the form "sSliceArray.asSlice[0].sNormal.dAAA" where > AAA is Sag (x), Cor (y), and Tra (z). > > 8. The XYZ coordinates at the exact center of a voxel at a given row, > col, and slice. > > a. Non-mosaics - DICOM (20,32) gives the XYZ coordinate at the > first voxel of the image. > > b. Mosaics - DICOM (20,32) is incorrect for mosaics. However, the > center of a slice can be obtained from the ASCII header from lines > of the form "sSliceArray.asSlice[N].sPosition.dAAA", where N is the > slice number and AAA is Sag (x), Cor (y), and Tra (z). This may be > off by half a voxel. > > c. Supermosaics - ??? > > 9. Number of Volumes (ie, number of frames or time points). > > a. Non-mosaics - count the number of files with the same image > position. > > b. Mosaics - count the number of files in the series. The number > of frames should also be stored in the ASCII header as 1 plus > lRepetitions. > > c. Supermosaics - ??? > > 10. Time between volumes/frames (ie, the TR for fMRI). > > a. Non-mosaics - ??? > > b. Mosaics - number of slices times the repetition time (DICOM > (18,80)). This is for version 1.6 and before. For version 2.1 > and after, (18,80) will contain the inter-volume TR (instead > of the time it takes to acquire one slice). The software version > can be determined from tag (18,1020) > > c. Supermosaics - ??? > > 11. Time at which each slice was obtained (relative to the start of > the volume acquisition). For sequences in which slices were > acquired uniformly across the TR, there is a variable in the ASCII > header called sSliceArray.ucMode which indicates the slice order: > 0x1 for Ascending, 0x2 for Descending, and 0x4 for Interleaved. > The selection box for this option can be found on the Numaris/4 > GUI on the "Geometry" tab, "Common" sub-tab, field name "Series". > > > 12. Time at which each volume was obtained (relative to the start of > the > series). This may not be computable from the slice timing if > there is a temporal gap between volumes. ??? > > 13. Other parameters not so critical: echo time, inversion time, phase > encode direction, readout direction, flip angle, patient name, > scan date, scan time, pulse sequence, protocol name, etc. These > are obtainable from the DICOM header. > > NOTE ON COORDINATE DEFINITION. It is assumed that all xyz coordinates > (including direction cosines) in the Siemens DICOM header (including > ASCII) conform to an LPS standard. "LPS" means that x increases from > the subject's right to Left, y increases from anterior to Posterior, > and z increases from inferior to Superior. All this assumes that the > subject in the scanner Head-First/Supine (HFS). The patient's position > can be determined from DICOM tag (18,5100). It appears that this > definition can be changed from the Numaris 4 console. On the "System" > tab, "Common" sub-tab, heading "Image Numbering" there are selections > for fields "Sagittal", "Coronal", and "Transversal". Changes these > parameters will affect the variable called > sSliceArray.ucImageNumb{Sag,Cor,Tra} in the ASCII header. The value > will be either 0 or 0x1 (NOTE: a value of 0 is indicated by the > absence of the variable in the header). The above coordinate system > definition assumes that the Sag and Cor components are set to 0x1. > Currently, only the LPS definition is valid in the software (ie, > changes in ImageNum variables from default will not be reflected in > the goemetry). minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/COPYING0000644000175000000620000000066612574624760022630 0ustar stevestaffCopyright Andrew Janke, The University of Queensland. 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 the University of Queensland make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/siemens_mosaic2mnc0000755000175000000620000004327712574624760025306 0ustar stevestaff#! /usr/bin/env perl # # siemens_mosaic2mnc # # Andrew Janke - a.janke@gmail.com # Center for Magnetic Resonance # The University of Queensland # http://www.cmr.uq.edu.au/~rotor # # See POD section for LICENCE $| = 1; use strict; use warnings "all"; use Getopt::Tabular; use File::Basename; use File::Temp qw/ tempdir /; use vars qw($VERSION); $VERSION = '1.0'; my($Help, $Usage, $me, $infile, $outfile, $history); my(@opt_table, %opt, $tmpdir, @args, $args); $me = basename($0); %opt = ( 'verbose' => 0, 'quiet' => 0, 'debug' => 0, 'clobber' => 0, 'fake' => 0, ); $Help = < 1, CLEANUP => 1 ); # check for infile and outfile if(!-e $infile){ die "$me: Couldn't find $infile\n"; } if(-e $outfile && !$opt{clobber}){ die "$me: $outfile exists! use -clobber to overwrite\n"; } # variables my($buf, $i, $j, $n, $gen_hdr, %siemens_hdr); my(@dimcodes, @dimnames, @dim_idx, @slice_pos, @slice_nrm); # have a hunt for the ASCCONV header my($ref, $hsh, $idx, $text); print STDOUT " +++ Seeking ASCCONV header in $infile\n" if !$opt{quiet}; open(FH, "<$infile"); do { chomp($buf = ); } until (index($buf, "### ASCCONV BEGIN ###") != -1); # copy the header into a hash print STDOUT " +++ Getting ASCCONV elements: " if !$opt{quiet}; $buf = ; for($i=0; (index($buf, "### ASCCONV END ###") == -1); $buf = , $i++){ my($key, $value); chomp($buf); ($key, $value) = split(/\s*\=\s*/, $buf, 2); $ref = '$siemens_hdr'; foreach (split(/\./, $key)){ if($_ =~ m/\[\d*\]$/){ ($hsh, $idx) = ($_ =~ m/(.*?)\[(\d*)\]$/); $text = "{'$hsh'}[$idx]"; } else{ $text = "{'$_'}"; } $ref .= $text; } $ref .= " = $value"; eval($ref); print STDOUT "." if !$opt{quiet}; } close(FH); print STDOUT "Done. ($i elements read)\n" if !$opt{quiet}; if($opt{debug}){ use Data::Dumper; my($ptr) = \%siemens_hdr; $Data::Dumper::Deepcopy = 1; print STDOUT Dumper(\$ptr); } # Read in ASCCONV header slice positions and normals (and fill in the blanks!) foreach (@{$siemens_hdr{sSliceArray}{asSlice}}){ # fill in "missing" (default?) co-ordinates. if(!defined($_->{sPosition}{dSag})){ $_->{sPosition}{dSag} = 0; print "Completed position for slice in Sag\n" if $opt{verbose}; } if(!defined($_->{sPosition}{dCor})){ $_->{sPosition}{dCor} = 0; print "Completed position for slice in Cor\n" if $opt{verbose}; } if(!defined($_->{sPosition}{dTra})){ $_->{sPosition}{dTra} = 0; print "Completed position for slice in Tra\n" if $opt{verbose}; } # Damn LPS and Radiological vs Neurological Stupidity! $_->{sPosition}{dSag} *= -1; $_->{sPosition}{dCor} *= -1; push(@slice_pos, [$_->{sPosition}{dSag}, $_->{sPosition}{dCor}, $_->{sPosition}{dTra}]); # fill in "missing" (default?) normals. if(!defined($_->{sNormal}{dSag})){ $_->{sNormal}{dSag} = 0; print "Completed normal for Sag\n" if $opt{verbose}; } if(!defined($_->{sNormal}{dCor})){ $_->{sNormal}{dCor} = 0; print "Completed normal for Cor\n" if $opt{verbose}; } if(!defined($_->{sNormal}{dTra})){ $_->{sNormal}{dTra} = 0; print "Completed normal for Tra\n" if $opt{verbose}; } push(@slice_nrm, [$_->{sNormal}{dSag}, $_->{sNormal}{dCor}, $_->{sNormal}{dTra}]); } # convert the image data using dicom_to_minc my($conv_fn); print STDOUT " +++ Converting using dicom_to_minc: \n" if !$opt{quiet}; &do_cmd('dicom3_to_minc', $tmpdir, $infile); chomp($conv_fn = `ls -1 $tmpdir/*.mnc`); print STDOUT " +++ Found file $conv_fn\n" if !$opt{quiet}; # get the file dimension order my($code); chomp($buf = `mincinfo -dimnames $conv_fn`); $buf =~ s/\ $//; ($dimnames[2], $dimnames[1], $dimnames[0]) = split(/\ /, $buf, 3); for($i=0; $i<3; $i++){ $code = substr($dimnames[$i], 0, 1); $dimcodes[$i] = $code; if($code eq 'x'){ $dim_idx[$i] = 0; } elsif($code eq 'y'){ $dim_idx[$i] = 1; } elsif($code eq 'z'){ $dim_idx[$i] = 2; } } print STDOUT "Dimension order: @dimnames - (@dimcodes)\n" if $opt{verbose}; # get the tile size my(@tile_size, $ntiles); # $tile_size[0] = $siemens_hdr{sKSpace}{lPhaseEncodingLines}; $tile_size[0] = $siemens_hdr{sKSpace}{lImagesPerSlab}; $tile_size[1] = $siemens_hdr{sKSpace}{lBaseResolution}; # check for interpolation if(defined($siemens_hdr{sKSpace}{uc2DInterpolation}) && $siemens_hdr{sKSpace}{uc2DInterpolation} == 1){ $tile_size[0] *= 2; $tile_size[1] *= 2; } # number of tiles $ntiles = $siemens_hdr{sSliceArray}{lSize}; if($ntiles == 1){ warn "$me: This is not a mosaic, (sSliceArray.lSize == $ntiles) simply moving the file should do\n"; &do_cmd('mv', $conv_fn, $outfile); exit(0); } # get the mosaic size my(@mosaic_size, @concat_files); chomp($mosaic_size[0] = `mincinfo -dimlength $dimnames[0] $conv_fn`); chomp($mosaic_size[1] = `mincinfo -dimlength $dimnames[1] $conv_fn`); # extract the mosaic tiles print STDOUT " +++ Extracting Mosaic tiles ($tile_size[0] x $tile_size[1])" . " from ($mosaic_size[0] x $mosaic_size[1]): " if !$opt{quiet}; $n = 0; for($j=0; $j<$mosaic_size[1]; $j += $tile_size[1]){ for($i=0; $i<$mosaic_size[0]; $i += $tile_size[0]){ my($tmpfile) = sprintf("$tmpdir/slice_%05d.mnc", $n); &do_cmd('mincreshape', '-quiet', '-clobber', '-dimrange', "$dimnames[0]=$i,$tile_size[0]", '-dimrange', "$dimnames[1]=$j,$tile_size[1]", '-dimrange', "$dimnames[2]=0,0", $conv_fn, $tmpfile); push(@concat_files, $tmpfile); $n++; print STDOUT "." if !$opt{quiet}; # stop after $ntiles if($n >= $ntiles){ last; } } # stop after $ntiles if($n >= $ntiles){ last; } } print STDOUT "Done. ($n Frames)\n" if !$opt{quiet}; # put them back together in the correct order... # currently broken until I get some docco from siemens push(@{$gen_hdr->{files}}, $concat_files[0]); foreach (@{$siemens_hdr{sSliceArray}{anAsc}}){ if(defined($_)){ push(@{$gen_hdr->{files}}, $concat_files[$_]); } } # get sizes $gen_hdr->{"$dimcodes[0]size"} = $tile_size[0]; $gen_hdr->{"$dimcodes[1]size"} = $tile_size[1]; $gen_hdr->{"$dimcodes[2]size"} = $ntiles; # in-plane steps $gen_hdr->{"$dimcodes[0]step"} = $siemens_hdr{sSliceArray}{asSlice}[0]{dPhaseFOV} / $gen_hdr->{"$dimcodes[0]size"}; $gen_hdr->{"$dimcodes[1]step"} = $siemens_hdr{sSliceArray}{asSlice}[0]{dReadoutFOV} / $gen_hdr->{"$dimcodes[1]size"}; # for inter-slice gap (z in axial) in mosaics let's assume # the gap between the first two slices is indicitive #$gen_hdr->{"$dimcodes[2]step"} = sqrt(($slice_pos[0][0] - $slice_pos[1][0])**2 + # ($slice_pos[0][1] - $slice_pos[1][1])**2 + # ($slice_pos[0][2] - $slice_pos[1][2])**2); # assume first slice thickness is indicitive $gen_hdr->{"$dimcodes[2]step"} = $siemens_hdr{sSliceArray}{asSlice}[0]{dThickness}; # add slice gap if defined if(defined($siemens_hdr{sGroupArray}{asGroup}[0]{dDistFact})){ $gen_hdr->{"$dimcodes[2]step"} *= 1 + $siemens_hdr{sGroupArray}{asGroup}[0]{dDistFact}; } # flipping step _SHOULD_ be here (for out of plane step) # check against the ASCCONV header $args = "mincinfo -attvalue $dimnames[0]:step ". "-attvalue $dimnames[1]:step $conv_fn"; my(@steps) = split(' ', `$args`); if(($steps[0] != $gen_hdr->{xstep}) || ($steps[1] != $gen_hdr->{ystep})){ warn "\n$me: ASCCONV slice steps (" . $gen_hdr->{"$dimcodes[0]step"} . ':' . $gen_hdr->{"$dimcodes[1]step"} . ') != '. "Header from minc file ($steps[0]:$steps[1])\n\n"; $gen_hdr->{"$dimcodes[0]step"} = $steps[0]; $gen_hdr->{"$dimcodes[1]step"} = $steps[1]; } # as dicom_to_minc has already extracted these, simply read in the direction cosines $gen_hdr->{xdircos} = [split(' ', `mincinfo -attvalue xspace:direction_cosines $conv_fn`)]; $gen_hdr->{ydircos} = [split(' ', `mincinfo -attvalue yspace:direction_cosines $conv_fn`)]; $gen_hdr->{zdircos} = [split(' ', `mincinfo -attvalue zspace:direction_cosines $conv_fn`)]; # check slice norm against the ASCCONV header my($delta) = 0.0000001; if((abs($slice_nrm[0][0]) - abs($gen_hdr->{"$dimcodes[2]dircos"}[0]) > $delta) || (abs($slice_nrm[0][1]) - abs($gen_hdr->{"$dimcodes[2]dircos"}[1]) > $delta) || (abs($slice_nrm[0][2]) - abs($gen_hdr->{"$dimcodes[2]dircos"}[2]) > $delta)){ warn "\n$me: ASCCONV normal (" . join(' ', @{$slice_nrm[0]}) . ")\n " . " != DICOM (" . join(' ', @{$gen_hdr->{"$dimcodes[2]dircos"}}) . ")\n\n"; } # calculate origin my(@origin, $scale, @tmp); for $i (0..2){ $origin[$i] = $slice_pos[0][$i]; } # transpose tile dimensions to origin # origin = slice_position - half FOV + 1/2 step for "tile dimensions" for $i (0..1){ $scale = -($gen_hdr->{"$dimcodes[$i]size"}/2 * $gen_hdr->{"$dimcodes[$i]step"}) + ($gen_hdr->{"$dimcodes[$i]step"}/2); @tmp = &vector_scale($scale, $gen_hdr->{"$dimcodes[$i]dircos"}); # add to the current origin @origin = &vector_add(\@origin, \@tmp); } for $i (0..2){ $gen_hdr->{origin}[$i] = $origin[$i]; } ($gen_hdr->{xstart}, $gen_hdr->{ystart}, $gen_hdr->{zstart}) = &convert_origin_to_start($gen_hdr->{origin}, $gen_hdr->{xdircos}, $gen_hdr->{ydircos}, $gen_hdr->{zdircos}); print STDOUT dump_general_header($gen_hdr) if $opt{verbose}; print STDOUT " +++ Creating 3D image ($dimnames[2]): " if !$opt{quiet}; &do_cmd('mincconcat', '-clobber', '-nocheck_dimensions', '-step', $gen_hdr->{"$dimcodes[2]step"}, '-start', $gen_hdr->{"$dimcodes[2]start"}, '-concat_dimension', $dimnames[2], @{$gen_hdr->{files}}, $outfile); # dodgy hack to ascertain out of slice step direction (and flip it) # THIS MUST BE DONE after mincconcat as it is too clever for it's own good if($slice_pos[0][$dim_idx[2]] > $slice_pos[1][$dim_idx[2]]){ $gen_hdr->{"$dimcodes[2]step"} *= -1; print STDOUT "$dim_idx[2] - Flipped step in $dimcodes[2] - " . $gen_hdr->{"$dimcodes[2]step"} . "\n"; } # insert correct start/step/dircos and add the history string &do_cmd('minc_modify_header', '-dinsert', "xspace:start=$gen_hdr->{xstart}", '-dinsert', "yspace:start=$gen_hdr->{ystart}", '-dinsert', "zspace:start=$gen_hdr->{zstart}", '-dinsert', "xspace:step=$gen_hdr->{xstep}", '-dinsert', "yspace:step=$gen_hdr->{ystep}", '-dinsert', "zspace:step=$gen_hdr->{zstep}", '-dinsert', "xspace:direction_cosines=" . join(',', @{$gen_hdr->{xdircos}}), '-dinsert', "yspace:direction_cosines=" . join(',', @{$gen_hdr->{ydircos}}), '-dinsert', "zspace:direction_cosines=" . join(',', @{$gen_hdr->{zdircos}}), '-sinsert', ":history=$history", $outfile); # add ASCCONV header to MINC header #foreach (sort(keys(%siemens_hdr))){ # push(@args, '-sinsert', "siemens_header:$_=" . Dumper($siemens_hdr{$_})); # } #&do_cmd('minc_modify_header', @args, $outfile); sub do_cmd { print STDOUT "@_\n" if $opt{verbose}; if(!$opt{fake}){ system(@_) == 0 or die; } } # return a ASCII dump of a general header sub dump_general_header{ my($h) = shift; my($tmp); $tmp = "General Header\n"; foreach (sort(keys(%{$h}))){ if($h->{$_} =~ /ARRAY/){ $tmp .= " $_\t<". join(' ' , @{$h->{$_}}) . ">\n"; } else{ $tmp .= " $_\t<$h->{$_}>\n"; } } $tmp .= "\n"; return $tmp; } # Routine to compute a dot product from two arrayrefs sub vector_dot_product { my($vec1, $vec2) = @_; my($result, $i); $result = 0; for $i (0..2) { $result += $$vec1[$i] * $$vec2[$i]; } return $result; } # Routine to compute a vector cross product from two arrayrefs sub vector_cross_product { my($vec1, $vec2) = @_; my(@result); $#result = 2; $result[0] = $$vec1[1] * $$vec2[2] - $$vec1[2] * $$vec2[1]; $result[1] = $$vec1[2] * $$vec2[0] - $$vec1[0] * $$vec2[2]; $result[2] = $$vec1[0] * $$vec2[1] - $$vec1[1] * $$vec2[0]; return @result; } # Routine to add two vectors sub vector_add { my($vec1, $vec2) = @_; my(@result, $i); $#result = 2; for $i (0..2) { $result[$i] = $$vec1[$i] + $$vec2[$i]; } return @result; } # Routine to scale a vector sub vector_scale { my($scale, $vec1) = @_; my(@result, $i); $#result = 2; for $i (0..2) { $result[$i] = $$vec1[$i] * $scale; } return @result; } # Routine to convert an origin specified in world coordinates # to an array of minc start positions by projecting the # point along the edges of the parallelopiped formed by the # 3 direction cosines. Direction cosines are normalized. # This is converted from convert_origin_to_start.c in the base # minc package sub convert_origin_to_start { my($origin, $xdircos, $ydircos, $zdircos) = @_; my(@axes, @normal, @lengths, $normal_length, $numerator, $denominator); my(@start); $#start = 2; # Set up dircos matrix for $i (0..2){ $axes[0][$i] = $$xdircos[$i]; $axes[1][$i] = $$ydircos[$i]; $axes[2][$i] = $$zdircos[$i]; } # Calculate lengths of direction cosines, # checking for zero lengths and parallel vectors. for $i (0..2){ @normal = &calculate_orthogonal_vector($axes[$i], $axes[($i+1) % 3]); $lengths[$i] = 0.0; $normal_length = 0.0; for $j (0..2) { $lengths[$i] += $axes[$i][$j] ** 2; $normal_length += $normal[$j] ** 2; } $lengths[$i] = sqrt($lengths[$i]); # sanity checks if($lengths[$i] == 0.0) { die "$me: Cannot convert origin to start - some direction cosines have zero length.\n"; } if($normal_length == 0.0) { die "$me: Cannot convert origin to start - some direction cosines are parallel.\n"; } } # Loop over axes, calculating projections for $i (0..2) { # Calculate vector normal to other two axes by doing cross product @normal = &calculate_orthogonal_vector($axes[($i+1) % 3], $axes[($i+2) % 3]); # Calculate dot product of origin with normal (numerator) and # dot product of current axis with normal (denominator) $denominator = $numerator = 0.0; for $j (0..2) { $numerator += $$origin[$j] * $normal[$j]; $denominator += $axes[$i][$j] * $normal[$j]; } # Calculate parallel projection if($denominator != 0.0){ $start[$i] = $lengths[$i] * $numerator / $denominator; } else{ $start[$i] = 0.0; } } return @start; } # Routine to calculate a vector that is orthogonal to 2 other # vectors by doing a cross product. The vectors are copied # before the calculation so the result vector can be one of # the input vectors. sub calculate_orthogonal_vector { my($vec1, $vec2) = @_; my(@result); $#result = 2; for $i (0..2) { $result[$i] = $$vec1[($i+1) % 3] * $$vec2[($i+2) % 3] - $$vec2[($i+1) % 3] * $$vec1[($i+2) % 3]; } return @result; } sub print_version_info { my($package, $version, $package_bugreport); $package = '@PACKAGE@'; # $version = '@VERSION@'; $version = $VERSION; $package_bugreport = '@PACKAGE_BUGREPORT@'; print STDOUT "\n$package version $version\n". "Comments to $package_bugreport\n\n"; exit; } __END__ # POD time =pod =head1 NAME siemens_mosaic2mnc - perl script to convert a single siemens DICOM mosaic file (.IMA) to a MINC file preserving coordinate information =head1 SYNOPSIS siemens_mosaic2mnc [options] siemens_mosaic2mnc -help =head1 PREREQUISITES This script requires the module. =head1 AUTHOR Andrew Janke Ea.janke@gmail.comE =head1 SEE ALSO dicom3_to_minc(1). =head1 COPYRIGHT Copyright Andrew Janke, The University of Queensland. 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 the University of Queensland make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. =cut minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/README0000644000175000000620000000044212574624760022445 0ustar stevestaffsiemens_mosaic2mnc ================== Converts siemens DICOM mosaic files to MINC INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Getopt::Tabular minc-tools-2.3.00+dfsg/conversion/siemens_mosaic2mnc/MANIFEST0000644000175000000620000000023112574624760022712 0ustar stevestaffChangeLog Makefile.PL INSTALL README MANIFEST siemens_mosaic2mnc t/test.t META.yml Module meta-data (added by MakeMaker) minc-tools-2.3.00+dfsg/conversion/micropet/0002755000175000000620000000000012574624760017633 5ustar stevestaffminc-tools-2.3.00+dfsg/conversion/micropet/upet2mnc.c0000644000175000000620000012511212574624760021534 0ustar stevestaff/* concorde microPET to minc */ #include "config.h" #define _XOPEN_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #define VERSIONSTR VERSION " built " __DATE__ " " __TIME__ /************************************************************************* * Concorde microPET definitions */ /* file_type field */ #define UPET_FT_UNKNOWN 0 #define UPET_FT_LIST_MODE 1 #define UPET_FT_SINOGRAM 2 #define UPET_FT_NORMALIZATION 3 #define UPET_FT_ATTENUATION_CORRECTION 4 #define UPET_FT_IMAGE 5 /* Standard image data file (typical) */ #define UPET_FT_BLANK 6 #define UPET_FT_MU_MAP 8 /* Mu map data file */ #define UPET_FT_SCATTER_CORRECTION 9 /* acquisition_mode field */ #define UPET_AM_UNKNOWN 0 #define UPET_AM_BLANK 1 #define UPET_AM_EMISSION 2 #define UPET_AM_DYNAMIC 3 #define UPET_AM_GATED 4 #define UPET_AM_CONTINUOUS_BED_MOTION 5 #define UPET_AM_SINGLES_TRANSMISSION 6 #define UPET_AM_WINDOWED_COINCIDENCE_TRANSMISSION 7 #define UPET_AM_NONWINDOWED_COINCIDENCE_TRANSMISSION 8 #define UPET_DT_UNKNOWN 0 #define UPET_DT_BYTE 1 #define UPET_DT_II16 2 /* Intel 16-bit signed integer */ #define UPET_DT_II32 3 /* Intel 32-bit signed integer */ #define UPET_DT_IF32 4 /* Intel 32-bit float */ #define UPET_DT_MF32 5 /* Sun 32-bit float */ #define UPET_DT_MI16 6 /* Sun 16-bit signed integer */ #define UPET_DT_MI32 7 /* Sun 32-bit signed integer */ #define DECLARE_FUNC(x) \ static int x(struct conversion_info *ci_ptr, char *val_str, char *new_var, char *new_att) struct conversion_info { FILE *hdr_fp; FILE *img_fp; int mnc_fd; int frame_index; int frame_zero; int data_type; nc_type minc_type; int dim_count; int dim_lengths[5]; int dim_ids[5]; double dim_steps[5]; int frame_nbytes; int frame_nvoxels; void *frame_buffer; double scale_factor; double deadtime_correction; double decay_correction; double calibration_factor; double isotope_branching_fraction; int swap_size; /* 0, 2, 4 */ }; DECLARE_FUNC(upet_file_type); DECLARE_FUNC(upet_acq_mode); DECLARE_FUNC(upet_bed_motion); DECLARE_FUNC(upet_data_type); DECLARE_FUNC(upet_data_order); DECLARE_FUNC(upet_ndims); DECLARE_FUNC(upet_total_frames); DECLARE_FUNC(upet_x_dim); DECLARE_FUNC(upet_y_dim); DECLARE_FUNC(upet_z_dim); DECLARE_FUNC(upet_vector_dim); DECLARE_FUNC(upet_injection_time); DECLARE_FUNC(upet_scan_time); DECLARE_FUNC(upet_axial_crystal_pitch); DECLARE_FUNC(upet_pixel_size); DECLARE_FUNC(upet_dose_units); DECLARE_FUNC(upet_calibration_factor); DECLARE_FUNC(upet_rotation); DECLARE_FUNC(upet_isotope_branching_fraction); DECLARE_FUNC(upet_frame_no); DECLARE_FUNC(upet_frame_start); DECLARE_FUNC(upet_frame_duration); DECLARE_FUNC(upet_frame_min); DECLARE_FUNC(upet_frame_max); DECLARE_FUNC(upet_frame_file_ptr); DECLARE_FUNC(upet_frame_scale_factor); DECLARE_FUNC(upet_frame_decay_correction); DECLARE_FUNC(upet_frame_deadtime_correction); static void copy_init(struct conversion_info *ci_ptr); static void copy_frame(struct conversion_info *ci_ptr); /* These values are used to represent the field types in the microPET * header file. */ #define UPET_TYPE_STR 1 /* String */ #define UPET_TYPE_INT 2 /* Integer */ #define UPET_TYPE_REAL 3 /* Floating-point */ #define UPET_TYPE_TIME 4 /* Timestamp */ #define UPET_TYPE_FILTER 5 /* Integer type followed by a float cutoff */ #define UPET_TYPE_3X64 6 /* 3 64-bit integers */ #define UPET_TYPE_FPTR 7 /* File pointer (2 32 bit integers) */ #define UPET_TYPE_SINGLE 8 /* Block #, singles/sec, raw singles/sec */ /* concorde keywords */ struct keywd_entry { char *upet_kwd; short upet_type; char *mnc_var; char *mnc_att; int (*func)(struct conversion_info *, char *, char *, char *); }; /* Per-frame attributes in the concorde microPET header */ struct keywd_entry frm_atts[] = { { "frame", UPET_TYPE_INT, NULL, NULL, upet_frame_no }, { "event_type", UPET_TYPE_INT, NULL, NULL, NULL }, { "gate", UPET_TYPE_INT, NULL, NULL, NULL }, { "bed", UPET_TYPE_INT, NULL, NULL, NULL }, { "bed_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, { "ending_bed_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, { "vertical_bed_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, { "data_file_pointer", UPET_TYPE_FPTR, NULL, NULL, upet_frame_file_ptr }, { "frame_start", UPET_TYPE_REAL, NULL, NULL, upet_frame_start }, { "frame_duration", UPET_TYPE_REAL, NULL, NULL, upet_frame_duration }, { "scale_factor", UPET_TYPE_REAL, NULL, NULL, upet_frame_scale_factor }, { "minimum", UPET_TYPE_REAL, NULL, NULL, upet_frame_min }, { "maximum", UPET_TYPE_REAL, NULL, NULL, upet_frame_max }, { "deadtime_correction", UPET_TYPE_REAL, NULL, NULL, upet_frame_deadtime_correction }, { "decay_correction", UPET_TYPE_REAL, NULL, NULL, upet_frame_decay_correction }, { "prompts", UPET_TYPE_3X64, NULL, NULL, NULL }, { "delays", UPET_TYPE_3X64, NULL, NULL, NULL }, { "trues", UPET_TYPE_3X64, NULL, NULL, NULL }, { "prompts_rate", UPET_TYPE_INT, NULL, NULL, NULL }, { "delays_rate", UPET_TYPE_INT, NULL, NULL, NULL }, { "singles", UPET_TYPE_SINGLE, NULL, NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; /* Per-volume attributes in the concorde microPET header */ struct keywd_entry vol_atts[] = { { "ROI_file:", UPET_TYPE_STR, NULL, NULL, NULL }, { "version", UPET_TYPE_STR, NULL, NULL, NULL }, { "model", UPET_TYPE_INT, MIstudy, MIdevice_model, NULL }, { "institution", UPET_TYPE_STR, MIstudy, MIinstitution, NULL }, { "study", UPET_TYPE_STR, NULL, NULL, NULL }, { "file_name", UPET_TYPE_STR, NULL, NULL, NULL }, /* actual file data */ { "file_type", UPET_TYPE_INT, NULL, NULL, upet_file_type }, { "acquisition_mode", UPET_TYPE_INT, NULL, NULL, upet_acq_mode}, { "bed_motion", UPET_TYPE_INT, NULL, NULL, upet_bed_motion}, { "total_frames", UPET_TYPE_INT, NULL, NULL, upet_total_frames }, { "isotope", UPET_TYPE_STR, MIacquisition, MIradionuclide, NULL }, { "isotope_half_life", UPET_TYPE_REAL, MIacquisition, MIradionuclide_halflife, NULL }, { "isotope_branching_fraction", UPET_TYPE_REAL, NULL, NULL, upet_isotope_branching_fraction }, { "transaxial_crystals_per_block", UPET_TYPE_INT, NULL, NULL, NULL }, { "axial_crystals_per_block", UPET_TYPE_INT, NULL, NULL, NULL }, { "intrinsic_crystal_offset", UPET_TYPE_INT, NULL, NULL, NULL }, { "transaxial_blocks", UPET_TYPE_INT, NULL, NULL, NULL }, { "axial_blocks", UPET_TYPE_INT, NULL, NULL, NULL }, { "transaxial_crystal_pitch", UPET_TYPE_REAL, NULL, NULL, NULL }, { "axial_crystal_pitch", UPET_TYPE_REAL, NULL, NULL, upet_axial_crystal_pitch }, { "radius", UPET_TYPE_REAL, NULL, NULL, NULL }, { "radial_fov", UPET_TYPE_REAL, NULL, NULL, NULL }, { "src_radius", UPET_TYPE_REAL, NULL, NULL, NULL }, {"src_cm_per_rev", UPET_TYPE_REAL, NULL, NULL, NULL }, {"src_steps_per_rev", UPET_TYPE_INT, NULL, NULL, NULL }, {"tx_src_type", UPET_TYPE_INT, NULL, NULL, NULL }, {"default_projections", UPET_TYPE_INT, NULL, NULL, NULL }, {"default_transaxial_angles", UPET_TYPE_INT, NULL, NULL, NULL }, {"crystal_thickness", UPET_TYPE_REAL, NULL, NULL, NULL }, {"depth_of_interaction", UPET_TYPE_REAL, NULL, NULL, NULL }, {"transaxial_bin_size", UPET_TYPE_REAL, NULL, NULL, NULL }, {"axial_plane_size", UPET_TYPE_REAL, NULL, NULL, NULL }, {"lld", UPET_TYPE_REAL, NULL, NULL, NULL }, {"uld", UPET_TYPE_REAL, NULL, NULL, NULL }, {"timing_window", UPET_TYPE_INT, NULL, NULL, NULL }, {"data_type", UPET_TYPE_INT, NULL, NULL, upet_data_type }, {"data_order", UPET_TYPE_INT, NULL, NULL, upet_data_order }, {"span", UPET_TYPE_INT, NULL, NULL, NULL }, {"ring_difference", UPET_TYPE_INT, NULL, NULL, NULL }, {"number_of_dimensions", UPET_TYPE_INT, NULL, NULL, upet_ndims }, {"x_dimension", UPET_TYPE_INT, NULL, NULL, upet_x_dim }, {"y_dimension", UPET_TYPE_INT, NULL, NULL, upet_y_dim }, {"z_dimension", UPET_TYPE_INT, NULL, NULL, upet_z_dim }, {"w_dimension", UPET_TYPE_INT, NULL, NULL, upet_vector_dim }, {"x_filter", UPET_TYPE_FILTER, NULL, NULL, NULL }, {"y_filter", UPET_TYPE_FILTER, NULL, NULL, NULL }, {"z_filter", UPET_TYPE_FILTER, NULL, NULL, NULL }, {"histogram_version", UPET_TYPE_STR, NULL, NULL, NULL }, {"rebinning_type", UPET_TYPE_INT, NULL, NULL, NULL }, {"rebinning_version", UPET_TYPE_STR, NULL, NULL, NULL }, {"recon_algorithm", UPET_TYPE_INT, NULL, NULL, NULL }, {"recon_version", UPET_TYPE_STR, NULL, NULL, NULL }, {"map_subsets", UPET_TYPE_INT, NULL, NULL, NULL }, {"map_osem3d_iterations", UPET_TYPE_INT, NULL, NULL, NULL }, {"map_iterations", UPET_TYPE_INT, NULL, NULL, NULL }, {"map_beta", UPET_TYPE_REAL, NULL, NULL, NULL }, {"map_blur_type", UPET_TYPE_INT, NULL, NULL, NULL }, {"map_prior_type", UPET_TYPE_INT, NULL, NULL, NULL }, {"map_blur_file", UPET_TYPE_STR, NULL, NULL, NULL }, {"map_pmatrix_file", UPET_TYPE_STR, NULL, NULL, NULL }, {"deadtime_correction_applied", UPET_TYPE_INT, NULL, NULL, NULL }, {"decay_correction_applied", UPET_TYPE_INT, NULL, NULL, NULL }, {"normalization_applied", UPET_TYPE_INT, NULL, NULL, NULL }, {"normalization_filename", UPET_TYPE_STR, NULL, NULL, NULL }, {"attenuation_applied", UPET_TYPE_INT, NULL, NULL, NULL }, {"attenuation_filename", UPET_TYPE_STR, NULL, NULL, NULL }, {"scatter_correction", UPET_TYPE_INT, NULL, NULL, NULL }, {"scatter_version", UPET_TYPE_STR, NULL, NULL, NULL }, {"arc_correction_applied", UPET_TYPE_INT, NULL, NULL, NULL }, {"rotation", UPET_TYPE_REAL, NULL, NULL, upet_rotation }, {"x_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, {"y_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, {"z_offset", UPET_TYPE_REAL, NULL, NULL, NULL }, {"zoom", UPET_TYPE_REAL, NULL, NULL, NULL }, {"pixel_size", UPET_TYPE_REAL, NULL, NULL, upet_pixel_size }, {"calibration_units", UPET_TYPE_INT, NULL, NULL, NULL }, {"calibration_factor", UPET_TYPE_REAL, NULL, NULL, upet_calibration_factor }, {"calibration_branching_fraction", UPET_TYPE_REAL, NULL, NULL, NULL }, {"number_of_singles_rates", UPET_TYPE_INT, NULL, NULL, NULL }, {"investigator", UPET_TYPE_STR, MIstudy, "investigator", NULL }, {"operator", UPET_TYPE_STR, MIstudy, MIoperator, NULL }, {"study_identifier", UPET_TYPE_STR, MIstudy, MIstudy_id, NULL }, {"scan_time", UPET_TYPE_TIME, NULL, NULL, upet_scan_time }, {"injected_compound", UPET_TYPE_STR, NULL, NULL, NULL }, {"dose_units", UPET_TYPE_INT, MIacquisition, MIdose_units, upet_dose_units }, {"dose", UPET_TYPE_REAL, MIacquisition, MIinjection_dose, NULL }, {"injection_time", UPET_TYPE_TIME, NULL, NULL, upet_injection_time }, {"injection_decay_correction", UPET_TYPE_REAL, NULL, NULL, NULL }, {"subject_identifier", UPET_TYPE_STR, NULL, NULL, NULL }, {"subject_genus", UPET_TYPE_STR, NULL, NULL, NULL }, {"subject_orientation", UPET_TYPE_INT, NULL, NULL, NULL }, {"subject_length_units", UPET_TYPE_INT, NULL, NULL, NULL }, {"subject_length", UPET_TYPE_REAL, NULL, NULL, NULL }, {"subject_weight_units", UPET_TYPE_INT, NULL, NULL, NULL }, {"subject_weight", UPET_TYPE_REAL, NULL, NULL, NULL }, {"subject_phenotype", UPET_TYPE_STR, NULL, NULL, NULL }, {"study_model", UPET_TYPE_STR, NULL, NULL, NULL }, {"anesthesia", UPET_TYPE_STR, NULL, NULL, NULL }, {"analgesia", UPET_TYPE_STR, NULL, NULL, NULL }, {"other_drugs", UPET_TYPE_STR, NULL, NULL, NULL }, {"food_access", UPET_TYPE_STR, NULL, NULL, NULL }, {"water_access", UPET_TYPE_STR, NULL, NULL, NULL }, {NULL, 0, NULL, NULL, NULL } }; /* Reflects "normal" image data order */ #define DIM_T 0 #define DIM_Z 1 #define DIM_Y 2 #define DIM_X 3 #define DIM_W 4 static char *_dimnames[5]; /* Calculate the overall scaling factor for the image data from the * conversion information structure. */ #define COMBINED_SCALE_FACTOR(ci_ptr) \ ((ci_ptr->scale_factor * ci_ptr->calibration_factor) / \ (ci_ptr->isotope_branching_fraction)) #define ORIENT_BODY 1 #define ORIENT_HEAD 2 int _orient_flag = ORIENT_HEAD; int _verbose_flag = 1; ArgvInfo argTable[] = { {"-head", ARGV_CONSTANT, (char *) ORIENT_HEAD, (char *) &_orient_flag, "Orient image for cerebral viewing (as with human brain)"}, {"-body", ARGV_CONSTANT, (char *) ORIENT_BODY, (char *) &_orient_flag, "Orient image for whole-body viewing (Z along long axis)"}, {"-quiet", ARGV_CONSTANT, (char *) 0, (char *) &_verbose_flag, "Turn off the various progress reporting messages."}, {NULL, ARGV_VERINFO, (char *) VERSIONSTR, (char *) NULL, NULL}, {NULL, ARGV_END, NULL, NULL, NULL} }; typedef enum { MSG_INFO, MSG_WARNING, MSG_ERROR, MSG_FATAL } msg_level_t; static void message(msg_level_t level, char *fmt, ...) { va_list ap; const char *prefix_str; switch (level) { case MSG_WARNING: prefix_str = "WARNING: "; break; case MSG_ERROR: prefix_str = "ERROR: "; break; case MSG_FATAL: prefix_str = "FATAL: "; break; default: prefix_str = NULL; break; } if (_verbose_flag || level != MSG_INFO) { if (level != MSG_INFO) { if (prefix_str != NULL) { fprintf(stderr, "%s", prefix_str); } va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } if (prefix_str != NULL) { fprintf(stdout, "%s", prefix_str); } va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); } } static int is_host_big_endian() { long ltmp = 0x04030201; char *ctmp = (char *) <mp; if (ctmp[0] == 0x01) { return (0); } if (ctmp[0] == 0x04) { return (1); } return (-1); } static void usage(const char *progname) { fprintf(stderr, "\nUsage: %s [] input.img[.hdr] [output.mnc]\n", progname); fprintf(stderr, " %s [-help]\n\n", progname); exit(-1); } int upet_to_minc(char *hdr_fname, char *img_fname, char *out_fname, char *prog_name) { char *line_ptr; char line_buf[1024]; char *val_ptr; int in_header; double dbl_tmp; int int_tmp; struct conversion_info ci; struct keywd_entry *ke_ptr; int is_known; char *argv_tmp[5]; char *out_history; ci.hdr_fp = fopen(hdr_fname, "r"); /* Text file */ if (ci.hdr_fp == NULL) { perror(hdr_fname); return (-1); } ci.img_fp = fopen(img_fname, "rb"); /* Binary file */ if (ci.img_fp == NULL) { perror(img_fname); return (-1); } ci.mnc_fd = micreate(out_fname, NC_NOCLOBBER); if (ci.mnc_fd < 0) { perror(out_fname); return (-1); } ci.frame_zero = -1; /* Initial frame is -1 until set. */ /* Define the basic MINC group variables. */ micreate_group_variable(ci.mnc_fd, MIstudy); micreate_group_variable(ci.mnc_fd, MIacquisition); micreate_group_variable(ci.mnc_fd, MIpatient); ncvardef(ci.mnc_fd, "micropet", NC_SHORT, 0, NULL); /* Fake the history here */ argv_tmp[0] = prog_name; argv_tmp[1] = VERSIONSTR; argv_tmp[2] = hdr_fname; argv_tmp[3] = img_fname; argv_tmp[4] = out_fname; out_history = time_stamp(5, argv_tmp); miattputstr(ci.mnc_fd, NC_GLOBAL, MIhistory, out_history); free(out_history); in_header = 1; ci.frame_nbytes = 1; ci.frame_nvoxels = 1; /* When we read voxels, we need COMBINED_SCALE_FACTOR() to have a sane * value for all modalities. Set defaults for these in case the modality * does not define one of these factors. For example, a CT (modality 2) * will not define isotope_branching_fraction or calibration_factor. */ ci.scale_factor = 1.0; ci.calibration_factor = 1.0; ci.isotope_branching_fraction = 1.0; /* Collect the headers */ while (fgets(line_buf, sizeof(line_buf), ci.hdr_fp) != NULL) { if (line_buf[0] == '#') /* */ continue; line_ptr = line_buf; while (!isspace(*line_ptr)) { line_ptr++; } *line_ptr++ = '\0'; val_ptr = line_ptr; while (*line_ptr != '\n' && *line_ptr != '\r' && *line_ptr != '\0') { line_ptr++; } *line_ptr = '\0'; is_known = 0; if (in_header) { if (*val_ptr != '\0') { /* Save the raw attribute into the file */ ncattput(ci.mnc_fd, ncvarid(ci.mnc_fd, "micropet"), line_buf, NC_CHAR, strlen(val_ptr), val_ptr); } for (ke_ptr = vol_atts; ke_ptr->upet_kwd != NULL; ke_ptr++) { if (!strcmp(ke_ptr->upet_kwd, line_buf)) { is_known = 1; if (ke_ptr->func != NULL) { (*ke_ptr->func)(&ci, val_ptr, ke_ptr->mnc_var, ke_ptr->mnc_att); } else if (ke_ptr->mnc_var != NULL && ke_ptr->mnc_att != NULL) { /* Interpret based upon type */ switch (ke_ptr->upet_type) { case UPET_TYPE_INT: int_tmp = atoi(val_ptr); miattputint(ci.mnc_fd, ncvarid(ci.mnc_fd, ke_ptr->mnc_var), ke_ptr->mnc_att, int_tmp); break; case UPET_TYPE_REAL: dbl_tmp = atof(val_ptr); miattputdbl(ci.mnc_fd, ncvarid(ci.mnc_fd, ke_ptr->mnc_var), ke_ptr->mnc_att, dbl_tmp); break; case UPET_TYPE_STR: miattputstr(ci.mnc_fd, ncvarid(ci.mnc_fd, ke_ptr->mnc_var), ke_ptr->mnc_att, val_ptr); break; } } break; } } } else { /* Not in the header any longer */ for (ke_ptr = frm_atts; ke_ptr->upet_kwd != NULL; ke_ptr++) { if (!strcmp(ke_ptr->upet_kwd, line_buf)) { is_known = 1; if (ke_ptr->func != NULL) { (*ke_ptr->func)(&ci, val_ptr, ke_ptr->mnc_var, ke_ptr->mnc_att); } break; } } } if (!is_known) { if (!strcmp(line_buf, "end_of_header")) { if (in_header) { in_header = 0; copy_init(&ci); } else { copy_frame(&ci); } } else { message(MSG_WARNING, "Unrecognized keyword %s\n", line_buf); } } } fclose(ci.hdr_fp); fclose(ci.img_fp); miclose(ci.mnc_fd); return (0); } int main(int argc, char **argv) { char *line_ptr; int i; char img_fname[1024]; char hdr_fname[1024]; char out_fname[1024]; int result; if (ParseArgv(&argc, argv, argTable, 0) || argc < 2) { usage(argv[0]); return (-1); } ncopts = 0; /* Set the dimension names. This is done here since the correct * arrangement depends on the value of _orient_flag */ _dimnames[DIM_T] = MItime; _dimnames[DIM_X] = MIxspace; _dimnames[DIM_Y] = MIyspace; _dimnames[DIM_Z] = MIzspace; _dimnames[DIM_W] = MIvector_dimension; if (_orient_flag == ORIENT_HEAD) { /* If using head orientation, exchange Y and Z. */ _dimnames[DIM_Y] = MIzspace; _dimnames[DIM_Z] = MIyspace; } /* Open the header and the associated binary file. */ for (i = 1; i < argc; i++) { /* Here we try to be flexible about allowing the user to specify * either the name of the .hdr file or the name of the .img file, * or just the base name of the two files. All three options * should work. */ strcpy(img_fname, argv[i]); strcpy(hdr_fname, argv[i]); /* Find the last extension. */ line_ptr = strrchr(argv[i], '.'); /* Did the user specify the .hdr file?? */ if (line_ptr != NULL && !strcmp(line_ptr, ".hdr")) { line_ptr = strrchr(img_fname, '.'); if (line_ptr != NULL) { *line_ptr = '\0'; } } /* Did the user specify the .img file?? */ else if (line_ptr != NULL && !strcmp(line_ptr, ".img")) { strcat(hdr_fname, ".hdr"); } /* Or perhaps just the base name?? */ else { strcat(img_fname, ".img"); strcat(hdr_fname, ".img.hdr"); } /* See if there is a filename following this one, and if so, does it * end with the ".mnc" extension. If so, take that names as the * output for this conversions. */ if (i < argc - 1 && (line_ptr = strrchr(argv[i+1], '.')) != NULL && !strcmp(line_ptr, ".mnc")) { strcpy(out_fname, argv[i+1]); i++; } else { strcpy(out_fname, img_fname); line_ptr = strrchr(out_fname, '.'); if (line_ptr != NULL) { strcpy(line_ptr, ".mnc"); } } /* Perform the conversion. */ message(MSG_INFO, "Starting conversion\n"); message(MSG_INFO, "- Input header: %s\n", hdr_fname); message(MSG_INFO, "- Input image: %s\n", img_fname); message(MSG_INFO, "- Output file: %s\n", out_fname); result = upet_to_minc(hdr_fname, img_fname, out_fname, argv[0]); if (result < 0) { message(MSG_ERROR, "Error creating %s\n", out_fname); } else { message(MSG_INFO, "Finished creating %s\n", out_fname); } } return 0; } DECLARE_FUNC(upet_file_type) { int file_type = atoi(val_str); switch (file_type) { case UPET_FT_IMAGE: /* Image file */ case UPET_FT_MU_MAP: /* Mu map file */ return (0); default: message(MSG_WARNING, "File type %d is not handled. Conversion results may be problematic...\n", file_type); break; } return (1); } DECLARE_FUNC(upet_acq_mode) { int mode_int = atoi(val_str); char *mode_str; switch (mode_int) { case UPET_AM_UNKNOWN: mode_str = "unknown"; break; case UPET_AM_BLANK: mode_str = "blank"; break; case UPET_AM_EMISSION: mode_str = "emission"; break; case UPET_AM_DYNAMIC: mode_str = "dynamic"; break; case UPET_AM_GATED: mode_str = "gated"; break; case UPET_AM_CONTINUOUS_BED_MOTION: mode_str = "continuous_bed_motion"; break; case UPET_AM_SINGLES_TRANSMISSION: mode_str = "singles_transmission"; break; case UPET_AM_WINDOWED_COINCIDENCE_TRANSMISSION: mode_str = "windowed_coincidence_transmission"; break; case UPET_AM_NONWINDOWED_COINCIDENCE_TRANSMISSION: mode_str = "non-windowed_coincidence_transmission"; break; default: message(MSG_WARNING, "Unknown acquisition mode %d\n", mode_int); mode_str = NULL; break; } if (mode_str != NULL) { miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIacquisition), "micropet_mode", mode_str); } return (0); } DECLARE_FUNC(upet_bed_motion) { return (0); } DECLARE_FUNC(upet_data_type) { ci_ptr->data_type = atoi(val_str); switch (ci_ptr->data_type) { case UPET_DT_BYTE: ci_ptr->minc_type = NC_BYTE; break; case UPET_DT_II16: ci_ptr->minc_type = NC_SHORT; ci_ptr->frame_nbytes *= 2; if (is_host_big_endian()) { ci_ptr->swap_size = 2; } else { ci_ptr->swap_size = 0; } break; case UPET_DT_II32: ci_ptr->minc_type = NC_INT; ci_ptr->frame_nbytes *= 4; if (is_host_big_endian()) { ci_ptr->swap_size = 4; } else { ci_ptr->swap_size = 0; } break; case UPET_DT_IF32: ci_ptr->minc_type = NC_FLOAT; ci_ptr->frame_nbytes *= 4; if (is_host_big_endian()) { ci_ptr->swap_size = 4; } else { ci_ptr->swap_size = 0; } break; case UPET_DT_MF32: ci_ptr->minc_type = NC_FLOAT; ci_ptr->frame_nbytes *= 4; if (!is_host_big_endian()) { ci_ptr->swap_size = 4; } else { ci_ptr->swap_size = 0; } break; case UPET_DT_MI16: ci_ptr->minc_type = NC_SHORT; ci_ptr->frame_nbytes *= 2; if (!is_host_big_endian()) { ci_ptr->swap_size = 2; } else { ci_ptr->swap_size = 0; } break; case UPET_DT_MI32: ci_ptr->minc_type = NC_INT; ci_ptr->frame_nbytes *= 4; if (!is_host_big_endian()) { ci_ptr->swap_size = 4; } else { ci_ptr->swap_size = 0; } break; default: message(MSG_ERROR, "Unknown data type %d\n", ci_ptr->data_type); return (1); } if (ci_ptr->swap_size != 0) { message(MSG_INFO, "Swapping groups of %d bytes.\n", ci_ptr->swap_size); } else { message(MSG_INFO, "No byte-swapping required.\n"); } return (0); } DECLARE_FUNC(upet_data_order) { if (atoi(val_str) != 1) { message(MSG_WARNING, "Unknown data order.\n"); } return (0); } DECLARE_FUNC(upet_ndims) { ci_ptr->dim_count = atoi(val_str); return (0); } static void create_dimension(struct conversion_info *ci_ptr, int index, int length) { ci_ptr->dim_lengths[index] = length; if (index == DIM_W && length <= 1) { return; } ci_ptr->dim_ids[index] = ncdimdef(ci_ptr->mnc_fd, _dimnames[index], length); if (index == DIM_T) { micreate_std_variable(ci_ptr->mnc_fd, _dimnames[index], NC_DOUBLE, 1, &ci_ptr->dim_ids[index]); micreate_std_variable(ci_ptr->mnc_fd, MItime_width, NC_DOUBLE, 1, &ci_ptr->dim_ids[index]); } else if (index != DIM_W) { micreate_std_variable(ci_ptr->mnc_fd, _dimnames[index], NC_DOUBLE, 0, NULL); } } DECLARE_FUNC(upet_total_frames) { create_dimension(ci_ptr, DIM_T, atoi(val_str)); return (0); } DECLARE_FUNC(upet_x_dim) { int x = atoi(val_str); ci_ptr->frame_nbytes *= x; ci_ptr->frame_nvoxels *= x; create_dimension(ci_ptr, DIM_X, x); return (0); } DECLARE_FUNC(upet_y_dim) { int y = atoi(val_str); ci_ptr->frame_nbytes *= y; ci_ptr->frame_nvoxels *= y; create_dimension(ci_ptr, DIM_Y, y); return (0); } DECLARE_FUNC(upet_z_dim) { int z = atoi(val_str); ci_ptr->frame_nbytes *= z; ci_ptr->frame_nvoxels *= z; create_dimension(ci_ptr, DIM_Z, z); return (0); } DECLARE_FUNC(upet_vector_dim) { int w = atoi(val_str); ci_ptr->frame_nbytes *= w; ci_ptr->frame_nvoxels *= w; create_dimension(ci_ptr, DIM_W, w); return (0); } /* Parse a micropet time string of the form: Ddd Mmm NN HH:MM:SS YYYY * e.g. Fri Jan 7 14:16:31 2005 */ static int parse_time(char *str_ptr, struct tm *tm_ptr) { /* Just skip the first three characters. */ while (*str_ptr != '\0' && *str_ptr != ' ') { str_ptr++; } while (*str_ptr == ' ') { str_ptr++; } /* Decode the month */ if (str_ptr[0] == 'A') { if (str_ptr[1] == 'p') { tm_ptr->tm_mon = 4 - 1; /* April */ } else { tm_ptr->tm_mon = 8 - 1; /* August */ } } else if (str_ptr[0] == 'D') { tm_ptr->tm_mon = 12 - 1; /* December */ } else if (str_ptr[0] == 'F') { /* February */ tm_ptr->tm_mon = 2 - 1; } else if (str_ptr[0] == 'J') { if (str_ptr[1] == 'a') { tm_ptr->tm_mon = 1 - 1; /* January */ } else if (str_ptr[2] == 'l') { tm_ptr->tm_mon = 7 - 1; /* July */ } else { tm_ptr->tm_mon = 6 - 1; /* June */ } } else if (str_ptr[0] == 'M') { if (str_ptr[2] == 'r') { tm_ptr->tm_mon = 3 - 1; /* March */ } else { tm_ptr->tm_mon = 5 - 1; /* May */ } } else if (str_ptr[0] == 'N') { tm_ptr->tm_mon = 11 - 1; /* November */ } else if (str_ptr[0] == 'O') { tm_ptr->tm_mon = 10 - 1; /* October */ } else if (str_ptr[0] == 'S') { tm_ptr->tm_mon = 9 - 1; /* September */ } else { return 0; } /* Skip past the month */ while (*str_ptr != ' ' && *str_ptr != '\0') { str_ptr++; } while (*str_ptr == ' ') { str_ptr++; } tm_ptr->tm_mday = 0; while (isdigit(*str_ptr)) { tm_ptr->tm_mday = (tm_ptr->tm_mday * 10) + (*str_ptr++ - '0'); } while (*str_ptr == ' ') { str_ptr++; } tm_ptr->tm_hour = 0; while (isdigit(*str_ptr)) { tm_ptr->tm_hour = (tm_ptr->tm_hour * 10) + (*str_ptr++ - '0'); } if (*str_ptr == ':') { str_ptr++; } else { return 0; } tm_ptr->tm_min = 0; while (isdigit(*str_ptr)) { tm_ptr->tm_min = (tm_ptr->tm_min * 10) + (*str_ptr++ - '0'); } if (*str_ptr == ':') { str_ptr++; } else { return 0; } tm_ptr->tm_sec = 0; while (isdigit(*str_ptr)) { tm_ptr->tm_sec = (tm_ptr->tm_sec * 10) + (*str_ptr++ - '0'); } while (*str_ptr == ' ') { str_ptr++; } tm_ptr->tm_year = 0; while (isdigit(*str_ptr)) { tm_ptr->tm_year = (tm_ptr->tm_year * 10) + (*str_ptr++ - '0'); } tm_ptr->tm_year -= 1900; return 1; } DECLARE_FUNC(upet_injection_time) { struct tm tmbuf; int id; char str_buf[128]; id = ncvarid(ci_ptr->mnc_fd, MIacquisition); if (!parse_time(val_str, &tmbuf)) { strcpy(str_buf, "unknown"); miattputstr(ci_ptr->mnc_fd, id, MIinjection_time, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIinjection_year, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIinjection_month, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIinjection_day, str_buf); } else { sprintf(str_buf, "%02d%02d%02d", tmbuf.tm_hour, tmbuf.tm_min, tmbuf.tm_sec); miattputstr(ci_ptr->mnc_fd, id, MIinjection_time, str_buf); sprintf(str_buf, "%d", tmbuf.tm_year + 1900); miattputstr(ci_ptr->mnc_fd, id, MIinjection_year, str_buf); sprintf(str_buf, "%d", tmbuf.tm_mon + 1); miattputstr(ci_ptr->mnc_fd, id, MIinjection_month, str_buf); sprintf(str_buf, "%d", tmbuf.tm_mday); miattputstr(ci_ptr->mnc_fd, id, MIinjection_day, str_buf); } return (0); } DECLARE_FUNC(upet_scan_time) { struct tm tmbuf; int id; char str_buf[128]; id = ncvarid(ci_ptr->mnc_fd, MIstudy); if (!parse_time(val_str, &tmbuf)) { strcpy(str_buf, "unknown"); miattputstr(ci_ptr->mnc_fd, id, MIstart_time, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIstart_year, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIstart_month, str_buf); miattputstr(ci_ptr->mnc_fd, id, MIstart_day, str_buf); } else { sprintf(str_buf, "%02d%02d%02d", tmbuf.tm_hour, tmbuf.tm_min, tmbuf.tm_sec); miattputstr(ci_ptr->mnc_fd, id, MIstart_time, str_buf); sprintf(str_buf, "%d", tmbuf.tm_year + 1900); miattputstr(ci_ptr->mnc_fd, id, MIstart_year, str_buf); sprintf(str_buf, "%d", tmbuf.tm_mon + 1); miattputstr(ci_ptr->mnc_fd, id, MIstart_month, str_buf); sprintf(str_buf, "%d", tmbuf.tm_mday); miattputstr(ci_ptr->mnc_fd, id, MIstart_day, str_buf); } return (0); } DECLARE_FUNC(upet_axial_crystal_pitch) { double dbl_tmp = atof(val_str); /* dbl_tmp is in cm. Convert to mm. */ dbl_tmp *= 10.0; /* Now convert from crystal pitch to actual slice width */ dbl_tmp /= 2.0; ci_ptr->dim_steps[DIM_Z] = dbl_tmp; return (0); } DECLARE_FUNC(upet_pixel_size) { double dbl_tmp = atof(val_str); /* dbl_tmp is in cm. Convert to mm. */ dbl_tmp *= 10.0; ci_ptr->dim_steps[DIM_X] = dbl_tmp; ci_ptr->dim_steps[DIM_Y] = dbl_tmp; return (0); } DECLARE_FUNC(upet_dose_units) { int tmp = atoi(val_str); char *str_ptr; if (tmp == 0) { str_ptr = "unknown"; } else if (tmp == 1) { str_ptr = "mCi"; } else if (tmp == 2) { str_ptr = "MBq"; } else { str_ptr = "???????"; message(MSG_WARNING, "Unrecognized dose_units value %d\n", tmp); } miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, new_var), new_att, str_ptr); return (0); } DECLARE_FUNC(upet_calibration_factor) { double dbl_tmp = atof(val_str); ci_ptr->calibration_factor = dbl_tmp; return (0); } DECLARE_FUNC(upet_isotope_branching_fraction) { double dbl_tmp = atof(val_str); ci_ptr->isotope_branching_fraction = dbl_tmp; return (0); } DECLARE_FUNC(upet_rotation) { double dbl_tmp = atof(val_str); if (dbl_tmp != 0.0) { message(MSG_WARNING, "Rotation is %f\n", dbl_tmp); } return (0); } /***********************/ /* Per-frame functions */ DECLARE_FUNC(upet_frame_no) { ci_ptr->frame_index = atoi(val_str); /* Set index of "zeroth" frame if not already set. */ if (ci_ptr->frame_zero < 0) { ci_ptr->frame_zero = ci_ptr->frame_index; } return (0); } DECLARE_FUNC(upet_frame_start) { long index = ci_ptr->frame_index - ci_ptr->frame_zero; double dbl_tmp = atof(val_str); mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MItime), &index, NC_DOUBLE, MI_SIGNED, &dbl_tmp); return (0); } DECLARE_FUNC(upet_frame_duration) { long index = ci_ptr->frame_index - ci_ptr->frame_zero; double dbl_tmp = atof(val_str); mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MItime_width), &index, NC_DOUBLE, MI_SIGNED, &dbl_tmp); return (0); } DECLARE_FUNC(upet_frame_min) { long index = ci_ptr->frame_index - ci_ptr->frame_zero; double dbl_tmp = atof(val_str); dbl_tmp *= COMBINED_SCALE_FACTOR(ci_ptr); mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimagemin), &index, NC_DOUBLE, MI_SIGNED, &dbl_tmp); return (0); } DECLARE_FUNC(upet_frame_max) { long index = ci_ptr->frame_index - ci_ptr->frame_zero; double dbl_tmp = atof(val_str); dbl_tmp *= COMBINED_SCALE_FACTOR(ci_ptr); mivarput1(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimagemax), &index, NC_DOUBLE, MI_SIGNED, &dbl_tmp); return (0); } DECLARE_FUNC(upet_frame_file_ptr) { long lopart; char *end_ptr; lopart = strtol(val_str, &end_ptr, 10); if (*end_ptr == ' ') { while (*end_ptr == ' ') { end_ptr++; } /* If we found spaces, that implies there was a high-part followed * by the low-part of the frame file pointer. We just ignore the * high part. */ if (isdigit(*end_ptr)) { lopart = strtol(end_ptr, NULL, 10); } } /* Seek the image file to the data */ fseek(ci_ptr->img_fp, lopart, SEEK_SET); return (0); } DECLARE_FUNC(upet_frame_scale_factor) { ci_ptr->scale_factor = atof(val_str); return (0); } DECLARE_FUNC(upet_frame_deadtime_correction) { ci_ptr->deadtime_correction = atof(val_str); return (0); } DECLARE_FUNC(upet_frame_decay_correction) { ci_ptr->decay_correction = atof(val_str); return (0); } static void swap_data(int swap_size, long nvox, unsigned char *data) { unsigned char tmp; if (swap_size == 2) { while (nvox--) { tmp = data[0]; data[0] = data[1]; data[1] = tmp; data += 2; } } else if (swap_size == 4) { while (nvox--) { tmp = data[0]; data[0] = data[3]; data[3] = tmp; tmp = data[1]; data[1] = data[2]; data[2] = tmp; data += 4; } } } static void scale_data(nc_type datatype, long nvox, void *data, double scale) { long i; double tmp; switch (datatype) { case NC_BYTE: for (i = 0; i < nvox; i++) { tmp = (double) ((char *)data)[i]; tmp *= scale; ((char *)data)[i] = tmp; } break; case NC_SHORT: for (i = 0; i < nvox; i++) { tmp = (double) ((short *)data)[i]; tmp *= scale; ((short *)data)[i] = tmp; } break; case NC_INT: for (i = 0; i < nvox; i++) { tmp = (double) ((int *)data)[i]; tmp *= scale; ((int *)data)[i] = tmp; } break; case NC_FLOAT: for (i = 0; i < nvox; i++) { tmp = (double) ((float *)data)[i]; tmp *= scale; ((float *)data)[i] = tmp; } break; case NC_DOUBLE: for (i = 0; i < nvox; i++) { tmp = (double) ((double *)data)[i]; tmp *= scale; ((double *)data)[i] = tmp; } break; default: message(MSG_ERROR, "Data type %d not handled\n", datatype); break; } } void copy_init(struct conversion_info *ci_ptr) { ci_ptr->frame_buffer = malloc(ci_ptr->frame_nbytes); if (ci_ptr->frame_buffer == NULL) { message(MSG_FATAL, "Out of memory\n"); exit(-1); } /* Create the image, imagemax, and imagemin variables. */ micreate_std_variable(ci_ptr->mnc_fd, MIimagemax, NC_DOUBLE, 1, ci_ptr->dim_ids); micreate_std_variable(ci_ptr->mnc_fd, MIimagemin, NC_DOUBLE, 1, ci_ptr->dim_ids); micreate_std_variable(ci_ptr->mnc_fd, MIimage, ci_ptr->minc_type, ci_ptr->dim_count + 1, ci_ptr->dim_ids); /* Set up the dimension step and start values. Because of the microPET * data orientation, we set Z and Y to be the inverse of the norm, to * put the animal's nose at the top of the display. * TODO: allow this behavior to be controlled on the command line. */ miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]), MIstep, -ci_ptr->dim_steps[DIM_Z]); miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]), MIstep, ci_ptr->dim_steps[DIM_X]); miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]), MIstep, -ci_ptr->dim_steps[DIM_Y]); miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]), MIstart, ci_ptr->dim_steps[DIM_Z] * ci_ptr->dim_lengths[DIM_Z]); miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]), MIstart, 0.0); miattputdbl(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]), MIstart, ci_ptr->dim_steps[DIM_Y] * ci_ptr->dim_lengths[DIM_Y]); miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Z]), MIunits, "mm"); miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_X]), MIunits, "mm"); miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_Y]), MIunits, "mm"); miattputstr(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, _dimnames[DIM_T]), MIunits, "s"); ncendef(ci_ptr->mnc_fd); } static void copy_frame(struct conversion_info *ci_ptr) { long start[5]; long count[5]; size_t nitems; message(MSG_INFO, "Inserting frame #%d\n", ci_ptr->frame_index); /* Actually read the data from the image file. */ nitems = fread(ci_ptr->frame_buffer, ci_ptr->frame_nbytes, 1, ci_ptr->img_fp); if (nitems != 1) { message(MSG_FATAL, "Read failed with error %d, return %d\n", errno, nitems); exit(-1); } /* Setup the starts and counts for the data block. */ start[DIM_T] = ci_ptr->frame_index - ci_ptr->frame_zero; start[DIM_X] = 0; start[DIM_Y] = 0; start[DIM_Z] = 0; start[DIM_W] = 0; count[DIM_T] = 1; count[DIM_X] = ci_ptr->dim_lengths[DIM_X]; count[DIM_Y] = ci_ptr->dim_lengths[DIM_Y]; count[DIM_Z] = ci_ptr->dim_lengths[DIM_Z]; count[DIM_W] = ci_ptr->dim_lengths[DIM_W]; /* Perform swapping if necessary. */ if (ci_ptr->swap_size != 0) { swap_data(ci_ptr->swap_size, ci_ptr->frame_nvoxels, ci_ptr->frame_buffer); } /* Scale the raw data into the final range. */ scale_data(ci_ptr->minc_type, ci_ptr->frame_nvoxels, ci_ptr->frame_buffer, COMBINED_SCALE_FACTOR(ci_ptr)); /* For now we perform no conversions on the data as it is stored. * This may be worth modifying in the future, to allow storage of * non-floating-point formats from a typical microPET file. */ ncvarput(ci_ptr->mnc_fd, ncvarid(ci_ptr->mnc_fd, MIimage), start, count, ci_ptr->frame_buffer); } minc-tools-2.3.00+dfsg/conversion/micropet/upet2mnc.man10000644000175000000620000000322412574624760022145 0ustar stevestaff.TH upet2mnc 1 "May 03 2005" "$Revision: 1.3 $" "" .SH NAME .B upet2mnc - convert a Concorde microPET format file to a MINC format file. .SH SYNOPSIS .B upet2mnc .I [] .B upet2mnc .I -help .SH DESCRIPTION The .B upet2mnc command is used to convert Concorde microPET format files to MINC format. The microPET format consists of two files, a binary image and a text header. The header file generally has the same name as the image with the suffix ".hdr" appended. Normally you can specify the name of either the binary image or the text header file on the command line. However, both of the files should be in the same directory for the converter to locate both files correctly. .SH "OPTIONS" Note that options can be specified in abbreviated form (as long as they are unique) and can be given anywhere on the command line. .TP .BI -head Orient the image according to typical neurological conventions, with the Y axis oriented from the posterior to anterior, the Z axis oriented from inferior to superior, and X oriented from patient left to patient right. .TP .BI -body Orient the image such that the Y axis is oriented from superior to inferior, the Z axis is oriented from posterior to anterior, and X from patient left to patient right. .TP .BI -quiet Quiet operation - do not print progress or debugging information. .SH "Generic options for all commands" .TP .BI -help Print summary of command\-line options and abort .TP .BI -version Print the program and library versions and abort .SH AUTHOR Robert Vincent (bert@bic.mni.mcgill.ca) .SH "COPYRIGHTS" Copyrights 2005 by Robert Vincent for the Montreal Neurological Institute. minc-tools-2.3.00+dfsg/AUTHORS0000644000175000000620000000027512574624760014676 0ustar stevestaffLeila Baghdadi Vladimir Fonov Colin Holmes Andrew Janke David Leonard Claude Lepage Ilana Leppert Jason Lerch Dave McDonald Peter Neelin Steven Robbins John Sled Robert Vincent Andrew Wood minc-tools-2.3.00+dfsg/Testing/0002755000175000000620000000000012574624760015241 5ustar stevestaffminc-tools-2.3.00+dfsg/Testing/minc2-testminctools.sh0000755000175000000620000000150112574624760021510 0ustar stevestaff#! /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 minc-tools-2.3.00+dfsg/Testing/CMakeLists.txt0000644000175000000620000000561112574624760020002 0ustar stevestaff# Fix tools tests to avoid modifications to source directory (bert). # # Get path to mincstats binary. GET_PROPERTY(mincstats_bin TARGET mincstats PROPERTY LOCATION) # Get path to mincinfo binary. GET_PROPERTY(mincinfo_bin TARGET mincinfo PROPERTY LOCATION) # Get path to mincextract binary GET_PROPERTY(mincextract_bin TARGET mincextract PROPERTY LOCATION) # Get path to mincaverage binary. GET_PROPERTY(mincaverage_bin TARGET mincaverage PROPERTY LOCATION) # Get path to mincaverage binary. GET_PROPERTY(mincresample_bin TARGET mincresample PROPERTY LOCATION) # Copy files used by minccalc-test and other tests. CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/test-zero.mnc" "${CMAKE_CURRENT_BINARY_DIR}/test-zero.mnc" COPYONLY) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/test-one.mnc" "${CMAKE_CURRENT_BINARY_DIR}/test-one.mnc" COPYONLY) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/test-two.mnc" "${CMAKE_CURRENT_BINARY_DIR}/test-two.mnc" COPYONLY) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/test-rnd.mnc" "${CMAKE_CURRENT_BINARY_DIR}/test-rnd.mnc" COPYONLY) ADD_TEST(mincinfo-test ${CMAKE_CURRENT_SOURCE_DIR}/mincinfo-test.pl) SET_TESTS_PROPERTIES(mincinfo-test PROPERTIES ENVIRONMENT "MINCINFO_BIN=${mincinfo_bin}") ADD_TEST(mincstats-test ${CMAKE_CURRENT_SOURCE_DIR}/mincstats-test.pl) SET_TESTS_PROPERTIES(mincstats-test PROPERTIES ENVIRONMENT "MINCSTATS_BIN=${mincstats_bin}") # Copy files used by mincaverage-test CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/mincaverage-in0.mnc" "${CMAKE_CURRENT_BINARY_DIR}/mincaverage-in0.mnc" COPYONLY) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/mincaverage-in1.mnc" "${CMAKE_CURRENT_BINARY_DIR}/mincaverage-in1.mnc" COPYONLY) # Add the test itself. ADD_TEST(mincaverage-test ${CMAKE_CURRENT_SOURCE_DIR}/mincaverage-test.sh) # Set its environment variables. SET_TESTS_PROPERTIES(mincaverage-test PROPERTIES ENVIRONMENT "MINCAVERAGE_BIN=${mincaverage_bin};MINCSTATS_BIN=${mincstats_bin}") # Get path to the binary. GET_PROPERTY(minccalc_bin TARGET minccalc PROPERTY LOCATION) # Add the test. ADD_TEST(minccalc-test ${CMAKE_CURRENT_SOURCE_DIR}/minccalc-test.sh) # Set the test's environment. SET_TESTS_PROPERTIES(minccalc-test PROPERTIES ENVIRONMENT "MINCCALC_BIN=${minccalc_bin};MINCSTATS_BIN=${mincstats_bin}") # Add the test itself. ADD_TEST(mincresample-test ${CMAKE_CURRENT_SOURCE_DIR}/mincresample-test.sh) # Set its environment variables. SET_TESTS_PROPERTIES(mincresample-test PROPERTIES ENVIRONMENT "MINCRESAMPLE_BIN=${mincresample_bin};MINCSTATS_BIN=${mincstats_bin}") # Get path to mincaverage binary. GET_PROPERTY(mincreshape_bin TARGET mincreshape PROPERTY LOCATION) ADD_TEST(mincreshape-test ${CMAKE_CURRENT_SOURCE_DIR}/mincreshape-test.pl) SET_TESTS_PROPERTIES(mincreshape-test PROPERTIES ENVIRONMENT "MINCRESHAPE_BIN=${mincreshape_bin};MINCSTATS_BIN=${mincstats_bin};MINCINFO_BIN=${mincinfo_bin};MINCEXTRACT_BIN=${mincextract_bin}") #TODO: add more tests? minc-tools-2.3.00+dfsg/Testing/mincresample-test.sh0000755000175000000620000000141112574624760021227 0ustar stevestaff#! /bin/bash let errors=0; if [[ ! -x $MINCSTATS_BIN ]]; then MINCSTATS_BIN=`which mincstats`; fi if [[ ! -x $MINCRESAMPLE_BIN ]]; then MINCRESAMPLE_BIN=`which mincresample`; fi # Test the standard (no-normalize) case. This has always worked. $MINCRESAMPLE_BIN -clobber test-rnd.mnc mincresample-out.mnc r1=`$MINCSTATS_BIN -quiet -sum mincresample-out.mnc` if [[ $r1 != "250" ]]; then echo "Problem with default operation:" $r1 exit 1; fi; # Now test the keep_real_range case. This failed until fixed in Feb 2015. $MINCRESAMPLE_BIN -keep_real_range -clobber test-rnd.mnc mincresample-out.mnc r2=`$MINCSTATS_BIN -quiet -sum mincresample-out.mnc` if [[ $r2 != "250" ]]; then echo "Problem with -keep_real_range operation:" $r2 exit 1; fi; echo "OK." exit 0 minc-tools-2.3.00+dfsg/Testing/mincreshape-test.pl0000755000175000000620000001032012574624760021046 0ustar stevestaff#! /usr/bin/env perl use strict; my $errors=0; my $mincstats_bin = `which mincstats`; chomp($mincstats_bin); if ($ENV{'MINCSTATS_BIN'}) { $mincstats_bin = $ENV{'MINCSTATS_BIN'}; } my $mincreshape_bin = `which mincreshape`; chomp($mincreshape_bin); if ($ENV{'MINCRESHAPE_BIN'}) { $mincreshape_bin = $ENV{'MINCRESHAPE_BIN'}; } my $mincinfo_bin = `which mincinfo`; chomp($mincinfo_bin); if ($ENV{'MINCINFO_BIN'}) { $mincinfo_bin = $ENV{'MINCINFO_BIN'}; } my $mincextract_bin = `which mincextract`; chomp($mincextract_bin); if ($ENV{'MINCEXTRACT_BIN'}) { $mincextract_bin = $ENV{'MINCEXTRACT_BIN'}; } # Case 1 - Test a very basic case. We reshape the data into unsigned # short integer in order to set up the next test. # system("$mincreshape_bin -quiet -clobber -unsigned -short test-one.mnc mincreshape-t1.mnc"); my $r1=`$mincstats_bin -clobber -integer_histogram -histogram mincreshape-t1.txt -quiet -sum mincreshape-t1.mnc`; if ($r1 != 125) { $errors++; print "Test 1 failed basic check.\n"; } my @h1 = read_histogram('mincreshape-t1.txt'); if ($h1[1] != 125) { $errors++; print "Test 1 failed histogram check.\n"; } # Case 2 - Now test the case where the fill data is out of range. system("$mincreshape_bin -quiet -clobber -start -1,-1,-1 mincreshape-t1.mnc mincreshape-t2.mnc"); my $r1=`$mincstats_bin -clobber -integer_histogram -histogram mincreshape-t2.txt -quiet -sum mincreshape-t2.mnc`; my @h2 = read_histogram('mincreshape-t2.txt'); if ($h2[1] != 64 || $h2[0] != 61) { $errors++; print "Test 2 failed histogram check.\n"; } # Case 3 - Now test the case where the fill data is out of range. system("$mincreshape_bin -quiet -fillvalue 2 -clobber -start -1,-1,-1 mincreshape-t1.mnc mincreshape-t3.mnc"); my $r1=`$mincstats_bin -clobber -integer_histogram -histogram mincreshape-t3.txt -quiet -sum mincreshape-t3.mnc`; my @h3 = read_histogram('mincreshape-t3.txt'); if ($h3[1] != 64 || $h3[2] != 61) { $errors++; print "Test 3 failed histogram check.\n"; } # Case 4 - Verify the operation of some simple manipulations system("$mincreshape_bin -quiet -clobber -ydirection -unsigned -byte test-rnd.mnc mincreshape-t4.mnc"); my $r1=`$mincstats_bin -clobber -integer_histogram -histogram mincreshape-t4.txt -quiet -sum2 mincreshape-t4.mnc`; if (int($r1) != 750) { $errors++; print "Test 4 failed simple sum^2 check.\n"; } my $r1 = `$mincinfo_bin -attvalue yspace:step test-rnd.mnc`; my $r2 = `$mincinfo_bin -attvalue yspace:step mincreshape-t4.mnc`; if ($r1 != -$r2) { $errors++; print "Test 4 failed step sign change check.\n"; } my $r1 = `$mincextract_bin -byte -range 0 4 -start 4,0,0 -count 1,5,1 test-rnd.mnc | od -An -td1`; my $r2 = `$mincextract_bin -byte -range 0 4 -start 4,0,0 -count 1,5,1 mincreshape-t4.mnc | od -An -td1`; my @a1 = split(' ', $r1); my @a2 = split(' ', $r2); for (my $i = 0; $i < 5; $i++) { if ($a1[$i] != $a2[5-$i-1]) { $errors++; print "Test 4 failed data direction check.\n"; } } my @h4 = read_histogram('mincreshape-t4.txt'); if ($h4[0] != 25 || $h4[1] != 25 || $h4[2] != 25 || $h4[3] != 25 || $h4[4] != 25) { $errors++; print "Test 4 failed histogram check.\n"; } # Case 5 - Now test a harder case where the fill data is out of range. system("$mincreshape_bin -clobber -quiet -unsigned -byte test-rnd.mnc mincreshape-t5a.mnc"); system("$mincreshape_bin -quiet -fillvalue 100 -clobber -start -1,0,0 mincreshape-t5a.mnc mincreshape-t5.mnc"); my $r1=`$mincstats_bin -clobber -integer_histogram -histogram mincreshape-t5.txt -quiet -sum mincreshape-t5.mnc`; my @h5 = read_histogram('mincreshape-t5.txt'); if ($h5[0] != 20 || $h5[1] != 20 || $h5[2] != 20 || $h5[3] != 20 || $h5[4] != 20 || $h5[100] != 25) { $errors++; print "Test 5 failed histogram check.\n"; for (my $i = 0; $i <= 1000; $i++) { print "$i $h5[$i]\n" if $h5[$i] != 0; } } print "OK.\n" if $errors == 0; print exit $errors > 0; sub read_histogram { my @hist = (); my ($filename) = @_; open(my $fh, "<", $filename); foreach my $line (<$fh>) { if ($line !~ /^#/) { my @fields = split(' ', $line); $hist[$fields[0]] = $fields[1] if @fields == 2; } } close($fh); return @hist; } minc-tools-2.3.00+dfsg/Testing/mincaverage-test.sh0000755000175000000620000000143412574624760021036 0ustar stevestaff#! /bin/bash if [[ ! -x $MINCAVERAGE_BIN ]]; then MINCAVERAGE_BIN=`which mincaverage`; fi; if [[ ! -x $MINCSTATS_BIN ]]; then MINCSTATS_BIN=`which mincstats`; fi; # Test the standard (no-normalize) case. This has always worked. $MINCAVERAGE_BIN -clobber mincaverage-in0.mnc mincaverage-in1.mnc mincaverage-out.mnc r1=`$MINCSTATS_BIN -quiet -sum mincaverage-out.mnc` if [[ $r1 != "-88.5" ]]; then echo "Problem with non-normalized average:" $r1 exit 1; fi; # Now test the normalize case. This failed until fixed in Oct 2014. $MINCAVERAGE_BIN -normalize -clobber mincaverage-in0.mnc mincaverage-in1.mnc mincaverage-out.mnc r2=`$MINCSTATS_BIN -quiet -sum mincaverage-out.mnc` if [[ $r2 != "-22.25" ]]; then echo "Problem with normalized average:" $r2 exit 1; fi; echo "OK." exit 0 minc-tools-2.3.00+dfsg/Testing/mincinfo-test.pl0000755000175000000620000001265112574624760020363 0ustar stevestaff#! /usr/bin/env perl use strict; my $errors=0; my $mincinfo_bin = `which mincinfo`; chomp($mincinfo_bin); if ($ENV{'MINCINFO_BIN'}) { $mincinfo_bin = $ENV{'MINCINFO_BIN'}; } print "Case 1 - Test the essential command line options.\n"; my $r1 = `$mincinfo_bin -version`; if ($? != 0) { print "-version returned something other than zero.\n"; $errors++; } my @arr = split(/^/m, $r1); if ($#arr < 1 || $arr[0] !~ /^program: \d+\.\d+\.\d+/) { print "-version message line 1 has wrong format.\n"; $errors++; } my $r1 = `$mincinfo_bin -help 2>&1`; if ($? != 256) { print "-help returned something other than 256 (-1 as unsigned char).\n"; $errors++; } my @arr = split(/^/m, $r1); if ($arr[0] !~ /^Command-specific options:$/) { print "-help message line 1 has wrong format.\n"; $errors++; } print "Case 2 - test operation with no command-line options.\n"; my $r1 = `$mincinfo_bin test-zero.mnc`; my $c2result = < 0; minc-tools-2.3.00+dfsg/Testing/xfmconcat_02.sh0000755000175000000620000000163112574624760020062 0ustar stevestaff#! /bin/sh # # Test application of concatenated transforms. set -e # _t1.xfm is linear transform. # _t2.xfm is inverse of _t1 # _t3.xfm is grid transform. # _t4.xfm is inverse of _t3 cp $srcdir/t1.xfm _t1.xfm ../xfminvert -clobber _t1.xfm _t2.xfm dd if=/dev/zero | ../rawtominc -vector 3 -byte -clobber _grid.mnc 8 8 8 ./create_grid_xfm _grid.mnc _t3.xfm ../xfminvert -clobber _t3.xfm _t4.xfm # Test primary transforms. # ./test_xfm 10000 _t1.xfm ./test_xfm 10000 _t2.xfm ./test_xfm 10000 _t3.xfm ./test_xfm 10000 _t4.xfm # Test concatenations. # ../xfmconcat -clobber _t1.xfm _t3.xfm _t5.xfm ./test_xfm 10000 _t5.xfm ../xfmconcat -clobber _t1.xfm _t4.xfm _t6.xfm ./test_xfm 10000 _t6.xfm ../xfmconcat -clobber _t5.xfm _t6.xfm _t7.xfm ./test_xfm 10000 _t7.xfm ../xfminvert -clobber _t7.xfm _t8.xfm ./test_xfm 10000 _t8.xfm ../xfmconcat -clobber _t8.xfm _t2.xfm _t4.xfm _t7.xfm _t9.xfm ./test_xfm 10000 _t9.xfm minc-tools-2.3.00+dfsg/Testing/test-rnd.mnc0000644000175000000620000003420312574624760017500 0ustar stevestaffHDF  8`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX zspaceyspacexspace8SNOD(Pp 80 X x X x TREE@ HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE%HEAPX0image-minimage-maximage(SNOD Pident.rvincent:ace-ws-21:2015.02.02.15.25.40:3475:1  DT 8versionMINC Version 1.0 0vartype group________@SNOD(  8varidMINC standard variable DT 8versionMINC Version 1.0 0vartype group________ 8varidMINC standard variable DT 8versionMINC Version 1.0 0vartype group________ 8varidMINC standard variableHh 0 minc_version2.3.00*H ?@4 4DT 0 length  8varidMINC standard variable SNOD!  0vartypedimension____ 8versionMINC Version 1.0 X comments.Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 0 dimorderzspace 8step ?@4 4? 8start ?@4 4 ?@4 4DT 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 ?@4 4DT 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  ?@4 4DT 8varidMINC standard variable 8versionMINC Version 1.0&8SNOD(X(('$ 0vartypevar_attribute  ?@4 4@DT 8varidMINC standard variable 8versionMINC Version 1.0 0vartypevar_attribute `8   deflate +DT @ dimorderzspace,yspace,xspace 8varidMINC standard variable 0vartypegroup________ 8versionMINC Version 1.0 P valid_range  @ 0 completetrue_ historyFri Oct 31 11:55:05 2014>>> mincreshape -float n0.mnc f0.mnc Mon Feb 2 15:25:40 2015>>> mincreshape -valid_range 0 4 -image_range 0 4 /home/rvincent/Documents/BIC/minc-toolkit/minctools/Testing/test-rnd.mnc rnd.mnc TREEs8x^mQ!BYͣ 9*vX|^sG~ɘy̽;Z׌op~^F2Ùzcmsfg^7f{yyKU-3CJ'ߵݘ-8(minc-tools-2.3.00+dfsg/Testing/mincaverage-in1.mnc0000644000175000000620000003406612574624760020715 0ustar stevestaffHDF  68`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX xspaceyspacezspace8SNOD(Pp 80 X x X x TREE HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE8HEAPX0imageimage-minimage-max(SNOD* Pident.rvincent:ace-ws-21:2014.10.31.11.55.11:28835:18   deflate +^WTH#HSNOD(  @ dimorderzspace,yspace,xspace  ?@4 4^WT 8varidMINC standard variable 8versionMINC Version 1.0`$8  ?@4 4?^WT 8varidMINC standard variable 8versionMINC Version 1.0$8 ?@4 4^WT 0 length  8varidMINC standard variable$SNODXp ?@4 4^WT 0 length  8varidMINC standard variable& ?@4 4^WT 0 length  8varidMINC standard variableX(  ^WT 8versionMINC Version 1.0 0vartype group________ *@SNOD "  ^WT 8versionMINC Version 1.0 0vartype group________`*@  ^WT 8versionMINC Version 1.0 0vartype group________*@ 8varidMINC standard variable 0vartype group________ 8versionMINC Version 1.0 P valid_range  ? 0 completetrue_ 0vartype var_attribute 0vartype var_attribute 0vartype dimension____ 8versionMINC Version 1.0 P comments&X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments.Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments-Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 8varidMINC standard variable 8varidMINC standard variableHh 0 minc_version2.3.00 historyFri Oct 31 10:26:18 2014>>> minccalc -expression A[0]-1 test.mnc n1.mnc Fri Oct 31 11:55:11 2014>>> mincreshape -float n1.mnc f1.mnc TREE 8x^c`h0h17 nminc-tools-2.3.00+dfsg/Testing/run_test_progs.sh0000755000175000000620000000037412574624760020657 0ustar stevestaff#! /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 minc-tools-2.3.00+dfsg/Testing/Makefile.am0000644000175000000620000000350412574624760017275 0ustar stevestaffINCLUDES = -I$(top_srcdir)/libsrc \ -I$(top_srcdir)/libsrc2 \ -I$(top_srcdir)/volume_io/Include \ -I$(top_builddir)/volume_io/Include AM_CFLAGS = -DAPPARENTORDER script_tests = \ run_test_arg_parse.sh \ run_tests.sh \ run_test2.sh \ xfmconcat_01.sh \ xfmconcat_02.sh \ run_test_progs.sh # minc2-testminctools.sh all-local: echo "SRCDIR: $(srcdir)" cd $(srcdir) && chmod +x $(script_tests) LDADD = ../libvolume_io2.la ../libminc2.la TESTS = run_test_arg_parse.sh \ run_tests.sh \ run_test2.sh \ xfmconcat_01.sh \ xfmconcat_02.sh \ mincapi \ run_test_progs.sh # minc2-testminctools.sh check_PROGRAMS = minc test_mconv minc_types icv icv_range \ icv_dim test_speed icv_dim1 icv_fillvalue \ test_xfm create_grid_xfm mincapi test_speed test_arg_parse \ minc2-create-test-images \ minc2-create-test-images-2 \ minc2-datatype-test \ minc2-dimension-test \ minc2-full-test \ minc2-grpattr-test \ minc2-hyper-test \ minc2-hyper-test-2 \ minc2-label-test \ minc2-record-test \ minc2-slice-test \ minc2-valid-test \ minc2-volprops-test \ minc2-multires-test \ minc2-convert-test \ minc2-vector_dimension-test # The test script "run_tests.sh" uses a series of "expected output" # files, comparing each with the output generated by the file under # test. We need to distribute these files (so they are in # EXTRA_DIST). # expect_files = icv.out icv_dim.out icv_dim1.out icv_fillvalue.out \ icv_range.out minc_types.out EXTRA_DIST = $(script_tests) $(expect_files) t1.xfm icv.mnc # CLEANFILES = test.mnc _* *.mnc # Unfortunately, GNU make has implicit rules for files with the suffix # ".out". The following lines disable implicit rules. In future, we # should probably use a different naming scheme for these files ... # icv.out: ; icv_dim.out: ; icv_dim1.out: ; icv_fillvalue.out: ; icv_range.out: ; minc_types.out: ; minc-tools-2.3.00+dfsg/Testing/xfmconcat_01.sh0000755000175000000620000000066712574624760020071 0ustar stevestaff#! /bin/sh # # Test concatenation of grid transforms. set -e # Create volume to use as displacement grid. # dd if=/dev/zero | ../rawtominc -vector 3 -byte -clobber _grid.mnc 8 8 8 ./create_grid_xfm _grid.mnc _t1.xfm ./create_grid_xfm _grid.mnc _t2.xfm ../xfmconcat -clobber _t1.xfm _t2.xfm _t3.xfm # Make sure the displacement volumes have unique filenames. # test -n "`grep Displacement_Volume _t3.xfm |uniq -u`" || exit 1 exit 0 minc-tools-2.3.00+dfsg/Testing/test-zero.mnc0000644000175000000620000003404612574624760017701 0ustar stevestaffHDF  &8`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX xspaceyspacezspace8SNOD(Pp 80 X x X x TREE HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE8HEAPX0imageimage-minimage-max(SNOD + Pident.rvincent:ace-ws-21:2014.10.31.11.55.05:28834:18   deflate +%2nTH#HSNOD(  @ dimorderzspace,yspace,xspace  ?@4 4%2nT 8varidMINC standard variable 8versionMINC Version 1.0`$8  ?@4 4%2nT 8varidMINC standard variable 8versionMINC Version 1.0$8 ?@4 4%2nT 0 length  8varidMINC standard variable$SNODXp ?@4 4%2nT 0 length  8varidMINC standard variable& ?@4 4%2nT 0 length  0 dimorderzspaceX(  %2nT 8versionMINC Version 1.0 0vartype group________`*@SNOD "  %2nT 8versionMINC Version 1.0 0vartype group________*@  %2nT 8versionMINC Version 1.0 0vartype group________*@ 8varidMINC standard variable 0vartype group________ 8versionMINC Version 1.0 P valid_range  zzD 0 completetrue_ 0vartype var_attribute 0vartype var_attribute 0vartype dimension____ 8versionMINC Version 1.0 P comments&X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments.Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 0vartype dimension____ 8versionMINC Version 1.0 X comments-Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 8varidMINC standard variable 8varidMINC standard variableHh 0 minc_version2.3.00 `history>Fri Oct 31 11:55:05 2014>>> mincreshape -float n0.mnc f0.mnc TREE8x^c`# minc-tools-2.3.00+dfsg/Testing/minccalc-test.sh0000755000175000000620000001114312574624760020324 0ustar stevestaff#! /bin/bash let errors=0; if [[ ! -x $MINCCALC_BIN ]]; then MINCCALC_BIN=`which minccalc`; fi if [[ ! -x $MINCSTATS_BIN ]]; then MINCSTATS_BIN=`which mincstats`; fi echo -n Case 1... # Test a simple addition case. $MINCCALC_BIN -clobber -quiet -expression "A[0]+A[1];" test-one.mnc test-two.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '375' ]]; then echo "Problem with basic per-voxel addition:" $r1 let errors+=1; else echo OK fi echo -n Case 2... # Test another basic multiplication case. $MINCCALC_BIN -clobber -quiet -expression "A[0]*A[1]" test-one.mnc test-two.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '250' ]]; then echo "Problem with basic per-voxel multiplication:" $r1 let errors+=1; else echo OK fi echo -n Case 3... # Test a more complex example including a function. The result here should # be equal to exp(-0.5)*125. # $MINCCALC_BIN -clobber -quiet -expression "ratio = A[0]/A[1]; exp(-ratio)" test-one.mnc test-two.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '75.81633329' ]]; then echo "Problem with first function test:" $r1 let errors+=1; else echo OK fi echo -n Case 4... # Test another example including a function. The result here should be # equal to sqrt(3)*125 # $MINCCALC_BIN -clobber -quiet -expression "x = cos(A[0])+A[1]; sqrt(x)" test-zero.mnc test-two.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '216.5063471' ]]; then echo "Problem with second function test:" $r1 let errors+=1; else echo OK fi echo -n Case 5... # Test a more complex script which sends its results to two different # output files. This is straight out of the man page. # $MINCCALC_BIN -clobber -quiet -expression "s0=s1=s2=0; for {i in [0:len(A))} { v=A[i]; s0 = s0 + 1; s1 = s1 + v; s2 = s2 + v*v; }; stdev = (s0>1) ? sqrt((s2 - s1*s1/s0) / (s0-1)) : (s0 > 0) ? 0 : NaN ; mean = (s0 > 0) ? s1 / s0 : NaN;" test-one.mnc test-one.mnc test-two.mnc -outfile mean minccalc-out.mnc -outfile stdev minccalc-out2.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '166.6666716' ]]; then echo "Problem with elaborate script mean:" $r1 let errors+=1; else echo -n OK... fi r1=`$MINCSTATS_BIN -quiet -sum minccalc-out2.mnc` if [[ $r1 != '72.16878235' ]]; then echo "Problem with elaborate script stddev:" $r1 let errors+=1; else echo OK fi echo -n Case 6... # Test clamp with the pseudorandom input file... $MINCCALC_BIN -clobber -quiet -expression "clamp(A[0]+A[1], 1, 4)" test-one.mnc test-rnd.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -min minccalc-out.mnc` if [[ $r1 != '1' ]]; then echo "Problem with clamped min:" $r1 let errors+=1; else echo -n OK... fi r1=`$MINCSTATS_BIN -quiet -max minccalc-out.mnc` if [[ $r1 != '4' ]]; then echo "Problem with clamped max:" $r1 let errors+=1; else echo -n OK... fi # Since the "random" file contains exactly 25 voxels of value 0,1,2,3, or 4, # the result here should contain 25 each of 1, 2, and 3 and 50 of 4, or # (6*25+4*50)=350. # r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '350' ]]; then echo "Problem with clamped sum:" $r1 let errors+=1; else echo OK fi echo -n Case 7... # Test segment with the pseudorandom input file... $MINCCALC_BIN -clobber -quiet -expression "segment(A[0]*A[1], 1.5, 4.5)" test-two.mnc test-rnd.mnc minccalc-out.mnc # Since the "random" file contains exactly 25 voxels of value 0,1,2,3, or 4, # the output should contain exactly 25 voxels with value of either 2 or 4. # r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '50' ]]; then echo "Problem with segmented image:" $r1 let errors+=1; else echo OK fi echo -n Case 8... # Test the currently failing case - modification of lower range limit. $MINCCALC_BIN -clobber -quiet -expression 'm=0;n=len(A); for {i in (m:n)} { 1; }; m;' test-zero.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '0' ]]; then echo "Problem with alteration of lower exclusive range limits:" $r1 let errors+=1; else echo OK fi echo -n Case 9... # Test for modification of upper range limit. $MINCCALC_BIN -clobber -quiet -expression 'm=0;n=len(A); for {i in (m:n)} { 1; }; n;' test-zero.mnc minccalc-out.mnc r1=`$MINCSTATS_BIN -quiet -sum minccalc-out.mnc` if [[ $r1 != '125' ]]; then echo "Problem with alteration of upper exclusive range limits:" $r1 let errors+=1; else echo OK fi if [[ $errors = "0" ]]; then echo "No errors detected." else echo $errors errors detected. fi exit $errors minc-tools-2.3.00+dfsg/Testing/mincaverage-in0.mnc0000644000175000000620000003405712574624760020714 0ustar stevestaffHDF  /8`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX xspaceyspacezspace8SNOD(Pp 80 X x X x TREE HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE8HEAPX0imageimage-minimage-max(SNOD + Pident.rvincent:ace-ws-21:2014.10.31.11.55.05:28834:18   deflate +[WTH#HSNOD(  @ dimorderzspace,yspace,xspace  ?@4 4[WT 8varidMINC standard variable 8versionMINC Version 1.0`$8  ?@4 4?[WT 8varidMINC standard variable 8versionMINC Version 1.0$8 ?@4 4[WT 0 length  8varidMINC standard variable$SNODXp ?@4 4[WT 0 length  8varidMINC standard variable& ?@4 4[WT 0 length  0 dimorderzspaceX(  [WT 8versionMINC Version 1.0 0vartype group________`*@SNOD "  [WT 8versionMINC Version 1.0 0vartype group________*@  [WT 8versionMINC Version 1.0 0vartype group________*@ 8varidMINC standard variable 0vartype group________ 8versionMINC Version 1.0 P valid_range  ? 0 completetrue_ 0vartype var_attribute 0vartype var_attribute 0vartype dimension____ 8versionMINC Version 1.0 P comments&X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments.Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 0vartype dimension____ 8versionMINC Version 1.0 X comments-Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 8varidMINC standard variable 8varidMINC standard variableHh 0 minc_version2.3.00 `history>Fri Oct 31 11:55:05 2014>>> mincreshape -float n0.mnc f0.mnc TREE8x^c`h0h17o'minc-tools-2.3.00+dfsg/Testing/mincstats-test.pl0000755000175000000620000001075512574624760020571 0ustar stevestaff#! /usr/bin/env perl use strict; my $errors=0; my $mincstats_bin = `which mincstats`; chomp($mincstats_bin); if ($ENV{'MINCSTATS_BIN'}) { $mincstats_bin = $ENV{'MINCSTATS_BIN'}; } print "Case 1 - Basic output.\n"; my $r1 = `$mincstats_bin test-zero.mnc`; if ($? != 0) { print "Default operation returned something other than zero.\n"; $errors++; } my $c1result = <) { # Skip comment and blank lines. if (/^#/ || /^$/) { next; } my @fields = split; my $centre = $fields[0]; my $count = $fields[1]; if ($centre == 0.001 || $centre == 1.001 || $centre == 2.001 || $centre == 3.001 || $centre == 3.999) { if ($count != 25) { $errors++; print "Histogram is incorrect: $n $centre $count.\n"; } } else { if ($count != 0) { print "Histogram is incorrect: $n $centre $count.\n"; $errors++; } } $n++; } close FH; if ($n != 2000) { print "Histogram has the wrong number of bins: $n.\n"; $errors++; } print "Case 9 - Test the integer histogram option.\n"; my $r1 = `$mincstats_bin -clobber -integer_hist -histogram test-ihist.txt test-rnd.mnc`; open(FH, "< test-ihist.txt") or die "Cannot open histogram for reading: $!"; my $n = 0; while () { # Skip comment and blank lines. if (/^#/ || /^$/) { next; } my @fields = split; my $centre = $fields[0]; my $count = $fields[1]; if ($count != 25) { $errors++; print "Histogram is incorrect: $n $centre $count.\n"; } $n++; } close FH; if ($n != 5) { print "Histogram has the wrong number of bins: $n.\n"; $errors++; } print "OK.\n" if $errors == 0; print exit $errors > 0; minc-tools-2.3.00+dfsg/Testing/test-one.mnc0000644000175000000620000004474312574624760017510 0ustar stevestaffHDF  I`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX xspaceyspacezspace8SNOD(Pp 80 X x X x TREE HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE8HEAPX0imageimage-minimage-max(SNOD, Pident.rvincent:ace-ws-21:2014.11.20.11.03.02:16776:18   deflate =2nTH#HSNOD(  @ dimorderzspace,yspace,xspace  ?@4 4 deflate-2nT 0 dimorderzspaceP$  ?@4 4 deflate@52nT 0 dimorderzspace% ?@4 42nT 0 length  8varidMINC standard variable%SNODH` ?@4 42nT 0 length  8varidMINC standard variable' ?@4 42nT 0 length  0 dimorderzspaceH)  2nT 8versionMINC Version 1.0 0vartype group________P+@SNODx !  2nT 8versionMINC Version 1.0 0vartype group________+@  2nT 8versionMINC Version 1.0 0vartype group________+@ 8varidMINC standard variable 0vartype group________ 8versionMINC Version 1.0 0 completetrue_ P valid_range  zzD 8varidMINC standard variable 8versionMINC Version 1.0 0vartype var_attribute 8varidMINC standard variable 8versionMINC Version 1.0 0vartype var_attribute 0vartype dimension____ 8versionMINC Version 1.0 P comments&X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments.Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 0vartype dimension____ 8versionMINC Version 1.0 X comments-Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 8varidMINC standard variable 8varidMINC standard variableHh 0 minc_version2.3.00 historyFri Oct 31 11:55:05 2014>>> mincreshape -float n0.mnc f0.mnc Thu Nov 20 11:03:02 2014>>> minccalc -expression 1; zero.mnc one.mnc TREEp=TREE=x^c``  isx^c``  isTREEIx^c`hg# ]Dminc-tools-2.3.00+dfsg/Testing/test-two.mnc0000644000175000000620000004474112574624760017536 0ustar stevestaffHDF  I`TREEHEAPXminc-2.0@pTREE HEAPX(dimensionsinfoimage0SNOD HhPp TREEHEAPX xspaceyspacezspace8SNOD(Pp 80 X x X x TREE HEAPX( acquisitionpatientstudy08TREEpHEAPXX0HTREE8HEAPX0imageimage-minimage-max(SNOD, Pident.rvincent:ace-ws-21:2014.11.20.11.03.43:16783:18   deflate =32nTH#HSNOD(  @ dimorderzspace,yspace,xspace  ?@4 4 deflate-32nT 0 dimorderzspaceP$  ?@4 4 deflate@532nT 0 dimorderzspace% ?@4 432nT 0 length  8varidMINC standard variable%SNODH` ?@4 432nT 0 length  8varidMINC standard variable' ?@4 432nT 0 length  0 dimorderzspaceH)  32nT 8versionMINC Version 1.0 0vartype group________P+@SNODx !  32nT 8versionMINC Version 1.0 0vartype group________+@  32nT 8versionMINC Version 1.0 0vartype group________+@ 8varidMINC standard variable 0vartype group________ 8versionMINC Version 1.0 0 completetrue_ P valid_range  zzD 8varidMINC standard variable 8versionMINC Version 1.0 0vartype var_attribute 8varidMINC standard variable 8versionMINC Version 1.0 0vartype var_attribute 0vartype dimension____ 8versionMINC Version 1.0 P comments&X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 0vartype dimension____ 8versionMINC Version 1.0 X comments.Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 0vartype dimension____ 8versionMINC Version 1.0 X comments-Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4? 8start ?@4 4 8varidMINC standard variable 8varidMINC standard variable 8varidMINC standard variableHh 0 minc_version2.3.00 historyFri Oct 31 11:55:05 2014>>> mincreshape -float n0.mnc f0.mnc Thu Nov 20 11:03:43 2014>>> minccalc -expression 2; zero.mnc two.mnc TREEp=TREE=x^c`hAx^c`hATREEIx^c``p`# hAminc-tools-2.3.00+dfsg/UseMINC2.cmake.in0000644000175000000620000000064712574624760016525 0ustar stevestaffINCLUDE_DIRECTORIES(${MINC2_INCLUDE_DIRS}) LINK_DIRECTORIES(${MINC2_LIBRARY_DIRS}) INCLUDE_DIRECTORIES(${NETCDF_INCLUDE_DIR} ) IF(HAVE_MINC2) SET(MINC2 "1") ADD_DEFINITIONS( -DMINC2=1 ) INCLUDE_DIRECTORIES(${MINC2_INCLUDE_DIRS}) LINK_DIRECTORIES(${MINC2_LIBRARY_DIRS}) INCLUDE_DIRECTORIES( ${HDF5_INCLUDE_DIR} ) ENDIF(HAVE_MINC2) IF(USE_ITK_HDF5) LINK_DIRECTORIES(${ITK_LIBRARY_DIRS}) ENDIF(USE_ITK_HDF5) minc-tools-2.3.00+dfsg/README0000644000175000000620000000317312574624760014506 0ustar stevestaffMINC Tools This package is a collection of tools that work on MINC format images. * rawtominc, minctoraw, mincextract - format conversion * mincheader, mincdiff, mincinfo, minchistory - file information * mincedit, minc_modify_header - header manipulation * mincresample - arbitrary volume resampling * mincreshape - extraction of volume sub-cubes, image flipping, dimension re-ordering, type conversion * mincconcat - concatenating or interleaving images from multiple files * mincmath - perform simple math on files * minccalc - perform more complicated math on files through an expression * mincaverage - average minc files * mincstats - calculate statistics across voxels of a file * minclookup - lookup table operations for arbitrary re-mappings of intensities * worldtovoxel, voxeltoworld - coordinate conversion * xfmconcat, xfminvert - generalized transformation utilities * mincview - simple image display using xv or ImageMagick * mincpik - generate picture from slice through volume INSTALLATION Compilation and installation is now done with CMake. Use these steps: 1. Compile $ cmake CMakeLists.txt $ make Note that if you have installed libminc in a non-standard location (in this case /opt/local/minc) you will have to tell CMake where to find it as such: $ export LIBMINC_DIR=/opt/local/minc $ cmake CMakeLists.txt $ make Similarly if you'd like to install minc-tools in this same location you will need to do this: $ export LIBMINC_DIR=/opt/local/minc $ cmake -DCMAKE_INSTALL_PREFIX:PATH=/opt/local/minc CMakeLists.txt $ make 2. Install $ sudo make install minc-tools-2.3.00+dfsg/.gitmodules0000644000175000000620000000000012574624760015765 0ustar stevestaff